use integer_encoding::VarInt;
use usize_cast::IntoUsize as _;
use crate::MltError::{BufferUnderflow, UnsupportedPhysicalEncoding};
use crate::codecs::fastpfor::decode_fastpfor;
use crate::utils::take;
use crate::{Decoder, MltRefResult, MltResult};
pub fn encode_bools_to_bytes(
bools: impl ExactSizeIterator<Item = bool>,
target: &mut Vec<u8>,
) -> &[u8] {
let num_bytes = bools.len().div_ceil(8);
target.clear();
target.resize(num_bytes, 0u8);
for i in bools.enumerate().filter_map(|(i, bit)| bit.then_some(i)) {
target[i / 8] |= 1 << (i % 8);
}
target
}
pub trait PhysicalWord: Copy + Sized + VarInt {
fn from_le_word(bytes: &[u8]) -> Self;
fn decode_fastpfor(data: &[u8], num_values: u32, dec: &mut Decoder) -> MltResult<Vec<Self>>;
}
impl PhysicalWord for u32 {
#[inline]
fn from_le_word(bytes: &[u8]) -> Self {
Self::from_le_bytes(bytes.try_into().expect("infallible: 4-byte chunk"))
}
fn decode_fastpfor(data: &[u8], num_values: u32, dec: &mut Decoder) -> MltResult<Vec<Self>> {
decode_fastpfor(data, num_values, dec)
}
}
impl PhysicalWord for u64 {
#[inline]
fn from_le_word(bytes: &[u8]) -> Self {
Self::from_le_bytes(bytes.try_into().expect("infallible: 8-byte chunk"))
}
fn decode_fastpfor(_data: &[u8], _num_values: u32, _dec: &mut Decoder) -> MltResult<Vec<Self>> {
Err(UnsupportedPhysicalEncoding("FastPFOR decoding u64"))
}
}
pub fn decode_bytes_to_words<'a, T: PhysicalWord>(
mut input: &'a [u8],
num_values: u32,
dec: &mut Decoder,
) -> MltRefResult<'a, Vec<T>> {
let width = u32::try_from(size_of::<T>()).expect("word size fits u32");
let Some(expected_bytes) = num_values.checked_mul(width) else {
return Err(BufferUnderflow(u32::MAX, input.len()));
};
if input.len() < expected_bytes.into_usize() {
return Err(BufferUnderflow(expected_bytes, input.len()));
}
let alloc_size = num_values.into_usize();
let mut values = dec.alloc(alloc_size)?;
for _ in 0..num_values {
let (new_input, bytes) = take(input, width)?;
values.push(T::from_le_word(bytes));
input = new_input;
}
debug_assert_length(&values, alloc_size);
Ok((input, values))
}
#[inline]
pub fn debug_assert_length<T>(buffer: &[T], expected_len: usize) {
debug_assert_eq!(
buffer.len(),
expected_len,
"Expected buffer to have exact length"
);
}
#[cfg(test)]
mod tests {
use bitvec::order::Lsb0;
use bitvec::view::BitView as _;
use proptest::prelude::*;
use super::*;
use crate::test_helpers::{assert_empty, dec};
proptest! {
#[test]
fn encode_bools_to_bytes_roundtrip(bools: Vec<bool>) {
let mut bytes = Vec::new();
let data = encode_bools_to_bytes(bools.iter().copied(), &mut bytes);
let packed = &data.view_bits::<Lsb0>()[..bools.len()];
prop_assert_eq!(packed.iter().by_vals().collect::<Vec<bool>>(), bools);
}
#[test]
fn test_u32_bytes_roundtrip(data: Vec<u32>) {
let mut encoded = Vec::with_capacity(data.len() * 4);
for val in &data {
encoded.extend_from_slice(&val.to_le_bytes());
}
let decoded = assert_empty(decode_bytes_to_words::<u32>(&encoded, u32::try_from(data.len()).unwrap(), &mut dec()));
prop_assert_eq!(data, decoded);
}
#[test]
fn test_u64_bytes_roundtrip(data: Vec<u64>) {
let mut encoded = Vec::with_capacity(data.len() * 8);
for val in &data {
encoded.extend_from_slice(&val.to_le_bytes());
}
let decoded = assert_empty(decode_bytes_to_words::<u64>(&encoded, u32::try_from(data.len()).unwrap(), &mut dec()));
prop_assert_eq!(data, decoded);
}
}
#[test]
fn test_bytes_to_u32s_valid() {
let bytes: [u8; 8] = [0x04, 0x03, 0x02, 0x01, 0xDD, 0xCC, 0xBB, 0xAA];
let u32s = assert_empty(decode_bytes_to_words::<u32>(&bytes, 2, &mut dec()));
assert_eq!(
u32s,
vec![0x0102_0304, 0xAABB_CCDD],
"Decoded values should match"
);
}
#[test]
fn test_bytes_to_u32s_empty() {
let bytes: [u8; 0] = [];
let u32s = assert_empty(decode_bytes_to_words::<u32>(&bytes, 0, &mut dec()));
assert!(
u32s.is_empty(),
"Output should be an empty Vec for 0 values"
);
}
#[test]
fn test_bytes_to_u32s_buffer_underflow() {
let bytes = [0x01, 0x02, 0x03, 0x04];
let res = decode_bytes_to_words::<u32>(&bytes, 2, &mut dec());
assert!(
res.is_err(),
"Should error if not enough bytes for requested values"
);
}
#[test]
fn test_bytes_to_u32s_partial_consumption() {
let bytes: [u8; 12] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
];
let res = decode_bytes_to_words::<u32>(&bytes, 2, &mut dec());
assert!(res.is_ok(), "Should decode 2 values from larger buffer");
let (remaining, u32s) = res.unwrap();
assert_eq!(remaining.len(), 4, "Should have 4 bytes remaining");
assert_eq!(u32s.len(), 2);
assert_eq!(u32s, vec![0x0403_0201, 0x0807_0605]);
}
#[test]
fn test_decode_u32() {
let bytes = [1, 0, 0, 0, 2, 0, 0, 0];
let expected = (&[][..], vec![1, 2]);
let decoded = decode_bytes_to_words::<u32>(&bytes, 2, &mut dec()).unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn test_decode_u64() {
let bytes = [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0];
let expected = (&[][..], vec![1, 2]);
let decoded = decode_bytes_to_words::<u64>(&bytes, 2, &mut dec()).unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn test_decode_bytes_to_u32s_empty() {
let decoded = assert_empty(decode_bytes_to_words::<u32>(&[], 0, &mut dec()));
assert_eq!(decoded, [] as [u32; 0]);
}
}