sdec-bitstream 0.8.0

Low-level bit packing primitives for the sdec codec
Documentation
use bitstream::{BitReader, BitVecWriter};
use proptest::prelude::*;

#[derive(Clone, Debug)]
enum Op {
    Bit(bool),
    Bits { bits: u8, value: u64 },
    Align,
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    VarU32(u32),
    VarS32(i32),
}

fn mask_value(bits: u8, value: u64) -> u64 {
    if bits >= 64 {
        value
    } else {
        let mask = (1u64 << bits) - 1;
        value & mask
    }
}

fn op_strategy() -> impl Strategy<Value = Op> {
    prop_oneof![
        any::<bool>().prop_map(Op::Bit),
        (1u8..=64, any::<u64>()).prop_map(|(bits, value)| Op::Bits {
            bits,
            value: mask_value(bits, value),
        }),
        Just(Op::Align),
        any::<u8>().prop_map(Op::U8),
        any::<u16>().prop_map(Op::U16),
        any::<u32>().prop_map(Op::U32),
        any::<u64>().prop_map(Op::U64),
        any::<u32>().prop_map(Op::VarU32),
        any::<i32>().prop_map(Op::VarS32),
    ]
}

proptest! {
    #[test]
    fn prop_roundtrip_ops(ops in prop::collection::vec(op_strategy(), 1..64)) {
        let mut writer = BitVecWriter::new();

        for op in &ops {
            match op {
                Op::Bit(b) => {
                    writer.write_bit(*b);
                }
                Op::Bits { bits, value } => {
                    writer.write_bits(*value, *bits).unwrap();
                }
                Op::Align => {
                    writer.align_to_byte();
                }
                Op::U8(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_u8_aligned(*v).unwrap();
                }
                Op::U16(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_u16_aligned(*v).unwrap();
                }
                Op::U32(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_u32_aligned(*v).unwrap();
                }
                Op::U64(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_u64_aligned(*v).unwrap();
                }
                Op::VarU32(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_varu32(*v).unwrap();
                }
                Op::VarS32(v) => {
                    if writer.bits_written() % 8 != 0 {
                        writer.align_to_byte();
                    }
                    writer.write_vars32(*v).unwrap();
                }
            }
        }

        let bytes = writer.finish();
        let mut reader = BitReader::new(&bytes);

        for op in &ops {
            match op {
                Op::Bit(b) => {
                    prop_assert_eq!(reader.read_bit().unwrap(), *b);
                }
                Op::Bits { bits, value } => {
                    prop_assert_eq!(reader.read_bits(*bits).unwrap(), *value);
                }
                Op::Align => {
                    reader.align_to_byte().unwrap();
                }
                Op::U8(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_u8_aligned().unwrap(), *v);
                }
                Op::U16(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_u16_aligned().unwrap(), *v);
                }
                Op::U32(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_u32_aligned().unwrap(), *v);
                }
                Op::U64(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_u64_aligned().unwrap(), *v);
                }
                Op::VarU32(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_varu32().unwrap(), *v);
                }
                Op::VarS32(v) => {
                    if reader.bit_position() % 8 != 0 {
                        reader.align_to_byte().unwrap();
                    }
                    prop_assert_eq!(reader.read_vars32().unwrap(), *v);
                }
            }
        }
    }
}