bitcram 0.3.0

A small, derive-based bit packing library for compact integer representations
Documentation
mod types;

use bitcram::{Buffer, Packable};
use types::*;

fn assert_round_trip<P, B>(value: P)
where
    P: Packable<B> + std::fmt::Debug + PartialEq,
    B: Buffer,
{
    let packed = value.pack();
    let unpacked = P::unpack(packed);
    assert_eq!(value, unpacked);
}

#[test]
fn struct_unit() {
    assert_eq!(UnitStruct::SIZE, 0);
    assert_round_trip(UnitStruct);
}

#[test]
fn struct_tuple() {
    assert_eq!(TupleStruct::SIZE, 6);
    assert_round_trip(TupleStruct(U3(1), U3(2), UnitStruct));
}

#[test]
fn struct_named() {
    assert_eq!(NamedStruct::SIZE, 6);
    assert_round_trip(NamedStruct { x: U3(3), y: U3(5) });
}

#[test]
fn enum_single_variant() {
    assert_eq!(SingleVariantEnum::SIZE, 0);
    assert_round_trip(SingleVariantEnum::Only);
}

#[test]
fn enum_mixed_variants() {
    // 4 variants → 2-bit index; max payload is Tuple(U3 + TupleStruct) = 9 bits
    assert_eq!(MixedEnum::SIZE, 11);

    assert_round_trip(MixedEnum::UnitVariant);
    assert_round_trip(MixedEnum::EmptyTuple());
    assert_round_trip(MixedEnum::Tuple(
        U3(1),
        TupleStruct(U3(2), U3(3), UnitStruct),
    ));
    assert_round_trip(MixedEnum::Named {
        x: UnitStruct,
        y: TupleStruct(U3(4), U3(5), UnitStruct),
    });
}

#[test]
fn generic_struct() {
    assert_eq!(GenericPair::<U3>::SIZE, 6);
    assert_round_trip(GenericPair { x: U3(1), y: U3(2) });
}

#[test]
fn buffer_boundary_u8() {
    assert_eq!(FullU8::SIZE, 8);
    assert_round_trip(FullU8 {
        hi: Nibble(0x0),
        lo: Nibble(0x0),
    });
    assert_round_trip(FullU8 {
        hi: Nibble(0xF),
        lo: Nibble(0xF),
    });
    assert_round_trip(FullU8 {
        hi: Nibble(0xA),
        lo: Nibble(0x5),
    });
}

#[test]
fn buffer_boundary_u128() {
    assert_eq!(FullU128::SIZE, 128);
    assert_round_trip(FullU128 {
        hi: U64v(0),
        lo: U64v(0),
    });
    assert_round_trip(FullU128 {
        hi: U64v(u64::MAX),
        lo: U64v(u64::MAX),
    });
    assert_round_trip(FullU128 {
        hi: U64v(0xDEAD_BEEF_CAFE_BABE),
        lo: U64v(0x0123_4567_89AB_CDEF),
    });
}

#[test]
fn option() {
    assert_eq!(Option::<U3>::SIZE, 4);
    assert_round_trip(Some(U3(5)));
    assert_round_trip(None::<U3>);
}

#[test]
fn boolean() {
    assert_eq!(<bool as Packable<u128>>::SIZE, 1);
    assert_round_trip::<_, u128>(true);
    assert_round_trip::<_, u128>(false);
}

#[test]
fn tuple() {
    assert_eq!(<(U3, U3, U3) as Packable<u128>>::SIZE, 9);
    assert_round_trip((U3(1),));
    assert_round_trip((U3(1), U3(2)));
    assert_round_trip((U3(1), U3(2), U3(3)));
    assert_round_trip((U3(7), U3(0), U3(5), U3(3), U3(1)));
}

#[test]
fn array() {
    assert_eq!(<[U3; 4] as Packable<u128>>::SIZE, 12);
    assert_round_trip([U3(1), U3(2), U3(3), U3(4)]);
}

#[test]
fn bits_struct() {
    assert_eq!(BitsStruct::SIZE, 13);
    assert_round_trip(BitsStruct { x: 0, y: 0, z: 0 });
    assert_round_trip(BitsStruct { x: 31, y: 31, z: 7 });
    assert_round_trip(BitsStruct { x: 5, y: 12, z: 3 });
}

#[test]
fn bits_tuple() {
    assert_eq!(BitsTuple::SIZE, 8);
    assert_round_trip(BitsTuple(0, 0));
    assert_round_trip(BitsTuple(0xF, 0xF));
    assert_round_trip(BitsTuple(0xA, 0x5));
}

#[test]
fn bits_enum() {
    // 3 variants → 2-bit index; max payload is Pair(4 + 1) = 5 bits, but
    // Value(8) > Pair(5), so max payload is 8. Total: 2 + 8 = 10.
    assert_eq!(BitsEnum::SIZE, 10);
    assert_round_trip(BitsEnum::Empty);
    assert_round_trip(BitsEnum::Value(200));
    assert_round_trip(BitsEnum::Pair { a: 0xF, b: true });
    assert_round_trip(BitsEnum::Pair { a: 0x0, b: false });
}

#[test]
fn multi_buffer() {
    assert_eq!(<MultiBuffer as Packable<u16>>::SIZE, 10);
    assert_eq!(<MultiBuffer as Packable<u32>>::SIZE, 10);
    assert_eq!(<MultiBuffer as Packable<u64>>::SIZE, 10);

    let v = MultiBuffer { x: 5, y: 12 };
    assert_round_trip::<_, u16>(v.clone());
    assert_round_trip::<_, u32>(v.clone());
    assert_round_trip::<_, u64>(v);
}