use zerocopy::FromBytes;
use zerocopy::byteorder::{F32, F64, I16, I32, LE};
macro_rules! impl_read_chunks {
($name:ident, $native:ty, $zerocopy:ty, $bytes:expr) => {
pub(crate) fn $name<T: AsRef<[u8]>>(chunks: &[T], sample_count: usize) -> Vec<$native> {
let mut output = Vec::with_capacity(sample_count);
let mut carry_buf = [0u8; $bytes];
let mut carry_len = 0;
for chunk in chunks {
if output.len() >= sample_count {
break;
}
let mut data = chunk.as_ref();
if carry_len > 0 {
let needed = $bytes - carry_len;
if data.len() < needed {
carry_buf[carry_len..carry_len + data.len()].copy_from_slice(data);
carry_len += data.len();
continue;
}
carry_buf[carry_len..].copy_from_slice(&data[..needed]);
let val = <$zerocopy>::read_from_bytes(&carry_buf).unwrap();
output.push(val.get());
carry_len = 0;
data = &data[needed..];
}
let remaining = sample_count - output.len();
let available_elements = data.len() / $bytes;
let count = remaining.min(available_elements);
let bytes_to_consume = count * $bytes;
let (bulk_bytes, rest) = data.split_at(bytes_to_consume);
let slice_ref = zerocopy::Ref::<&[u8], [$zerocopy]>::from_bytes(bulk_bytes)
.expect("slice length must be multiple of element size");
let vals: &[$zerocopy] = zerocopy::Ref::into_ref(slice_ref);
output.extend(vals.iter().map(|v| v.get()));
data = rest;
if !data.is_empty() && output.len() < sample_count {
carry_buf[..data.len()].copy_from_slice(data);
carry_len = data.len();
}
}
if output.len() < sample_count {
output.resize(sample_count, <$native>::default());
}
output
}
};
}
pub(crate) fn read_i8_chunks<T: AsRef<[u8]>>(chunks: &[T], sample_count: usize) -> Vec<i8> {
let mut output = Vec::with_capacity(sample_count);
for chunk in chunks {
if output.len() >= sample_count {
break;
}
let data = chunk.as_ref();
let remaining = sample_count - output.len();
let count = remaining.min(data.len());
let cast_data: &[i8] =
unsafe { std::slice::from_raw_parts(data.as_ptr() as *const i8, count) };
output.extend_from_slice(cast_data);
}
if output.len() < sample_count {
output.resize(sample_count, 0);
}
output
}
impl_read_chunks!(read_i16_chunks, i16, I16<LE>, 2);
impl_read_chunks!(read_i32_chunks, i32, I32<LE>, 4);
impl_read_chunks!(read_f32_chunks, f32, F32<LE>, 4);
impl_read_chunks!(read_f64_chunks, f64, F64<LE>, 8);
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
#[test]
fn read_f32_chunks_handles_unaligned_input() {
let mut raw = [0u8; 17];
let values = [0.0f32, 1.0, 2.0, 3.0];
let bytes: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
raw[1..17].copy_from_slice(&bytes);
let chunks = vec![&raw[1..]];
let output = read_f32_chunks(&chunks, 4);
assert_eq!(output, values);
}
}