use crate::bytes::le_u32;
use crate::error::UrnaError;
pub const INT8_PAYLOAD_VERSION: u32 = 1;
pub const INT8_SCALE_KIND_PER_VECTOR: u32 = 0;
pub const INT8_PREFIX_SIZE: usize = 8;
pub fn quantize_f32_to_i8(values: &[f32]) -> (f32, Vec<i8>) {
let max_abs = values.iter().fold(0.0f32, |acc, &v| acc.max(v.abs()));
if max_abs == 0.0 {
return (1.0, vec![0i8; values.len()]);
}
let scale = max_abs / 127.0;
let inv_scale = 1.0 / scale;
let q: Vec<i8> = values
.iter()
.map(|&v| {
let scaled = (v * inv_scale).round();
scaled.clamp(-127.0, 127.0) as i8
})
.collect();
(scale, q)
}
pub fn encode_int8_embeddings(embeddings: &[f32], n: usize, dim: usize) -> crate::Result<Vec<u8>> {
if embeddings.len() != n * dim {
return Err(UrnaError::InvalidInput(format!(
"encode_int8_embeddings: got {} f32 values for n={} dim={}",
embeddings.len(),
n,
dim
)));
}
let mut out = Vec::with_capacity(INT8_PREFIX_SIZE + n * 4 + n * dim);
out.extend_from_slice(&INT8_PAYLOAD_VERSION.to_le_bytes());
out.extend_from_slice(&INT8_SCALE_KIND_PER_VECTOR.to_le_bytes());
let mut scales: Vec<u8> = Vec::with_capacity(n * 4);
let mut bodies: Vec<u8> = Vec::with_capacity(n * dim);
for i in 0..n {
let row = &embeddings[i * dim..(i + 1) * dim];
let (scale, q) = quantize_f32_to_i8(row);
scales.extend_from_slice(&scale.to_le_bytes());
bodies.extend(q.iter().map(|&v| v as u8));
}
out.extend_from_slice(&scales);
out.extend_from_slice(&bodies);
Ok(out)
}
pub struct Int8EmbeddingsView<'a> {
pub scales: &'a [u8], pub bodies: &'a [u8], pub n: usize,
pub dim: usize,
}
impl<'a> Int8EmbeddingsView<'a> {
pub fn parse(bytes: &'a [u8], n: usize, dim: usize) -> crate::Result<Self> {
let want = super::expected_embeddings_size("int8", n, dim).unwrap_or(usize::MAX);
if bytes.len() != want {
return Err(UrnaError::EmbeddingSizeMismatch {
expected: want,
got: bytes.len(),
});
}
let version = le_u32(&bytes[0..4])?;
if version != INT8_PAYLOAD_VERSION {
return Err(UrnaError::UnsupportedSectionVersion {
section_id: crate::layout::SECTION_EMBEDDINGS,
version,
});
}
let kind = le_u32(&bytes[4..8])?;
if kind != INT8_SCALE_KIND_PER_VECTOR {
return Err(UrnaError::MalformedSectionPayload {
section_id: crate::layout::SECTION_EMBEDDINGS,
reason: format!("int8 scale_kind {} not supported", kind),
});
}
let scales_end = INT8_PREFIX_SIZE + n * 4;
Ok(Self {
scales: &bytes[INT8_PREFIX_SIZE..scales_end],
bodies: &bytes[scales_end..],
n,
dim,
})
}
#[inline]
pub fn scale(&self, i: usize) -> f32 {
let off = i * 4;
f32::from_le_bytes([
self.scales[off],
self.scales[off + 1],
self.scales[off + 2],
self.scales[off + 3],
])
}
#[inline]
pub fn row(&self, i: usize) -> &'a [i8] {
let start = i * self.dim;
let end = start + self.dim;
bytemuck::cast_slice(&self.bodies[start..end])
}
}