wire-repr 0.3.0

A no_std, no_alloc Rust library for wire representations.
Documentation
use wire_repr::{U24RangeError, wire_repr};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Kind(u16);

impl From<u16> for Kind {
    fn from(value: u16) -> Self {
        Self(value)
    }
}

impl From<Kind> for u16 {
    fn from(value: Kind) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Address([u8; 4]);

impl From<[u8; 4]> for Address {
    fn from(value: [u8; 4]) -> Self {
        Self(value)
    }
}

impl From<Address> for [u8; 4] {
    fn from(value: Address) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Flags(u8);

impl From<u8> for Flags {
    fn from(value: u8) -> Self {
        Self(value)
    }
}

impl From<Flags> for u8 {
    fn from(value: Flags) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Value(u32);

impl From<u32> for Value {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl From<Value> for u32 {
    fn from(value: Value) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Length(u8);

impl From<u8> for Length {
    fn from(value: u8) -> Self {
        Self(value)
    }
}

impl From<Length> for u8 {
    fn from(value: Length) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Endpoint(u16);

impl From<u16> for Endpoint {
    fn from(value: u16) -> Self {
        Self(value)
    }
}

impl From<Endpoint> for u16 {
    fn from(value: Endpoint) -> Self {
        value.0
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Code(u8);

impl From<u8> for Code {
    fn from(value: u8) -> Self {
        Self(value)
    }
}

impl From<Code> for u8 {
    fn from(value: Code) -> Self {
        value.0
    }
}

wire_repr! {
    pub layout Sequential {
        field kind: BeU16 as crate::Kind;
        field address: bytes(4) as crate::Address;
        field flags: U8 as crate::Flags {
            projections {
                bit enabled: 0;
                bits mode: 1..=3;
            }
        }
        field value: BeU24 as crate::Value;
    }

    pub absolute layout Absolute {
        field kind: BeU16 as crate::Kind { offset: 5; }
        field address: bytes(4) as crate::Address { offset: 0; }
        field code: U8 as crate::Code { offset: 4; }
    }

    pub layout Dynamic {
        field length: U8 as crate::Length;
        field payload: bytes(current_pos..current_pos + length);
        field code: U8 as crate::Code;
    }

    pub layout AbsoluteEndpoint {
        field tag: U8 { position: 1; }
        field end: BeU16 as crate::Endpoint { position: 2; }
        padding { position: 3; length: 1; }
        align { position: 4; boundary: 4; }
        field payload: bytes(current_pos..end) { position: 5; }
        field tail: U8 { position: 6; }
    }

    pub layout EndpointCases {
        field tag: U8 { position: 1; }
        field end: U8 { position: 2; }
        field payload: bytes(current_pos..end) { position: 3; }
    }

    pub layout SignedEndpoint {
        field end: BeI16 { position: 1; }
        field payload: bytes(current_pos..end) { position: 2; }
    }
}

#[test]
fn sequential_mappings_preserve_semantics_raw_values_and_atomic_failures() {
    let input = [0x12, 0x34, 1, 2, 3, 4, 0b0000_1011, 0x12, 0x34, 0x56, 0xee];
    let (view, suffix) = SequentialView::parse_prefix(&input).unwrap();
    assert_eq!(suffix, &[0xee]);
    assert_eq!(view.kind(), Kind(0x1234));
    assert_eq!(view.kind_raw(), 0x1234);
    assert_eq!(view.address(), Address([1, 2, 3, 4]));
    assert_eq!(view.address_raw(), [1, 2, 3, 4]);
    assert_eq!(view.flags(), Flags(0b0000_1011));
    assert_eq!(view.flags_raw(), 0b0000_1011);
    assert!(view.enabled());
    assert_eq!(view.mode(), 5);
    assert_eq!(view.value(), Value(0x12_3456));
    assert_eq!(view.value_raw(), 0x12_3456);
    assert_eq!(view.as_bytes(), &input[..10]);
    assert!(SequentialView::parse_exact(&input).is_err());
    assert_eq!(
        SequentialView::parse_exact(&input[..10]).unwrap().kind(),
        Kind(0x1234)
    );

    let mut owned_address = view.address();
    owned_address.0[0] = 9;
    let mut owned_raw_address = view.address_raw();
    owned_raw_address[1] = 8;
    assert_eq!(view.address(), Address([1, 2, 3, 4]));
    assert_eq!(view.address_raw(), [1, 2, 3, 4]);

    let mut bytes: [u8; 10] = input[..10].try_into().unwrap();
    let mut mutable = SequentialViewMut::parse_exact_mut(&mut bytes).unwrap();
    mutable.set_kind(Kind(1)).unwrap();
    mutable.set_kind_raw(0xabcd).unwrap();
    mutable.set_address(Address([0, 0, 0, 0])).unwrap();
    mutable.set_address_raw([9, 8, 7, 6]).unwrap();
    mutable.set_flags_raw(0).unwrap();
    mutable.set_flags(Flags(0b0000_0010)).unwrap();
    mutable.set_value(Value(1)).unwrap();
    mutable.set_value_raw(0x00_abcd).unwrap();
    assert_eq!(
        mutable.as_bytes(),
        &[0xab, 0xcd, 9, 8, 7, 6, 2, 0, 0xab, 0xcd]
    );
    let before = [0xab, 0xcd, 9, 8, 7, 6, 2, 0, 0xab, 0xcd];
    assert!(matches!(
        mutable.set_value(Value(0x01_000000)),
        Err(SequentialMutationError::FieldValue(error)) if error == U24RangeError::new(0x01_000000)
    ));
    assert_eq!(mutable.as_bytes(), before);
    assert!(matches!(
        mutable.set_value_raw(0x01_000000),
        Err(SequentialMutationError::FieldValue(error)) if error == U24RangeError::new(0x01_000000)
    ));
    assert_eq!(mutable.as_bytes(), before);

    let mut output = [0xa5; 11];
    let (built, suffix) = SequentialBuilder::new()
        .kind_raw(1)
        .kind(Kind(0x1234))
        .address(Address([9, 9, 9, 9]))
        .address_raw([1, 2, 3, 4])
        .flags_raw(0)
        .flags(Flags(0b0000_1011))
        .value_raw(1)
        .value(Value(0x12_3456))
        .build_into(&mut output)
        .unwrap();
    assert_eq!(built.as_bytes(), &input[..10]);
    assert_eq!(suffix, [0xa5]);

    let mut unchanged = [0x5a; 10];
    assert!(matches!(
        SequentialBuilder::new()
            .kind(Kind(1))
            .address(Address([1, 2, 3, 4]))
            .flags(Flags(0))
            .value(Value(0x01_000000))
            .build_into(&mut unchanged),
        Err(SequentialWriteError::FieldValue(error)) if error == U24RangeError::new(0x01_000000)
    ));
    assert_eq!(unchanged, [0x5a; 10]);
    let mut short = [0x3c; 9];
    assert!(matches!(
        SequentialBuilder::new()
            .kind(Kind(1))
            .address(Address([1, 2, 3, 4]))
            .flags(Flags(0))
            .value(Value(1))
            .build_into(&mut short),
        Err(SequentialWriteError::OutputTooShort {
            needed: 10,
            available: 9
        })
    ));
    assert_eq!(short, [0x3c; 9]);
}

#[test]
fn absolute_mappings_use_offsets_for_both_builder_forms_and_mutation() {
    let input = [1, 2, 3, 4, 7, 0x12, 0x34, 0xff];
    let (view, suffix) = AbsoluteView::parse_prefix(&input).unwrap();
    assert_eq!(suffix, &[0xff]);
    assert_eq!(view.address(), Address([1, 2, 3, 4]));
    assert_eq!(view.address_raw(), [1, 2, 3, 4]);
    assert_eq!(view.code(), Code(7));
    assert_eq!(view.code_raw(), 7);
    assert_eq!(view.kind(), Kind(0x1234));
    assert_eq!(view.kind_raw(), 0x1234);
    assert!(AbsoluteView::parse_exact(&input).is_err());

    let mut bytes: [u8; 7] = input[..7].try_into().unwrap();
    let mut mutable = AbsoluteViewMut::parse_exact_mut(&mut bytes).unwrap();
    mutable.set_kind_raw(0xabcd).unwrap();
    mutable.set_address(Address([9, 8, 7, 6])).unwrap();
    mutable.set_code_raw(3).unwrap();
    assert_eq!(mutable.as_bytes(), &[9, 8, 7, 6, 3, 0xab, 0xcd]);

    let mut output = [0xde; 8];
    let (built, suffix) = AbsoluteBuilder::new()
        .kind_raw(1)
        .kind(Kind(0x1234))
        .address_raw([0, 0, 0, 0])
        .address(Address([1, 2, 3, 4]))
        .code(Code(2))
        .code_raw(7)
        .build_into(&mut output)
        .unwrap();
    assert_eq!(built.as_bytes(), &input[..7]);
    assert_eq!(suffix, [0xde]);
}

#[test]
fn dynamic_mappings_derive_range_lengths_without_losing_boundaries() {
    let input = [3, b'a', b'b', b'c', 7, 0xee];
    let (view, suffix) = DynamicView::parse_prefix(&input).unwrap();
    assert_eq!(suffix, &[0xee]);
    assert_eq!(view.length(), Length(3));
    assert_eq!(view.length_raw(), 3);
    assert_eq!(view.payload(), b"abc");
    assert_eq!(view.code(), Code(7));
    assert_eq!(view.code_raw(), 7);
    assert_eq!(view.as_bytes(), &input[..5]);
    assert!(DynamicView::parse_exact(&input).is_err());

    let mut bytes: [u8; 5] = input[..5].try_into().unwrap();
    let mut mutable = DynamicViewMut::parse_exact_mut(&mut bytes).unwrap();
    mutable.set_code(Code(8)).unwrap();
    mutable.set_code_raw(9).unwrap();
    assert_eq!(mutable.code(), Code(9));
    assert_eq!(mutable.as_bytes(), &[3, b'a', b'b', b'c', 9]);

    let mut output = [0xa5; 7];
    let (built, suffix) = DynamicBuilder::new()
        .payload(b"wxyz")
        .code_raw(1)
        .code(Code(7))
        .build_into(&mut output)
        .unwrap();
    assert_eq!(built.length(), Length(4));
    assert_eq!(built.length_raw(), 4);
    assert_eq!(built.payload(), b"wxyz");
    assert_eq!(built.code(), Code(7));
    assert_eq!(built.as_bytes(), &[4, b'w', b'x', b'y', b'z', 7]);
    assert_eq!(suffix, [0xa5]);
}

#[test]
fn absolute_endpoint_mapped_raw_source_preserves_physical_endpoints_and_suffix() {
    let input = [0xaa, 0, 6, 0xa5, b'x', b'y', 9, 0xee];
    let (view, suffix) = AbsoluteEndpointView::parse_prefix(&input).unwrap();
    assert_eq!(view.end(), Endpoint(6));
    assert_eq!(view.end_raw(), 6);
    assert_eq!(view.payload(), b"xy");
    assert_eq!(view.tail(), 9);
    assert_eq!(view.as_bytes(), &input[..7]);
    assert_eq!(suffix, &[0xee]);
    assert!(matches!(
        AbsoluteEndpointView::parse_exact(&input),
        Err(AbsoluteEndpointError::TrailingBytes {
            expected: 7,
            actual: 8
        })
    ));

    let mut output = [0xa5; 9];
    let (view, suffix) = AbsoluteEndpointBuilder::new()
        .tag(0xaa)
        .payload(b"xy")
        .tail(9)
        .build_into(&mut output)
        .unwrap();
    assert_eq!(view.as_bytes(), &[0xaa, 0, 6, 0xa5, b'x', b'y', 9]);
    assert_eq!(view.end(), Endpoint(6));
    assert_eq!(view.end_raw(), 6);
    assert_eq!(suffix, &[0xa5, 0xa5]);
}

#[test]
fn absolute_endpoint_parse_boundaries_and_signed_conversion_are_precise() {
    let empty = EndpointCasesView::parse_exact(&[0xaa, 2]).unwrap();
    assert!(empty.payload().is_empty());
    assert_eq!(empty.as_bytes(), &[0xaa, 2]);

    assert!(matches!(
        EndpointCasesView::parse_prefix(&[0xaa, 1]),
        Err(EndpointCasesError::RangeEndBeforeStart {
            position: 3,
            source_position: 2,
            end: 1,
            start: 2,
        })
    ));
    assert!(matches!(
        EndpointCasesView::parse_prefix(&[0xaa, 6]),
        Err(EndpointCasesError::InputTooShort {
            position: 3,
            expected: 4,
            available: 0,
        })
    ));
    assert!(matches!(
        SignedEndpointView::parse_prefix(&[0xff, 0xff]),
        Err(SignedEndpointError::InvalidRangeSource {
            position: 2,
            source_position: 1,
        })
    ));
}