#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CodedBlock {
pub bytes: Vec<u8>,
pub nbits: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecodedBlock {
pub bytes: Vec<u8>,
pub nbits: usize,
}
pub(crate) fn set_bit(bytes: &mut [u8], i: usize) {
bytes[i / 8] |= 1 << (7 - (i % 8));
}
pub(crate) fn get_bit(bytes: &[u8], i: usize) -> u8 {
(bytes[i / 8] >> (7 - (i % 8))) & 1
}
#[cfg(test)]
pub(crate) fn pack(bits: &[u8], nbits: usize) -> Vec<u8> {
let mut out = vec![0u8; nbits.div_ceil(8)];
for (i, &b) in bits.iter().take(nbits).enumerate() {
if b & 1 == 1 {
set_bit(&mut out, i);
}
}
out
}
#[cfg(test)]
pub(crate) fn unpack(bytes: &[u8], nbits: usize) -> Vec<u8> {
(0..nbits).map(|i| get_bit(bytes, i)).collect()
}
pub(crate) fn unpack_into(bytes: &[u8], nbits: usize, out: &mut Vec<u8>) {
debug_assert!(
nbits.div_ceil(8) <= bytes.len(),
"unpack_into: bytes too short for nbits"
);
out.clear();
for i in 0..nbits {
out.push(get_bit(bytes, i));
}
}
pub(crate) fn pack_into(bits: &[u8], nbits: usize, out: &mut Vec<u8>) {
debug_assert!(
bits.len() >= nbits,
"pack_into: fewer than nbits input bits"
);
out.clear();
out.resize(nbits.div_ceil(8), 0);
for (i, &b) in bits.iter().take(nbits).enumerate() {
if b & 1 == 1 {
set_bit(out, i);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn pack_msb_first_byte_aligned() {
assert_eq!(pack(&[1, 0, 1, 1, 0, 0, 0, 0], 8), vec![0xB0]);
}
#[test]
fn pack_non_byte_aligned_zero_pads() {
assert_eq!(pack(&[1, 0, 1], 3), vec![0xA0]); }
#[test]
fn unpack_is_inverse_of_pack() {
let bits = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0];
assert_eq!(unpack(&pack(&bits, bits.len()), bits.len()), bits);
}
#[test]
fn empty_round_trips() {
assert_eq!(pack(&[], 0), Vec::<u8>::new());
assert_eq!(unpack(&[], 0), Vec::<u8>::new());
}
#[test]
fn pack_into_matches_pack_incl_non_byte_aligned() {
let bits = [1u8, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1]; for &n in &[0usize, 3, 8, 11] {
let mut out = Vec::new();
pack_into(&bits, n, &mut out);
assert_eq!(
out,
pack(&bits, n),
"pack_into must match pack for nbits={n}"
);
}
}
proptest! {
#[test]
fn unpack_pack_bit_roundtrip(bits in proptest::collection::vec(0u8..=1, 0..300)) {
let n = bits.len();
prop_assert_eq!(unpack(&pack(&bits, n), n), bits);
}
}
}