bitbuffer 0.11.3

Reading bit sequences from a byte slice
Documentation
use bitbuffer::{
    BigEndian, BitRead, BitReadBuffer, BitReadStream, BitWriteStream, Endianness, LittleEndian,
};
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
use std::hint::black_box;

const ONES: &[u8; 1024 * 1024 * 10] = &[1u8; 1024 * 1024 * 10];

const ONES_LE: BitReadBuffer<'static, LittleEndian> = BitReadBuffer::new(ONES, LittleEndian);
const ONES_BE: BitReadBuffer<'static, BigEndian> = BitReadBuffer::new(ONES, BigEndian);

#[library_benchmark]
#[bench::le(ONES_LE)]
#[bench::be(ONES_BE)]
fn read_perf<E: Endianness>(buffer: BitReadBuffer<E>) -> u16 {
    let size = 5;
    let mut pos = 0;
    let len = buffer.bit_len();
    let mut result: u16 = 0;
    loop {
        if pos + size > len {
            return black_box(result);
        }
        let data = buffer.read_int::<u64>(pos, size).unwrap() as u16;
        result = result.wrapping_add(data);
        pos += size;
    }
}

#[library_benchmark]
#[bench::le(ONES_LE)]
#[bench::be(ONES_BE)]
fn perf_f32<E: Endianness>(buffer: BitReadBuffer<E>) -> f32 {
    let mut pos = 0;
    let len = buffer.bit_len();
    let mut result: f32 = 0.0;
    loop {
        if pos + 32 > len {
            break;
        }
        let num = buffer.read_float::<f32>(pos).unwrap();
        result += num;
        pos += 32;
    }
    assert_eq!(result, 0.00000000000000000000000000000006170106);
    black_box(result)
}
const F64_RESULT: f64 = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010156250477904244;

#[library_benchmark]
#[bench::le(ONES_LE)]
#[bench::be(ONES_BE)]
fn perf_f64<E: Endianness>(buffer: BitReadBuffer<E>) -> f64 {
    let mut pos = 0;
    let len = buffer.bit_len();
    let mut result: f64 = 0.0;
    loop {
        if pos + 64 > len {
            break;
        }
        let num = buffer.read_float::<f64>(pos).unwrap();
        result += num;
        pos += 64;
    }
    assert_eq!(result, F64_RESULT);
    black_box(result)
}

#[library_benchmark]
#[bench::le(ONES_LE)]
#[bench::be(ONES_BE)]
fn perf_bool<E: Endianness>(buffer: BitReadBuffer<E>) {
    let mut pos = 0;
    let len = buffer.bit_len() / 8;
    loop {
        if pos >= len {
            break;
        }
        let num = buffer.read_bool(pos).unwrap();
        black_box(num);
        pos += 1;
    }
}

fn build_string_buffer<E: Endianness>(
    offset: usize,
    endianness: E,
) -> (usize, BitReadBuffer<'static, E>) {
    let mut data = Vec::new();
    let input = [
        "foo\0",
        "bar\0",
        "something a little bit longer for extra testing\0",
        "a\0",
        "\0",
    ]
    .join("");
    let mut writer = BitWriteStream::new(&mut data, endianness);
    writer.write_int(0usize, offset).unwrap();
    while writer.byte_len() < 10 * 1024 {
        writer.write(&input).unwrap()
    }
    (offset, BitReadBuffer::new_owned(data, endianness))
}

#[library_benchmark(setup = build_string_buffer)]
#[bench::le_alligned(0, LittleEndian)]
#[bench::be_alligned(0, BigEndian)]
#[bench::le_unalligned(3, LittleEndian)]
#[bench::be_unalligned(3, BigEndian)]
fn perf_string<E: Endianness>((offset, buffer): (usize, BitReadBuffer<E>)) {
    let mut pos = offset;
    let len = buffer.bit_len();
    loop {
        if pos + (128 * 8) > len {
            break;
        }
        let result = buffer.read_string(pos, None).unwrap();
        pos += (result.len() + 1) * 8;
        black_box(result);
    }
}

#[library_benchmark(setup = build_string_buffer)]
#[bench::le_alligned(0, LittleEndian)]
#[bench::be_alligned(0, BigEndian)]
#[bench::le_unalligned(3, LittleEndian)]
#[bench::be_unalligned(3, BigEndian)]
fn perf_bytes<E: Endianness>((offset, buffer): (usize, BitReadBuffer<E>)) {
    let mut pos = offset;
    let len = buffer.bit_len();
    loop {
        if pos + (128 * 8) > len {
            break;
        }
        let result = buffer.read_bytes(pos, 128).unwrap();
        pos += (result.len() + 1) * 8;
        black_box(result);
    }
}

#[allow(dead_code)]
#[derive(BitRead)]
struct BasicStruct {
    a: f32,
    b: bool,
    #[size = 7]
    c: u32,
}

const BASIC: BasicStruct = BasicStruct {
    a: 0.0,
    b: false,
    c: 0,
};

#[allow(dead_code)]
#[derive(BitRead)]
#[discriminant_bits = 2]
enum BasicEnum {
    #[size = 5]
    Foo(i8),
    Bar(bool),
    Asd(u8),
    Empty,
}

const ENUM: BasicEnum = BasicEnum::Empty;

#[library_benchmark]
#[bench::le(ONES_LE, BASIC)]
#[bench::be(ONES_BE, BASIC)]
#[bench::le_enum(ONES_LE, ENUM)]
#[bench::be_enum(ONES_BE, ENUM)]
fn perf_struct<E: Endianness, Struct: BitRead<'static, E>>(
    buffer: BitReadBuffer<E>,
    _struct: Struct,
) {
    let mut stream: BitReadStream<E> = buffer.into();
    while stream.bits_left() > 40 {
        let result = stream.read::<BasicStruct>().unwrap();
        black_box(result);
    }
}

library_benchmark_group!(
    name = bench_read_primitives;
    benchmarks = read_perf, perf_bool, perf_f32, perf_f64
);

library_benchmark_group!(
    name = bench_read_string;
    benchmarks = perf_string
);

library_benchmark_group!(
    name = bench_read_bytes;
    benchmarks = perf_bytes
);

library_benchmark_group!(
    name = bench_read_struct;
    benchmarks = perf_struct
);

main!(
    library_benchmark_groups = bench_read_primitives,
    bench_read_string,
    bench_read_bytes,
    bench_read_struct
);