use kopitiam_core::{DType, Error, Result, Shape};
#[derive(Debug, Clone)]
pub enum Storage {
F32(Vec<f32>),
F16(Vec<u16>),
BF16(Vec<u16>),
I8(Vec<i8>),
I32(Vec<i32>),
Quantized { dtype: DType, bytes: Vec<u8> },
}
impl Storage {
pub fn dtype(&self) -> DType {
match self {
Self::F32(_) => DType::F32,
Self::F16(_) => DType::F16,
Self::BF16(_) => DType::BF16,
Self::I8(_) => DType::I8,
Self::I32(_) => DType::I32,
Self::Quantized { dtype, .. } => *dtype,
}
}
pub fn len(&self) -> usize {
match self {
Self::F32(v) => v.len(),
Self::F16(v) | Self::BF16(v) => v.len(),
Self::I8(v) => v.len(),
Self::I32(v) => v.len(),
Self::Quantized { dtype, bytes } => bytes.len() / dtype.block_bytes() * dtype.block_size(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn new_quantized(dtype: DType, bytes: Vec<u8>, elem_count: usize) -> Result<Self> {
let expected = dtype
.storage_bytes(elem_count)
.ok_or(Error::PartialQuantizedBlock {
dtype,
count: elem_count,
block_size: dtype.block_size(),
})?;
if bytes.len() != expected {
return Err(Error::StorageTooSmall {
shape: Shape::new([elem_count]),
dtype,
expected,
actual: bytes.len(),
});
}
Ok(Self::Quantized { dtype, bytes })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dtype_and_len_match_the_variant() {
assert_eq!(Storage::F32(vec![1.0, 2.0, 3.0]).dtype(), DType::F32);
assert_eq!(Storage::F32(vec![1.0, 2.0, 3.0]).len(), 3);
assert_eq!(Storage::I32(vec![1, 2]).dtype(), DType::I32);
}
#[test]
fn quantized_storage_reports_decoded_element_count() {
let storage = Storage::new_quantized(DType::Q4_0, vec![0u8; 36], 64).unwrap();
assert_eq!(storage.len(), 64);
assert_eq!(storage.dtype(), DType::Q4_0);
}
#[test]
fn a_partial_block_element_count_is_rejected() {
let err = Storage::new_quantized(DType::Q4_0, vec![0u8; 18], 33).unwrap_err();
assert!(matches!(err, Error::PartialQuantizedBlock { count: 33, .. }));
}
#[test]
fn mismatched_byte_length_is_rejected() {
let err = Storage::new_quantized(DType::Q4_0, vec![0u8; 18], 64).unwrap_err();
assert!(matches!(
err,
Error::StorageTooSmall {
expected: 36,
actual: 18,
..
}
));
}
#[test]
fn zero_elements_is_a_valid_empty_quantized_tensor() {
let storage = Storage::new_quantized(DType::Q8_0, vec![], 0).unwrap();
assert!(storage.is_empty());
}
}