pub fn packed_values(data: &[u8], bits: usize) -> PackedValues<'_> {
debug_assert!((1..=32).contains(&bits));
PackedValues {
data: data.iter(),
bits,
mask: (1u64 << bits) - 1,
accumulator: 0,
available: 0,
}
}
pub struct PackedValues<'a> {
data: std::slice::Iter<'a, u8>,
bits: usize,
mask: u64,
accumulator: u64,
available: usize,
}
impl Iterator for PackedValues<'_> {
type Item = i32;
fn next(&mut self) -> Option<i32> {
while self.available < self.bits {
let &byte = self.data.next()?;
self.accumulator |= (byte as u64) << self.available;
self.available += 8;
}
let value = sign_extend((self.accumulator & self.mask) as u32, self.bits);
self.accumulator >>= self.bits;
self.available -= self.bits;
Some(value)
}
}
pub fn sign_extend(raw: u32, bits: usize) -> i32 {
let shift = 32 - bits as u32;
((raw << shift) as i32) >> shift
}
#[cfg(test)]
mod tests {
use super::*;
fn unpack(data: &[u8], bits: usize) -> Vec<i32> {
packed_values(data, bits).collect()
}
#[test]
fn packed_values_sign_extend() {
assert_eq!(unpack(&[0xF7, 0x08], 4), vec![7, -1, -8, 0]);
}
#[test]
fn packed_values_full_width() {
assert_eq!(unpack(&(-24i32).to_le_bytes(), 32), vec![-24]);
assert_eq!(unpack(&(-24i16).to_le_bytes(), 16), vec![-24]);
}
#[test]
fn packed_values_unaligned_width() {
assert_eq!(unpack(&[0x41, 0x0C], 5), vec![1, 2, 3]);
}
#[test]
fn packed_values_ignore_trailing_partial_value() {
assert_eq!(unpack(&[0b11_010_001], 3), vec![1, 2]);
}
}