weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
use super::format::{HEADER_LEN, MAGIC, to_u64, to_usize};
use crate::error::SearchError;

pub(super) struct Encoder {
    pub(super) data: Vec<u8>,
}

impl Encoder {
    pub(super) fn with_header() -> Result<Self, SearchError> {
        let mut data = Vec::new();
        data.try_reserve_exact(HEADER_LEN)
            .map_err(|_| SearchError::AllocationFailed)?;
        data.extend_from_slice(MAGIC);
        data.resize(HEADER_LEN, 0);
        Ok(Self { data })
    }

    fn reserve(&mut self, additional: usize) -> Result<(), SearchError> {
        self.data
            .try_reserve(additional)
            .map_err(|_| SearchError::AllocationFailed)
    }

    pub(super) fn bytes(&mut self, value: &[u8]) -> Result<(), SearchError> {
        self.reserve(value.len())?;
        self.data.extend_from_slice(value);
        Ok(())
    }

    pub(super) fn u8(&mut self, value: u8) -> Result<(), SearchError> {
        self.reserve(1)?;
        self.data.push(value);
        Ok(())
    }

    pub(super) fn u32(&mut self, value: u32) -> Result<(), SearchError> {
        self.bytes(&value.to_le_bytes())
    }

    pub(super) fn u64(&mut self, value: u64) -> Result<(), SearchError> {
        self.bytes(&value.to_le_bytes())
    }

    pub(super) fn f32s(&mut self, values: &[f32]) -> Result<(), SearchError> {
        self.reserve(
            values
                .len()
                .checked_mul(std::mem::size_of::<f32>())
                .ok_or(SearchError::CapacityOverflow)?,
        )?;
        for value in values {
            self.data.extend_from_slice(&value.to_bits().to_le_bytes());
        }
        Ok(())
    }

    pub(super) fn length_prefixed(&mut self, value: &[u8]) -> Result<(), SearchError> {
        self.u64(to_u64(value.len())?)?;
        self.bytes(value)
    }
}

pub(super) struct Decoder<'a> {
    bytes: &'a [u8],
    cursor: usize,
}

impl<'a> Decoder<'a> {
    pub(super) const fn new(bytes: &'a [u8], cursor: usize) -> Self {
        Self { bytes, cursor }
    }

    pub(super) fn take(&mut self, length: usize) -> Result<&'a [u8], SearchError> {
        let end = self
            .cursor
            .checked_add(length)
            .ok_or(SearchError::CapacityOverflow)?;
        let value = self
            .bytes
            .get(self.cursor..end)
            .ok_or(SearchError::CorruptSnapshot("bundle payload is truncated"))?;
        self.cursor = end;
        Ok(value)
    }

    pub(super) fn u8(&mut self) -> Result<u8, SearchError> {
        Ok(self.take(1)?[0])
    }

    pub(super) fn u32(&mut self) -> Result<u32, SearchError> {
        Ok(u32::from_le_bytes(
            self.take(4)?
                .try_into()
                .expect("checked four-byte bundle value"),
        ))
    }

    pub(super) fn u64(&mut self) -> Result<u64, SearchError> {
        Ok(u64::from_le_bytes(
            self.take(8)?
                .try_into()
                .expect("checked eight-byte bundle value"),
        ))
    }

    pub(super) fn f32s(&mut self, length: usize) -> Result<Vec<f32>, SearchError> {
        let byte_length = length
            .checked_mul(std::mem::size_of::<f32>())
            .ok_or(SearchError::CapacityOverflow)?;
        let bytes = self.take(byte_length)?;
        let mut values = Vec::new();
        values
            .try_reserve_exact(length)
            .map_err(|_| SearchError::AllocationFailed)?;
        values.extend(bytes.chunks_exact(4).map(|chunk| {
            f32::from_bits(u32::from_le_bytes(
                chunk.try_into().expect("four-byte f32 chunk"),
            ))
        }));
        Ok(values)
    }

    pub(super) fn length_prefixed(&mut self) -> Result<&'a [u8], SearchError> {
        let length = to_usize(self.u64()?)?;
        self.take(length)
    }

    pub(super) const fn is_finished(&self) -> bool {
        self.cursor == self.bytes.len()
    }
}