use crate::codec::codec_config::SUPPORTED_BIT_WIDTHS;
use crate::errors::CodecError;
use crate::types::ConfigHash;
use alloc::boxed::Box;
use alloc::sync::Arc;
#[derive(Clone, Debug)]
pub struct CompressedVector {
indices: Arc<[u8]>,
residual: Option<Arc<[u8]>>,
config_hash: ConfigHash,
dimension: u32,
bit_width: u8,
}
impl CompressedVector {
pub fn new(
indices: Box<[u8]>,
residual: Option<Box<[u8]>>,
config_hash: ConfigHash,
dimension: u32,
bit_width: u8,
) -> Result<Self, CodecError> {
if !SUPPORTED_BIT_WIDTHS.contains(&bit_width) {
return Err(CodecError::UnsupportedBitWidth { got: bit_width });
}
#[allow(clippy::cast_possible_truncation)]
let got_dim = indices.len() as u32;
if got_dim != dimension {
return Err(CodecError::DimensionMismatch {
expected: dimension,
got: got_dim,
});
}
if let Some(r) = residual.as_ref() {
let expected = indices.len() * 2;
if r.len() != expected {
return Err(CodecError::LengthMismatch {
left: r.len(),
right: expected,
});
}
}
Ok(Self {
indices: Arc::from(indices),
residual: residual.map(Arc::from),
config_hash,
dimension,
bit_width,
})
}
#[inline]
#[must_use]
pub fn indices(&self) -> &[u8] {
&self.indices
}
#[inline]
#[must_use]
pub fn residual(&self) -> Option<&[u8]> {
self.residual.as_deref()
}
#[inline]
#[must_use]
pub const fn config_hash(&self) -> &ConfigHash {
&self.config_hash
}
#[inline]
#[must_use]
pub const fn dimension(&self) -> u32 {
self.dimension
}
#[inline]
#[must_use]
pub const fn bit_width(&self) -> u8 {
self.bit_width
}
#[inline]
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn has_residual(&self) -> bool {
self.residual.is_some()
}
#[must_use]
pub fn size_bytes(&self) -> usize {
let dim = self.dimension as usize;
let bw = self.bit_width as usize;
let packed = (dim * bw + 7) / 8;
packed + self.residual.as_ref().map_or(0, |r| r.len())
}
}