prolly-map 0.4.0

Content-addressed versioned map storage primitives.
Documentation
use super::codec::{put_f32, put_f64, put_varint, Reader, FORMAT_VERSION, VECTOR_ENCODING_F32_LE};
use crate::prolly::error::Error;

const MAGIC: &[u8; 4] = b"PRXV";
const HAS_NORM: u8 = 1;

#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ExternalVector {
    pub(crate) vector: Vec<f32>,
    pub(crate) norm: Option<f64>,
}

impl ExternalVector {
    pub(crate) fn encode(&self) -> Result<Vec<u8>, Error> {
        let mut bytes = Vec::new();
        bytes.extend_from_slice(MAGIC);
        bytes.push(FORMAT_VERSION);
        bytes.push(VECTOR_ENCODING_F32_LE);
        bytes.push(if self.norm.is_some() { HAS_NORM } else { 0 });
        put_varint(self.vector.len() as u64, &mut bytes);
        for &component in &self.vector {
            put_f32(component, &mut bytes)?;
        }
        if let Some(norm) = self.norm {
            if norm < 0.0 {
                return Err(Error::InvalidProximityObject {
                    kind: "vector",
                    reason: "norm must be non-negative".to_owned(),
                });
            }
            put_f64(norm, &mut bytes)?;
        }
        Ok(bytes)
    }

    pub(crate) fn decode(bytes: &[u8]) -> Result<Self, Error> {
        let mut reader = Reader::new(bytes, "vector");
        reader.exact(MAGIC)?;
        reader.version()?;
        if reader.u8()? != VECTOR_ENCODING_F32_LE {
            return Err(reader.invalid("unsupported vector encoding"));
        }
        let flags = reader.u8()?;
        if flags & !HAS_NORM != 0 {
            return Err(reader.invalid("unknown flags"));
        }
        let dimensions = reader.bounded_usize(super::codec::MAX_OBJECT_ENTRIES)?;
        if dimensions
            .checked_mul(4)
            .map_or(true, |len| len > reader.remaining())
        {
            return Err(reader.invalid("impossible vector length"));
        }
        let mut vector = Vec::with_capacity(dimensions);
        for _ in 0..dimensions {
            vector.push(reader.f32()?);
        }
        let norm = if flags & HAS_NORM != 0 {
            let norm = reader.f64()?;
            if norm < 0.0 {
                return Err(reader.invalid("norm must be non-negative"));
            }
            Some(norm)
        } else {
            None
        };
        reader.finish()?;
        Ok(Self { vector, norm })
    }
}