#[cfg(any(feature = "alloc", kani))]
use alloc::vec::Vec;
use core::{error::Error, fmt};
use crate::LayoutIndex;
#[cfg(any(feature = "alloc", kani))]
use crate::SnapshotWidth;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum IdOutOfBounds {
UsizeOverflow,
OutOfRange {
slot: usize,
count: usize,
},
}
impl fmt::Display for IdOutOfBounds {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UsizeOverflow => formatter.write_str("ID value did not fit in usize"),
Self::OutOfRange { slot, count } => write!(
formatter,
"ID slot {slot} is not less than the dense bound {count}"
),
}
}
}
impl Error for IdOutOfBounds {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum OffsetOverflow {
IndexOverflow {
value: usize,
},
}
impl fmt::Display for OffsetOverflow {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IndexOverflow { value } => {
write!(
formatter,
"value {value} does not fit in the target index width"
)
}
}
}
}
impl Error for OffsetOverflow {}
#[inline]
pub fn map_offset_overflow<E>(error: OffsetOverflow, on_overflow: impl FnOnce(usize) -> E) -> E {
match error {
OffsetOverflow::IndexOverflow { value } => on_overflow(value),
}
}
#[inline]
pub fn next_dense_index<Index: LayoutIndex, E>(
counter: &mut usize,
on_overflow: impl Fn(usize) -> E,
) -> Result<Index, E> {
let id = Index::from_usize(*counter).ok_or_else(|| on_overflow(*counter))?;
*counter = counter
.checked_add(1)
.ok_or_else(|| on_overflow(usize::MAX))?;
Ok(id)
}
#[inline]
pub fn id_to_slot<I: LayoutIndex>(id: I, count: usize) -> Result<usize, IdOutOfBounds> {
let slot = id.to_usize().ok_or(IdOutOfBounds::UsizeOverflow)?;
if slot < count {
Ok(slot)
} else {
Err(IdOutOfBounds::OutOfRange { slot, count })
}
}
#[inline]
#[must_use]
pub fn slot_or_max<I: LayoutIndex>(id: I) -> usize {
id.to_usize().unwrap_or(usize::MAX)
}
#[inline]
pub fn index_from_usize<O: LayoutIndex>(value: usize) -> Result<O, OffsetOverflow> {
O::from_usize(value).ok_or(OffsetOverflow::IndexOverflow { value })
}
#[cfg(any(feature = "alloc", kani))]
#[must_use]
pub fn slice_to_le<W: SnapshotWidth>(values: &[W]) -> Vec<W::LittleEndianWord> {
values.iter().copied().map(W::to_le_word).collect()
}
#[cfg(any(feature = "alloc", kani))]
pub fn build_offset_index<O, T>(buckets: Vec<Vec<T>>) -> Result<(Vec<O>, Vec<T>), OffsetOverflow>
where
O: LayoutIndex,
{
let mut offsets = Vec::with_capacity(buckets.len() + 1);
let mut items: Vec<T> = Vec::new();
offsets.push(index_from_usize::<O>(0)?);
for bucket in buckets {
items.extend(bucket);
offsets.push(index_from_usize::<O>(items.len())?);
}
Ok((offsets, items))
}