use super::tensor::DType;
use crate::error::{Error, Result};
use std::sync::OnceLock;
pub(crate) fn expected_byte_len(dtype: DType, shape: &[usize]) -> Result<usize> {
let elements = shape.iter().try_fold(1_usize, |acc, &dim| {
acc.checked_mul(dim).ok_or_else(|| Error::InvalidConfig {
reason: format!("tensor shape {shape:?} overflows usize element counting"),
})
})?;
elements
.checked_mul(dtype.element_size())
.ok_or_else(|| Error::InvalidConfig {
reason: format!(
"tensor shape {shape:?} with dtype {dtype:?} overflows usize byte counting"
),
})
}
pub(crate) trait ToLeBytesArray<const N: usize> {
fn to_le_bytes_array(&self) -> [u8; N];
}
impl ToLeBytesArray<4> for f32 {
fn to_le_bytes_array(&self) -> [u8; 4] {
self.to_le_bytes()
}
}
impl ToLeBytesArray<8> for f64 {
fn to_le_bytes_array(&self) -> [u8; 8] {
self.to_le_bytes()
}
}
impl ToLeBytesArray<4> for i32 {
fn to_le_bytes_array(&self) -> [u8; 4] {
self.to_le_bytes()
}
}
impl ToLeBytesArray<8> for i64 {
fn to_le_bytes_array(&self) -> [u8; 8] {
self.to_le_bytes()
}
}
pub(crate) fn numeric_to_le_bytes<T: Copy + ToLeBytesArray<N>, const N: usize>(
data: &[T],
) -> Vec<u8> {
let mut bytes = Vec::with_capacity(data.len() * N);
for item in data {
bytes.extend_from_slice(&item.to_le_bytes_array());
}
bytes
}
pub(crate) fn cast_numeric_slice<'a, T, const N: usize>(
data: &'a [u8],
cache: &'a OnceLock<Vec<T>>,
decode: fn([u8; N]) -> T,
) -> Result<&'a [T]>
where
T: Copy,
{
let elem_size = std::mem::size_of::<T>();
if data.len() % elem_size != 0 {
return Err(Error::InvalidConfig {
reason: format!(
"data length {} is not a multiple of element size {}",
data.len(),
elem_size
),
});
}
Ok(cache
.get_or_init(|| {
data.chunks_exact(N)
.map(|chunk| {
let mut bytes = [0_u8; N];
bytes.copy_from_slice(chunk);
decode(bytes)
})
.collect()
})
.as_slice())
}