prefix_uvarint 0.2.0

Prefix based variable length integer coding.
Documentation
use rand::distributions::uniform::SampleUniform;
use rand::distributions::Uniform;
use rand::prelude::*;

fn bounds_u64() -> impl Iterator<Item = (u64, u64)> {
    crate::MAX_VALUE
        .iter()
        .copied()
        .zip(crate::MAX_VALUE.iter().skip(1).copied())
        .map(|(min, max)| if min > 0 { (min + 1, max) } else { (min, max) })
}

fn bounds_i64() -> impl Iterator<Item = (i64, i64)> {
    bounds_u64().map(|(_, max)| (crate::zigzag_decode(max), crate::zigzag_decode(max - 1)))
}

fn generate_array<V: SampleUniform + Copy>(len: usize, min: V, max: V) -> Vec<V> {
    let mut rng = StdRng::from_seed([0xabu8; 32]);
    (0..len)
        .map(|_| Uniform::from(min..=max).sample(&mut rng))
        .collect::<Vec<_>>()
}

const RANDOM_TEST_LEN: usize = 4096;

mod raw {
    use super::bounds_u64;
    use crate::{decode_prefix_uvarint, encode_prefix_uvarint, MAX_LEN};

    #[test]
    fn boundary_coding() {
        let mut buf = [0u8; MAX_LEN];
        for (len, (min, max)) in bounds_u64().enumerate().map(|(i, x)| (i + 1, x)) {
            assert_eq!(unsafe { encode_prefix_uvarint(min, buf.as_mut_ptr()) }, len);
            assert_eq!(unsafe { decode_prefix_uvarint(buf.as_ptr()) }, (min, len));
            assert_eq!(unsafe { encode_prefix_uvarint(max, buf.as_mut_ptr()) }, len);
            assert_eq!(unsafe { decode_prefix_uvarint(buf.as_ptr()) }, (max, len));
        }
    }
}

mod buf {
    use super::{bounds_i64, bounds_u64, generate_array, RANDOM_TEST_LEN};
    use crate::{VarintBuf, VarintBufMut, MAX_VALUE};

    macro_rules! test_random_buf_put_get {
        ($name:ident, $bounds:ident, $put:ident, $get:ident) => {
            #[test]
            fn $name() {
                for (min, max) in $bounds() {
                    let input_values = generate_array(RANDOM_TEST_LEN, min, max);
                    let mut buf_mut: Vec<u8> = Vec::new();
                    for v in input_values.iter() {
                        buf_mut.$put(*v);
                    }

                    let mut output_values = Vec::new();
                    let mut buf = buf_mut.as_slice();
                    for _ in 0..input_values.len() {
                        output_values.push(buf.$get().unwrap());
                    }

                    assert_eq!(input_values, output_values, "{}..{}", min, max);
                }
            }
        };
    }

    test_random_buf_put_get!(
        random_u64,
        bounds_u64,
        put_prefix_uvarint,
        get_prefix_uvarint
    );
    test_random_buf_put_get!(random_i64, bounds_i64, put_prefix_varint, get_prefix_varint);

    #[test]
    fn decode_empty_fail() {
        assert_eq!([].as_slice().get_prefix_uvarint(), None);
    }

    #[test]
    fn decode_tag_only_fail() {
        let mut tag = u8::MAX;
        while tag != 0 {
            assert_eq!([tag].as_slice().get_prefix_uvarint(), None, "{:#b}", tag);
            tag <<= 1;
        }
    }

    #[test]
    fn decode_truncated() {
        for v in MAX_VALUE.iter().skip(1) {
            let mut buf = Vec::new();
            buf.put_prefix_uvarint(*v);
            let mut trunc = &buf[0..(buf.len() - 1)];
            assert_eq!(trunc.get_prefix_uvarint(), None, "{}", *v);
        }
    }
}

mod io {
    use super::{bounds_i64, bounds_u64, generate_array, RANDOM_TEST_LEN};
    use crate::io::VarintWrite;

    macro_rules! test_random_io_write_read {
        ($name:ident, $reader:ident, $bounds:ident, $write:ident, $read:ident) => {
            #[test]
            fn $name() {
                use crate::io::$reader;

                for (min, max) in $bounds() {
                    let input_values = generate_array(RANDOM_TEST_LEN, min, max);
                    let mut write: Vec<u8> = Vec::new();
                    for v in input_values.iter() {
                        write.$write(*v).unwrap();
                    }

                    let mut output_values = Vec::new();
                    let mut read = write.as_slice();
                    while let Ok(v) = read.$read() {
                        output_values.push(v);
                    }

                    assert_eq!(input_values, output_values, "{}..{}", min, max);
                }
            }
        };
    }
    test_random_io_write_read!(
        random_read_u64,
        VarintRead,
        bounds_u64,
        write_prefix_uvarint,
        read_prefix_uvarint
    );
    test_random_io_write_read!(
        random_read_i64,
        VarintRead,
        bounds_i64,
        write_prefix_varint,
        read_prefix_varint
    );
    test_random_io_write_read!(
        random_bufread_u64,
        VarintBufRead,
        bounds_u64,
        write_prefix_uvarint,
        read_prefix_uvarint
    );
    test_random_io_write_read!(
        random_bufread_i64,
        VarintBufRead,
        bounds_i64,
        write_prefix_varint,
        read_prefix_varint
    );
}