AkurAI Build
Menu

Public repository / fee5b6c2cde89fa6bf8dc51068af7336b56a88b8

AkurAI-Framework / crates/vector/src/codec.rs

Latest change 35c1784a2189dce748f85504e491110cffe15ebf - OLI-13: clippy 1.98 fixes (as_chunks, mem::take) by Ólafur Búi Ólafsson

//! Little-endian `f32` byte (de)serialization.
//!
//! Embeddings are stored as opaque byte blobs in the B+tree by the CLI. We use
//! a fixed little-endian `f32` layout so a vector survives a store/load round
//! trip exactly, on any little-endian or big-endian host (we convert
//! explicitly, never `transmute`).

/// Encode a vector as a little-endian `f32` byte blob (4 bytes per element).
pub fn encode(v: &[f32]) -> Vec<u8> {
    let mut out = Vec::with_capacity(v.len() * 4);
    for &x in v {
        out.extend_from_slice(&x.to_le_bytes());
    }
    out
}

/// Decode a little-endian `f32` byte blob back into a vector.
///
/// Returns `None` if the byte length isn't a multiple of 4 (i.e. the blob is
/// not a whole number of `f32`s), so a corrupt/short blob can never panic the
/// search path.
pub fn decode(bytes: &[u8]) -> Option<Vec<f32>> {
    if !bytes.len().is_multiple_of(4) {
        return None;
    }
    let mut out = Vec::with_capacity(bytes.len() / 4);
    for chunk in bytes.as_chunks::<4>().0 {
        let arr = [chunk[0], chunk[1], chunk[2], chunk[3]];
        out.push(f32::from_le_bytes(arr));
    }
    Some(out)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn round_trip_preserves_values() {
        let v = vec![0.0, 1.0, -1.5, 42.125, 1e30, -1e-30, f32::MAX, f32::MIN];
        let bytes = encode(&v);
        assert_eq!(bytes.len(), v.len() * 4);
        assert_eq!(decode(&bytes), Some(v));
    }

    #[test]
    fn round_trip_empty() {
        let v: Vec<f32> = Vec::new();
        let bytes = encode(&v);
        assert!(bytes.is_empty());
        assert_eq!(decode(&bytes), Some(v));
    }

    #[test]
    fn decode_rejects_non_multiple_of_four() {
        assert_eq!(decode(&[0, 1, 2]), None);
        assert_eq!(decode(&[0, 1, 2, 3, 4]), None);
    }

    #[test]
    fn known_byte_layout_is_little_endian() {
        // 1.0f32 == 0x3F800000, little-endian on the wire.
        assert_eq!(encode(&[1.0]), vec![0x00, 0x00, 0x80, 0x3F]);
        assert_eq!(decode(&[0x00, 0x00, 0x80, 0x3F]), Some(vec![1.0]));
    }

    #[test]
    fn handles_negative_zero_and_subnormal() {
        let v = vec![-0.0f32, f32::MIN_POSITIVE / 2.0];
        let back = decode(&encode(&v)).unwrap();
        assert_eq!(back.len(), 2);
        // -0.0 == 0.0 compares true, so check the sign bit survived exactly.
        assert!(back[0].is_sign_negative());
        assert_eq!(back[1], v[1]);
    }
}