Delbin - Descriptive Language for Binary Objects
A Domain-Specific Language (DSL) and its supporting library for describing and generating binary data structures, primarily designed for embedded firmware header generation.
Features
Delbin enables firmware engineers to:
- ✅ Define binary data structures using human-readable syntax
- ✅ Automatically calculate sizes, offsets, and checksums
- ✅ Support environment variable substitution
- ✅ Generate binary data from DSL definitions
- ✅ CRC32, CRC16-MODBUS, and SHA256 checksums — unified
@crc("algo", ...)API - ✅ Handle self-referencing fields (e.g., header CRC)
- ✅ Full range expressions:
@self,@self[..field],@self[field..],@self[field_a..field_b] - ✅ Support both little-endian and big-endian byte orders
- ✅ Flexible array initialization with multiple syntax forms
- ✅ Struct alignment padding via
@align(n) - ✅ Type safety: hard errors for type mismatches, warnings for value truncation
- ✅
validate()API — check DSL without generating bytes - ✅
parse()API — reverse-read binary data according to DSL schema - ✅ Command-line tool with
--env,--section,--format,--output,--verbose
Quick Start
Installation
Add this to your Cargo.toml:
[]
= "0.1"
Basic Usage
use ;
use HashMap;
let dsl = r#"
@endian = little;
struct header @packed {
magic: [u8; 4] = @bytes("FPK\0");
version: u32 = ${VERSION};
img_size: u32 = @sizeof(image);
img_crc: u32 = @crc32(image);
}
"#;
// Set environment variables
let mut env = new;
env.insert;
// Set sections (external binary data)
let mut sections = new;
sections.insert;
// Generate binary data
let result = generate?;
println!;
DSL Syntax Overview
Global Directives
@endian = little; // or big
Struct Definition
Struct attributes:
| Attribute | Description |
|---|---|
@packed |
No alignment padding between fields |
@align(n) |
Pad struct output to next n-byte boundary |
Types
- Scalar types:
u8,u16,u32,u64,i8,i16,i32,i64 - Array types:
[u8; 4],[u32; N]
Array Initialization
Arrays support flexible initialization syntax:
data: ; // Default: all zeros
pattern: = ; // Repeat value (explicit count)
fill: = ; // Repeat value (inferred count)
values: = ; // Element list (full)
partial: = ; // Element list (partial, pad with 0)
magic: = @bytes; // Function call
// Environment variables in arrays
data: = ; // Repeat with env var
mixed: = ; // Element list with env var
Expressions
- Literals:
0x1234,0b1010,42,"string" - Environment variables:
${VAR_NAME} - Operators:
|,&,<<,>>,+,-,~
Built-in Functions
| Function | Description | Example |
|---|---|---|
@bytes(str) |
Convert string to byte array | @bytes("FPK\0") |
@sizeof(section) |
Get size of section or struct | @sizeof(image) |
@offsetof(field) |
Get field byte offset | @offsetof(crc) |
@crc32(range) |
CRC32-ISO-HDLC (alias for @crc("crc32", ...)) |
@crc32(image) |
@crc("algo", range) |
CRC with named algorithm | @crc("crc16-modbus", image) |
@sha256(range) |
SHA256 hash (returns [u8; 32]) |
@sha256(image) |
Supported CRC algorithms for @crc():
| Name | Width | Description |
|---|---|---|
"crc32" / "crc32-iso-hdlc" |
32-bit | Same as @crc32() |
"crc16-modbus" |
16-bit | CRC16-MODBUS |
Range Expressions
@self // Entire current struct
@self // From start to before field
@self // From field to end of struct
@self // From field_a to before field_b
@self // From byte offset 0x10 to before field
section_name // Entire external section (e.g. image)
Example: Firmware Header
@endian = little;
struct header @packed {
// Magic number
magic: [u8; 4] = @bytes("fpk\0");
// Version (packed: major.minor.patch)
fw_version: u32 = (${VERSION_MAJOR} << 24) |
(${VERSION_MINOR} << 16) |
${VERSION_PATCH};
// Sizes
header_size: u32 = @sizeof(@self);
img_size: u32 = @sizeof(image);
// Timestamp
timestamp: u32 = ${UNIX_STAMP};
// Checksums
img_crc32: u32 = @crc32(image);
img_sha256: [u8; 32] = @sha256(image);
// Self-referencing: CRC of body from magic to body_crc (partial range)
body_crc: u32 = @crc("crc16-modbus", @self[magic..body_crc]);
// Self-referencing header CRC
header_crc32: u32 = @crc32(@self[..header_crc32]);
// Padding to 256 bytes
_padding: [u8; 256 - @offsetof(_padding)];
}
Implementation Status
✅ Implemented Features
- DSL parser with Pest grammar
- AST generation
- Binary data generation
- Environment variable substitution
- Built-in functions:
@bytes,@sizeof,@offsetof,@crc32,@sha256 -
@crc("algorithm", range)unified CRC withcrc32andcrc16-modbus - Self-referencing fields with two-phase evaluation
- Full range expressions:
@self,@self[..field],@self[field..],@self[field_a..field_b] - Little-endian and big-endian support
- Struct attributes:
@packed,@align(n) - Array literal initialization with five syntax forms
- Environment variables in array elements
- Type checking: hard error for string→array without
@bytes, for@byteson non-u8arrays - Value truncation warning (W03002) when value overflows target field width
- Shift overflow warning (W04001) for shift amount ≥ 64
- Structured error and warning codes (E01xxx–E05xxx, W03xxx–W04xxx)
-
validate()API — parse + semantic check without generating bytes -
parse()API — reverse-read binary into named fields -
merge()API — generate header and prepend to image in one call - CLI tool (
delbin) with--env,--section,--format,--output,--verbose
🚧 Planned Features
- Multiple structs per DSL file
- Additional CRC algorithms (currently:
crc32,crc16-modbus) - Additional hash algorithms (
@hash()with algorithm parameter) - TOML configuration file support for CLI
❌ Not Planned
- Firmware encryption/decryption
- Digital signature generation/verification
- Firmware transmission protocols
- Dynamic TLV structure parsing
API Reference
Core Functions
/// Generate binary output from DSL
;
/// Generate and return as uppercase hex string
;
/// Generate header and prepend to image_data
;
/// Validate DSL syntax and semantics without generating output.
/// Returns any warnings on success.
;
/// Parse raw bytes back into named field values according to the DSL layout.
;
Types
CLI
delbin [OPTIONS] <INPUT>
Arguments:
<INPUT> DSL file path; use '-' to read from stdin
Options:
-o, --output <FILE> Write to file instead of stdout
--format <hex|bin> Output format: 'hex' (default) or 'bin' (raw bytes)
--env <KEY=VALUE> Set environment variable (repeatable)
--section <NAME=FILE> Load section data from file (repeatable)
--verbose Print warnings to stderr
-h, --help
-V, --version
CLI examples:
# Print hex to stdout
# Write binary file
# Inject environment variables
# Pass external section (for @sizeof / @crc32)
# Read DSL from stdin (useful in CI pipelines)
|
# Print truncation / overflow warnings
Error Handling
Delbin uses structured error and warning codes:
| Category | Code Range | Description |
|---|---|---|
| Parse errors | E01xxx | DSL syntax errors |
| Semantic errors | E02xxx | Undefined variables/fields/sections |
| Type errors | E03xxx | Type mismatches, size mismatches |
| Evaluation errors | E04xxx | Expression evaluation failures |
| IO errors | E05xxx | File operation errors |
| String warnings | W03001 | String truncated to fit array |
| Truncation warnings | W03002 | Integer value truncated to fit field width |
| Shift warnings | W04001 | Shift amount ≥ 64 bits (result is 0) |
Example:
match generate
Examples
# Firmware header with CRC and SHA256
# All array initialization syntax forms
# Unified @crc() and CRC16-MODBUS
# validate() and parse() APIs
Testing
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Documentation
References
- Pest Parser - PEG parser used for DSL parsing
- MCUboot - Inspiration for firmware header formats
- CRC RevEng Catalogue - CRC algorithm reference