use alloc::collections::TryReserveError;
use core::num::{NonZeroU32, TryFromIntError};
use super::dense_key_index::{DenseKeyIndex, DenseKeyIndexCreationError};
use super::{WarpTable, WarpTableRow};
crate::value_wrapper! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Capacity(NonZeroU32);
}
#[derive(::core::fmt::Debug)]
pub enum WarpTableCreationError<ColumnsError, IndexesError> {
CapacityUnrepresentable {
capacity: Capacity,
source: TryFromIntError,
},
ColumnsUnavailable {
source: ColumnsError,
},
IndexesUnavailable {
source: IndexesError,
},
IdStorageUnavailable {
source: TryReserveError,
},
PrimaryIndexCapacityOverflow {
maximum_rows: usize,
},
PrimaryIndexStorageUnavailable {
source: TryReserveError,
},
}
impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
pub fn try_new(
capacity: Capacity,
) -> Result<Self, WarpTableCreationError<Row::ColumnsCreationError, Row::IndexesCreationError>>
{
let maximum_rows = represent_capacity(capacity)?;
let columns = Row::try_new_columns(maximum_rows)
.map_err(|source| WarpTableCreationError::ColumnsUnavailable { source })?;
let indexes = Row::try_new_indexes(maximum_rows)
.map_err(|source| WarpTableCreationError::IndexesUnavailable { source })?;
let mut ids = alloc::vec::Vec::new();
ids.try_reserve_exact(maximum_rows)
.map_err(|source| WarpTableCreationError::IdStorageUnavailable { source })?;
let primary_index =
DenseKeyIndex::try_new(maximum_rows).map_err(WarpTableCreationError::from)?;
Ok(Self {
capacity,
maximum_rows,
len: 0,
next_id: Some(core::num::NonZeroU64::MIN),
ids,
columns,
indexes,
primary_index,
brand: core::marker::PhantomData,
})
}
}
fn represent_capacity<Index, ColumnsError, IndexesError>(
capacity: Capacity,
) -> Result<Index, WarpTableCreationError<ColumnsError, IndexesError>>
where
Index: TryFrom<u32, Error = TryFromIntError>,
{
Index::try_from(capacity.value().get())
.map_err(|source| WarpTableCreationError::CapacityUnrepresentable { capacity, source })
}
impl<ColumnsError, IndexesError> From<DenseKeyIndexCreationError>
for WarpTableCreationError<ColumnsError, IndexesError>
{
fn from(reason: DenseKeyIndexCreationError) -> Self {
match reason {
DenseKeyIndexCreationError::CapacityOverflow { maximum_rows } => {
Self::PrimaryIndexCapacityOverflow { maximum_rows }
}
DenseKeyIndexCreationError::StorageUnavailable { source } => {
Self::PrimaryIndexStorageUnavailable { source }
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primary_creation_refusals_preserve_the_bound_or_allocator_cause() -> Result<(), &'static str>
{
use alloc::string::ToString;
let mapped: WarpTableCreationError<(), ()> =
DenseKeyIndexCreationError::CapacityOverflow { maximum_rows: 19 }.into();
assert!(matches!(
mapped,
WarpTableCreationError::PrimaryIndexCapacityOverflow { maximum_rows: 19 }
));
let source = alloc::vec::Vec::<u8>::new()
.try_reserve_exact(usize::MAX)
.err()
.ok_or("impossible allocation succeeded")?;
let message = source.to_string();
let mapped: WarpTableCreationError<(), ()> =
DenseKeyIndexCreationError::StorageUnavailable { source }.into();
let WarpTableCreationError::PrimaryIndexStorageUnavailable { source } = mapped else {
return Err("failure stage changed");
};
assert_eq!(source.to_string(), message);
Ok(())
}
}
#[test]
fn capacity_representation_retains_the_bound_on_narrow_targets() -> Result<(), &'static str> {
let maximum = Capacity::new(NonZeroU32::new(u32::from(u16::MAX)).ok_or("zero capacity")?);
assert!(matches!(
represent_capacity::<u16, (), ()>(maximum),
Ok(u16::MAX)
));
let outside = Capacity::new(
NonZeroU32::new(u32::from(u16::MAX).saturating_add(1)).ok_or("zero capacity")?,
);
assert!(
matches!(represent_capacity::<u16, (), ()>(outside), Err(WarpTableCreationError::CapacityUnrepresentable { capacity, .. }) if capacity == outside)
);
assert!(
matches!(represent_capacity::<usize, (), ()>(maximum), Ok(value) if value == usize::from(u16::MAX))
);
Ok(())
}