weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
use super::{FNV_OFFSET, FNV_PRIME};
use crate::error::SearchError;

pub(super) fn checksum(bytes: &[u8]) -> u64 {
    update_checksum(FNV_OFFSET, bytes)
}

pub(super) fn update_checksum(mut hash: u64, bytes: &[u8]) -> u64 {
    for byte in bytes {
        hash ^= u64::from(*byte);
        hash = hash.wrapping_mul(FNV_PRIME);
    }
    hash
}

pub(super) fn bytes_for<T>(len: usize) -> Result<usize, SearchError> {
    len.checked_mul(std::mem::size_of::<T>())
        .ok_or(SearchError::CapacityOverflow)
}

pub(super) fn align8(value: usize) -> Result<usize, SearchError> {
    value
        .checked_add(7)
        .map(|value| value & !7)
        .ok_or(SearchError::CapacityOverflow)
}

pub(super) fn read_u32(bytes: &[u8], offset: usize) -> Result<u32, SearchError> {
    let end = offset.checked_add(4).ok_or(SearchError::CapacityOverflow)?;
    let slice = bytes
        .get(offset..end)
        .ok_or(SearchError::CorruptSnapshot("integer field is truncated"))?;
    Ok(u32::from_le_bytes(
        slice
            .try_into()
            .expect("four-byte range converts to u32 bytes"),
    ))
}

pub(super) fn read_u64(bytes: &[u8], offset: usize) -> Result<u64, SearchError> {
    let end = offset.checked_add(8).ok_or(SearchError::CapacityOverflow)?;
    let slice = bytes
        .get(offset..end)
        .ok_or(SearchError::CorruptSnapshot("integer field is truncated"))?;
    Ok(u64::from_le_bytes(
        slice
            .try_into()
            .expect("eight-byte range converts to u64 bytes"),
    ))
}

pub(super) fn put_u32(bytes: &mut [u8], offset: usize, value: u32) {
    bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
}

pub(super) fn put_u64(bytes: &mut [u8], offset: usize, value: u64) {
    bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
}

pub(super) fn to_usize(value: u64) -> Result<usize, SearchError> {
    usize::try_from(value).map_err(|_| SearchError::CapacityOverflow)
}

pub(super) fn to_u64(value: usize) -> Result<u64, SearchError> {
    u64::try_from(value).map_err(|_| SearchError::CapacityOverflow)
}

pub(super) fn decode_u16s(
    bytes: &[u8],
    offset: usize,
    count: usize,
) -> Result<Vec<u16>, SearchError> {
    decode_fixed(bytes, offset, count, 2, |chunk| {
        u16::from_le_bytes(chunk.try_into().expect("two-byte chunk"))
    })
}

pub(super) fn decode_u32s(
    bytes: &[u8],
    offset: usize,
    count: usize,
) -> Result<Vec<u32>, SearchError> {
    decode_fixed(bytes, offset, count, 4, |chunk| {
        u32::from_le_bytes(chunk.try_into().expect("four-byte chunk"))
    })
}

pub(super) fn decode_u64s(
    bytes: &[u8],
    offset: usize,
    count: usize,
) -> Result<Vec<u64>, SearchError> {
    decode_fixed(bytes, offset, count, 8, |chunk| {
        u64::from_le_bytes(chunk.try_into().expect("eight-byte chunk"))
    })
}

pub(super) fn decode_f32s(
    bytes: &[u8],
    offset: usize,
    count: usize,
) -> Result<Vec<f32>, SearchError> {
    decode_fixed(bytes, offset, count, 4, |chunk| {
        f32::from_bits(u32::from_le_bytes(
            chunk.try_into().expect("four-byte f32 chunk"),
        ))
    })
}

fn decode_fixed<T>(
    bytes: &[u8],
    offset: usize,
    count: usize,
    width: usize,
    decode: impl Fn(&[u8]) -> T,
) -> Result<Vec<T>, SearchError> {
    let length = count
        .checked_mul(width)
        .ok_or(SearchError::CapacityOverflow)?;
    let end = offset
        .checked_add(length)
        .ok_or(SearchError::CapacityOverflow)?;
    let source = bytes.get(offset..end).ok_or(SearchError::CorruptSnapshot(
        "snapshot section is truncated",
    ))?;
    let mut values = Vec::new();
    values
        .try_reserve_exact(count)
        .map_err(|_| SearchError::AllocationFailed)?;
    values.extend(source.chunks_exact(width).map(decode));
    Ok(values)
}