Crunch
Crunch is a C++ message definition and serialization framework for mission-critical, resource-constrained systems where message semantics matter as much as structure.
See the Doxygen for API reference and detailed documentation.
Key Features
- Opt-out validation: Semantic field and cross-field validation are first-class, built-in, and happen by default.
- Static memory allocation: For use in resource-constrained systems.
- Flexible serialization: Swap serialization formats (e.g., TLV, static layout) without changing message definitions.
- Built-in integrity checks: Support for CRC16 or parity is built-in.
- Zero exceptions: Uses
std::expectedandstd::optionalfor error handling to be compati…
Crunch
Crunch is a C++ message definition and serialization framework for mission-critical, resource-constrained systems where message semantics matter as much as structure.
See the Doxygen for API reference and detailed documentation.
Key Features
- Opt-out validation: Semantic field and cross-field validation are first-class, built-in, and happen by default.
- Static memory allocation: For use in resource-constrained systems.
- Flexible serialization: Swap serialization formats (e.g., TLV, static layout) without changing message definitions.
- Built-in integrity checks: Support for CRC16 or parity is built-in.
- Zero exceptions: Uses
std::expectedandstd::optionalfor error handling to be compatible with real-time requirements.
Dependencies
Crunch requires C++23 and uses some STL libraries.
None of the STL libraries used by Crunch perform dynamic memory allocation.
Installation
Crunch is a header-only library. You can vendor it directly or use a build system.
Vendoring
Copy the include/ directory to your project:
cp -r crunch/include/ your_project/vendor/
Then add the include path to your compiler:
g++ -I vendor -std=c++23 your_code.cpp
CMake
Add Crunch as a subdirectory:
add_subdirectory(vendor/crunch)
target_link_libraries(your_target PRIVATE Crunch::crunch)
Bazel
Add the dependency to your BUILD file:
cc_binary(
name = "your_target",
srcs = ["main.cpp"],
deps = ["//include:crunch"], # Or @crunch//include:crunch if external
)
Quick Start
#include <crunch/crunch.hpp>
using namespace Crunch;
using namespace Crunch::fields;
using namespace Crunch::messages;
struct SensorReading {
CRUNCH_MESSAGE_FIELDS(temperature, humidity);
static constexpr MessageId message_id = 1;
// Temperature in Celsius, must be between -40 and 85
Field<1, Required, Float32<InRange<-40.0, 85.0>>> temperature;
// Humidity percentage, must be between 0 and 100
Field<2, Required, Float32<InRange<0.0, 100.0>>> humidity;
// Cross-field validation: high humidity requires moderate temperature
constexpr auto Validate() const -> std::optional<Error> {
if (*humidity.get() > 90.0f && *temperature.get() > 50.0f) {
return Error::validation(2, "humidity > 90% invalid above 50C");
}
return std::nullopt;
}
bool operator==(const SensorReading&) const = default;
};
int main() {
SensorReading msg;
msg.temperature.set(23.5f);
msg.humidity.set(65.0f);
// Get a correctly-sized buffer for this message type
auto buffer = GetBuffer<SensorReading, integrity::CRC16, serdes::PackedLayout>();
// Serialize (validates fields AND cross-field logic, calculates and appends integrity check, then writes)
if (auto err = Serialize(buffer, msg); err) {
return 1;
}
// Deserialize into new object. Checks integrity and validates fields AND cross-field logic.
SensorReading decoded;
if (auto err = Deserialize(buffer, decoded); err) {
return 1;
}
assert(msg == decoded); // Round-trip successful
}
Message Definition
Messages must define:
- A unique
MessageId - A
CRUNCH_MESSAGE_FIELDSmacro listing all fields - A
Validatemethod returningstd::optional<Error> - An
operator==for equality comparison
Supported Field Types
| Category | Types |
|---|---|
| Integers | Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64 |
| Floats | Float32, Float64 |
| Other | Bool, String, Enum |
| Aggregate | Submessage, Array, Map |
See Field Types for detailed examples of each type.
Validation Flow
When serializing or deserializing, validation occurs in order:
- Field Presence: Checks that all
Requiredfields are set. - Field Values: Checks that set fields satisfy their validators.
- Message Logic: Executes the user-defined
Validate()method.
Validation can be bypassed using SerializeWithoutValidation.
Serialization
Crunch supports pluggable serialization:
serdes::StaticLayout<Alignment>- Deterministic, fixed-size binary formatserdes::TlvLayout- Tag-Length-Value format for flexibility
See Serialization Formats for wire format details.
Roadmap
Done:
- Basic message definitions, field/message validation
- Scalar fields, enums, strings, submessages, arrays, maps
- Static layout and TLV serialization
- CRC16 and parity integrity checking
- Unit testing, documentation, CI/CD
Upcoming:
- Test coverage reporting
- QEMU-based cross-platform testing
- Fuzz testing
- Performance benchmarks
- C, Rust, Python bindings
Follow along at volatileint.dev for roadmap updates!