Skip to main content

Crate wire_repr

Crate wire_repr 

Source
Expand description

§Zero-cost byte-backed representations

wire-repr generates safe borrowed views, constrained mutable views, and atomic caller-buffer builders for binary layouts. Generated code works directly with byte slices: it does not cast bytes to Rust structs, allocate, perform I/O, or depend on alignment and ABI layout.

The crate is no_std, has no default features, and keeps domain policy in the consumer. Layouts establish byte ownership, bounds, framing, and encoding mechanics; callers remain responsible for magic values, reserved bits, checksums, and cross-field semantics.

§Quick start

use wire_repr::wire_repr;

wire_repr! {
    pub layout Header {
        field kind: U8;
        field length: BeU16;
        field flags: U8 {
            projections {
                bit enabled: 0;
                bits mode: 1..=3;
            }
        }
    }
}

let input = [7, 0x01, 0x00, 0b0000_1011, 0xff];
let (view, suffix) = HeaderView::parse_prefix(&input).unwrap();

assert_eq!(view.as_bytes(), &input[..4]);
assert_eq!(suffix, &[0xff]);
assert_eq!(view.kind(), 7);
assert_eq!(view.length(), 256);
assert!(view.enabled());
assert_eq!(view.mode(), 5);

let mut output = [0u8; 5];
let (mut view, suffix) = HeaderBuilder::new()
    .kind(7)
    .length(256)
    .flags(0b0000_1011)
    .build_into(&mut output)
    .unwrap();

view.set_kind(8).unwrap();
assert_eq!(view.as_bytes(), &[8, 0x01, 0x00, 0b0000_1011]);
assert_eq!(suffix, &[0]);

§Parsing and represented bytes

Every generated immutable view provides two explicit entry points:

  • parse_prefix parses one representation and returns the disjoint suffix;
  • parse_exact requires the complete input to be exactly one representation.

as_bytes returns only the bytes owned by the representation. Accepted bytes are borrowed and preserved verbatim, including opaque gaps and legal noncanonical encodings accepted by a custom PrefixCodec. Parsing performs structural validation; it does not reconstruct bytes from decoded values or apply consumer policy.

Fixed layouts expose View::WIDTH. Dynamic sequential layouts discover their represented extent while validating prefix fields, byte ranges, padding, and alignment.

§Generated API

For layout Packet, wire_repr! generates a family centered on the declaration:

Generated itemPurpose
PacketView<'wire>Immutable borrowed representation and field getters
PacketViewMut<'wire>Constrained mutable representation and eligible setters
PacketBuilder<'value>Fluent caller-buffer construction
PacketErrorStructural parse errors
PacketMutationErrorFixed-field planning and setter errors
PacketWriteErrorMissing inputs, planning, extent, and capacity errors

A field getter has the declared field name. Prefix fields also expose <field>_raw() so callers can inspect the exact validated raw wire bytes (the original wire representation). Bit projections produce named bool or unsigned scalar getters without becoming independent byte owners.

Mutable views provide parse_prefix_mut, parse_exact_mut, as_view, and into_view. Setters are generated only for same-width fields whose mutation cannot invalidate dynamic framing. Mutable views deliberately do not expose unrestricted access to their backing slice.

Builders use new, one fluent method per caller-supplied field, and build_into. They preflight all plans, extents, conversions, arithmetic, and capacity before writing, so an error leaves the complete destination unchanged. Relative range sources derive payload lengths; absolute sources derive physical exclusive payload ends, including prior fixed and prefix widths, padding, alignment, and ranges. Derived sources have no builder input or setter; shared sources use identical algebra and values. buf_end has no source. Success returns the bounded mutable view and its disjoint mutable suffix.

Generated error variants retain field or physical-position context. Their exact surface is visible on each generated layout because it depends on the declared codecs and layout shape.

§Layout kinds

Sequential layouts normally infer physical placement from declaration order:

wire_repr! {
    pub layout Frame {
        field payload_length: BeU16;
        field payload: bytes(current_pos..current_pos + payload_length);
        field checksum: BeU32;
    }
}

Fields, padding, and alignment may instead all use explicit one-based position values when physical order must differ from declaration and API order. Mixing implicit and explicit sequential placement is rejected.

Absolute layouts use mandatory zero-based offsets and may contain represented gaps:

wire_repr! {
    pub absolute layout Header {
        field magic: bytes(4) { offset: 0; }
        field version: BeU16 { offset: 8; }
    }
}

Absolute layouts are fixed-width. They do not infer offsets and do not support prefix fields, byte ranges, padding, or alignment.

See wire_repr! for the declaration grammar and generated-name reference.

§Field codecs and framing

Built-in fixed codecs cover unsigned 8/16/24/32/64/128-bit integers and signed 8/16/32/64/128-bit integers in the applicable byte orders. bytes(N) exposes an opaque borrowed span through Bytes.

Custom fixed-width fields use codec(path) and implement FixedCodec. Variable-width prefix fields use prefix(path) and implement PrefixCodec. Both contracts separate fallible planning from infallible emission through EncodePlan. This separation is what lets generated setters and builders preserve whole-destination atomicity.

Sequential byte ranges borrow opaque spans in three forms:

  • bytes(current_pos..current_pos + source) uses source as a relative length;
  • bytes(current_pos..source) uses source as an exclusive absolute endpoint from representation byte zero;
  • bytes(current_pos..buf_end) consumes the supplied view-buffer tail.

The first two require a physically preceding built-in fixed integer or semantic mapping over one. Geometry uses the raw physical integer and checked usize conversion; prefix, custom/direct, declared scalar, nominal, and byte-range sources are unsupported. bytes(0) is invalid, although dynamic ranges may be empty. An absolute range does not itself end parse_prefix: the suffix follows the complete represented layout, including later physical fields. buf_end has no source, is physically terminal, and therefore produces an empty suffix. Variable-width-source framing such as ULEB128 section lengths remains consumer-owned.

The codec module documents the implementor laws and extension boundary.

§Total semantic mappings

An eligible built-in fixed integer or bytes(N) field may add as TypePath, for example field kind: BeU16 as crate::Kind; or field address: bytes(4) as crate::Address;. The physical codec still owns byte decoding, encoding, range errors, and atomicity; the mapped type is the nominal consumer-facing API.

Mapped fields generate field() for the semantic value and field_raw() for the raw codec value. Eligible mutable fields also generate semantic and _raw setters, and builders generate both fluent forms for the same input slot: the last call wins. A byte-range source exposes both getters but has neither setter nor builder input because the builder derives its raw value from the byte range.

Mappings are deliberately total: getters use Semantic: From<Raw> and setters/builders use Raw: From<Semantic>. Raw types are exact codec types, including u32 for U24 and [u8; N] for bytes(N). There is no fallible semantic conversion layer. Consequently a semantic U24 value can still produce an out-of-range raw u32, which the physical codec rejects without changing the destination. Mapped byte values are owned arrays or wrappers; unmapped bytes(N) continues to return borrowed Bytes (&[u8]).

Only built-in fixed integers and bytes(N) are eligible. Declared scalar Name: Codec; instead declares a reusable codec-owning nominal wrapper; it is not an as Type mapping, and mappings do not apply to declared scalar codecs, custom/direct codecs, prefix fields, or byte ranges. A mapping compiles to direct From calls around the existing codec operations: no runtime metadata, allocation, or dynamic dispatch is introduced.

§Deliberate limits

wire-repr is a byte-representation compiler, not a runtime schema system. It does not provide reflection, descriptors, schema walkers, repeated sequences, tagged unions, arbitrary conditional fields, checksums, protocol state, allocation policy, or I/O. Custom codecs and consumer validation remain ordinary explicit Rust.

Re-exports§

pub use codec::BeI16;
pub use codec::BeI32;
pub use codec::BeI64;
pub use codec::BeI128;
pub use codec::BeU16;
pub use codec::BeU24;
pub use codec::BeU32;
pub use codec::BeU64;
pub use codec::BeU128;
pub use codec::Bytes;
pub use codec::EncodePlan;
pub use codec::ExactWidthError;
pub use codec::FixedCodec;
pub use codec::I8;
pub use codec::LeI16;
pub use codec::LeI32;
pub use codec::LeI64;
pub use codec::LeI128;
pub use codec::LeU16;
pub use codec::LeU24;
pub use codec::LeU32;
pub use codec::LeU64;
pub use codec::LeU128;
pub use codec::PrefixCodec;
pub use codec::PrefixExtent;
pub use codec::U8;
pub use codec::U24RangeError;

Modules§

codec
Codec contracts and built-in codecs.

Macros§

wire_repr
Declares sequential or fixed absolute-offset byte-backed layouts.