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)
}