squitter 0.1.1

no_std, no_alloc parser and encoder for 1090ES/DF17 (ADS-B extended squitter) messages
Documentation
//! Bit-level codec for Mode S frame data.

mod reader;
mod writer;

pub use reader::BitReader;
pub use writer::BitWriter;

#[cfg(kani)]
mod kani_proofs {
    use super::{BitReader, BitWriter};

    const BUF_LEN: usize = 8;

    // BitReader must never panic for any input, including an out-of-range
    // nbits argument -- only the documented error returns.
    #[kani::proof]
    fn read_u64_never_panics() {
        let data: [u8; 4] = kani::any();
        let nbits: u32 = kani::any();
        let mut r = BitReader::new(&data);
        let _ = r.read_u64(nbits);
    }

    // write_bits must report BufferFull/OutOfRange rather than panic, for
    // any output buffer size (including zero) and any value/width
    // combination. nbits is bounded purely so CBMC's loop unwinding has a
    // concrete bound; the nbits > 64 check is exercised well within it.
    #[kani::proof]
    #[kani::unwind(65)]
    fn write_bits_never_panics() {
        let mut buf: [u8; 4] = kani::any();
        let value: u64 = kani::any();
        let nbits: u32 = kani::any();
        kani::assume(nbits <= 100);
        let mut w = BitWriter::new(&mut buf);
        let _ = w.write_bits(value, nbits);
    }

    #[kani::proof]
    #[kani::unwind(65)]
    fn write_read_u64_roundtrip() {
        let nbits: u32 = kani::any();
        kani::assume(nbits >= 1 && nbits <= 64);
        let value: u64 = kani::any();
        let masked = if nbits == 64 {
            value
        } else {
            value & ((1u64 << nbits) - 1)
        };

        let mut buf = [0u8; BUF_LEN];
        let mut w = BitWriter::new(&mut buf);
        w.write_bits(masked, nbits).unwrap();

        let mut r = BitReader::new(&buf);
        assert_eq!(r.read_u64(nbits).unwrap(), masked);
    }
}

// Kani proves the properties below exhaustively, but its toolchain isn't
// installed in every dev/CI environment (see the crate README); these
// proptest versions give fast sampled coverage of the same properties under
// plain `cargo test`.
#[cfg(test)]
mod proptests {
    use proptest::prelude::*;

    use super::{BitReader, BitWriter};

    proptest! {
        #[test]
        fn read_u64_never_panics(data: [u8; 4], nbits: u32) {
            let mut r = BitReader::new(&data);
            let _ = r.read_u64(nbits);
        }

        #[test]
        fn write_bits_never_panics(mut buf: [u8; 4], value: u64, nbits in 0u32..=100) {
            let mut w = BitWriter::new(&mut buf);
            let _ = w.write_bits(value, nbits);
        }

        #[test]
        fn write_read_u64_roundtrip(nbits in 1u32..=64, value: u64) {
            let masked = if nbits == 64 { value } else { value & ((1u64 << nbits) - 1) };

            let mut buf = [0u8; 8];
            let mut w = BitWriter::new(&mut buf);
            w.write_bits(masked, nbits).unwrap();

            let mut r = BitReader::new(&buf);
            prop_assert_eq!(r.read_u64(nbits).unwrap(), masked);
        }
    }
}