mod unique;
pub use unique::{CallerUniqueLookup, CallerUniqueLookupError};
mod bulk;
pub use bulk::{CallerBulkUpdateError, CallerWarpTableBulkUpdate};
mod ranking;
pub use ranking::{CallerOrderingLevel, CallerRanking, CallerRankingError};
mod selection;
use core::num::NonZeroU64;
pub use selection::{
CallerMembershipLookup, CallerQuery, CallerRangeLookup, CallerSelection, CallerSelectionError,
CallerSelectionOperand, CallerWarpTableUpdate, bind_caller_selection,
};
use super::dense_key_index::{DenseKeyIndex, required_buckets};
use super::inconsistency::invariant_violation;
use super::{
Capacity, ColumnLease, WarpTable, WarpTableBacking, WarpTableInconsistency, WarpTableRow,
WarpTableRowGetError, WarpTableRowGetOutcome, WarpTableRowReplaceError,
WarpTableRowReplaceOutcome,
};
pub trait CallerWarpTableColumns<Row: WarpTableRow> {
type Indexes: super::WarpTableIndexes<Row>;
type Mut<'row>
where
Self: 'row,
Row: 'row;
fn validate_indexes(
indexes: &Self::Indexes,
maximum_rows: usize,
) -> Result<(), CallerStorageError>;
fn validate(&self, maximum_rows: usize) -> Result<(), CallerStorageError>;
fn columns_mut(&mut self) -> Row::ColumnsMut<'_>;
fn append(&mut self, row: Row);
fn swap_remove(&mut self, row: usize) -> Row;
fn get<'row>(
&'row self,
id: &'row Row::Id,
row: usize,
) -> Result<WarpTableRowGetOutcome<Row::Ref<'row>>, WarpTableRowGetError>
where
Row: 'row;
fn rows<'row>(
&'row self,
ids: &'row [Row::Id],
) -> Result<Row::Rows<'row>, WarpTableRowGetError>
where
Row: 'row;
fn fold_rows<'row, Acc, Fold>(
&'row self,
ids: &'row [Row::Id],
start: usize,
initial: Acc,
mut fold: Fold,
) -> Result<Acc, WarpTableRowGetError>
where
Row: 'row,
Fold: FnMut(Acc, Row::Ref<'row>) -> Acc,
{
ids.iter()
.enumerate()
.try_fold(initial, |acc, (offset, id)| {
let WarpTableRowGetOutcome::Present { row } =
self.get(id, start.saturating_add(offset))?;
Ok(fold(acc, row))
})
}
fn get_mut<'row>(
&'row mut self,
id: &'row Row::Id,
indexes: &'row mut Self::Indexes,
row: usize,
) -> Result<WarpTableRowGetOutcome<Self::Mut<'row>>, WarpTableRowGetError>
where
Row: 'row,
Self: 'row;
fn replace(
&mut self,
row: usize,
replacement: Row,
) -> Result<WarpTableRowReplaceOutcome<Row>, WarpTableRowReplaceError<Row>>;
}
pub type CallerWarpTable<'storage, Row, Columns, Brand = Row> =
WarpTable<Row, Brand, CallerStorage<'storage, Row, Columns>>;
pub struct CallerStorage<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> {
pub ids: ColumnLease<'storage, Row::Id>,
pub columns: Columns,
pub indexes: Columns::Indexes,
pub primary_buckets: &'storage mut [u32],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CallerStorageError {
CapacityUnrepresentable {
capacity: Capacity,
},
PrimaryIndexCapacityOverflow {
maximum_rows: usize,
},
IdsNotEmpty {
rows: usize,
},
IdCapacityInsufficient {
required: usize,
available: usize,
},
ColumnNotEmpty {
column: &'static str,
rows: usize,
},
ColumnCapacityInsufficient {
column: &'static str,
required: usize,
available: usize,
},
MembershipLayout {
field: &'static str,
reason: crate::storage::membership::MembershipIndexLayoutError,
},
IndexNotEmpty {
field: &'static str,
},
IndexCapacityMismatch {
field: &'static str,
required: usize,
available: usize,
},
PrimaryIndexCapacityInsufficient {
required: usize,
available: usize,
},
}
pub struct CallerStorageRefusal<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> {
pub storage: CallerStorage<'storage, Row, Columns>,
pub reason: CallerStorageError,
}
impl Capacity {
pub fn primary_index_buckets(self) -> Result<usize, CallerStorageError> {
let maximum_rows = represent_capacity::<usize>(self)?;
primary_bucket_count(maximum_rows)
}
}
fn represent_capacity<Index: TryFrom<u32>>(
capacity: Capacity,
) -> Result<Index, CallerStorageError> {
Index::try_from(capacity.value().get())
.map_err(|_| CallerStorageError::CapacityUnrepresentable { capacity })
}
fn primary_bucket_count(maximum_rows: usize) -> Result<usize, CallerStorageError> {
required_buckets(maximum_rows)
.ok_or(CallerStorageError::PrimaryIndexCapacityOverflow { maximum_rows })
}
impl<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>, Brand>
WarpTable<Row, Brand, CallerStorage<'storage, Row, Columns>>
{
pub fn from_storage(
capacity: Capacity,
storage: CallerStorage<'storage, Row, Columns>,
) -> Result<
CallerWarpTable<'storage, Row, Columns, Brand>,
CallerStorageRefusal<'storage, Row, Columns>,
> {
let dimensions = (|| {
let maximum_rows = represent_capacity::<usize>(capacity)?;
let bucket_count = primary_bucket_count(maximum_rows)?;
if !storage.ids.is_empty() {
return Err(CallerStorageError::IdsNotEmpty {
rows: storage.ids.len(),
});
}
if storage.ids.capacity() < maximum_rows {
return Err(CallerStorageError::IdCapacityInsufficient {
required: maximum_rows,
available: storage.ids.capacity(),
});
}
storage.columns.validate(maximum_rows)?;
Columns::validate_indexes(&storage.indexes, maximum_rows)?;
if storage.primary_buckets.len() < bucket_count {
return Err(CallerStorageError::PrimaryIndexCapacityInsufficient {
required: bucket_count,
available: storage.primary_buckets.len(),
});
}
Ok(maximum_rows)
})();
let maximum_rows = match dimensions {
Ok(maximum_rows) => maximum_rows,
Err(reason) => return Err(CallerStorageRefusal { storage, reason }),
};
let CallerStorage {
ids,
columns,
indexes,
primary_buckets,
} = storage;
Ok(WarpTable {
capacity,
maximum_rows,
len: 0,
next_id: Some(NonZeroU64::MIN),
ids,
columns,
indexes,
primary_index: DenseKeyIndex::from_buckets(primary_buckets),
#[cfg(feature = "secondary-indexes")]
bulk_workspace: None,
#[cfg(feature = "secondary-indexes")]
query_storage: core::cell::OnceCell::new(),
#[cfg(feature = "secondary-indexes")]
mutable_snapshot: None,
brand: core::marker::PhantomData,
})
}
}
impl<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> WarpTableBacking<Row>
for CallerStorage<'storage, Row, Columns>
{
type Indexes = Columns::Indexes;
type Mut<'row>
= Columns::Mut<'row>
where
Self: 'row,
Row: 'row;
type Ids = ColumnLease<'storage, Row::Id>;
type Columns = Columns;
type Buckets = &'storage mut [u32];
fn ids(ids: &Self::Ids) -> &[Row::Id] {
ids.as_slice()
}
fn append_id(ids: &mut Self::Ids, id: Row::Id) {
if ids.push(id).is_err() {
invariant_violation(WarpTableInconsistency::IdStorageFull {
capacity: ids.capacity(),
});
}
}
fn remove_id(ids: &mut Self::Ids, row: usize) {
if ids.swap_remove(row).is_none() {
invariant_violation(WarpTableInconsistency::IdUnavailable { row });
}
}
fn columns_mut(columns: &mut Self::Columns) -> Row::ColumnsMut<'_> {
columns.columns_mut()
}
fn append(columns: &mut Self::Columns, row: Row) {
columns.append(row);
}
fn swap_remove(columns: &mut Self::Columns, row: usize) -> Row {
columns.swap_remove(row)
}
fn get<'row>(
id: &'row Row::Id,
columns: &'row Self::Columns,
row: usize,
) -> Result<WarpTableRowGetOutcome<Row::Ref<'row>>, WarpTableRowGetError>
where
Row: 'row,
{
columns.get(id, row)
}
fn rows<'row>(
ids: &'row [Row::Id],
columns: &'row Self::Columns,
) -> Result<Row::Rows<'row>, WarpTableRowGetError>
where
Row: 'row,
{
columns.rows(ids)
}
fn fold_rows<'row, Acc, Fold>(
ids: &'row [Row::Id],
columns: &'row Self::Columns,
start: usize,
initial: Acc,
fold: Fold,
) -> Result<Acc, WarpTableRowGetError>
where
Row: 'row,
Fold: FnMut(Acc, Row::Ref<'row>) -> Acc,
{
columns.fold_rows(ids, start, initial, fold)
}
fn get_mut<'row>(
id: &'row Row::Id,
columns: &'row mut Self::Columns,
indexes: &'row mut Self::Indexes,
row: usize,
) -> Result<WarpTableRowGetOutcome<Self::Mut<'row>>, WarpTableRowGetError>
where
Row: 'row,
Self: 'row,
{
columns.get_mut(id, indexes, row)
}
fn replace(
columns: &mut Self::Columns,
row: usize,
replacement: Row,
) -> Result<WarpTableRowReplaceOutcome<Row>, WarpTableRowReplaceError<Row>> {
columns.replace(row, replacement)
}
}
#[doc(hidden)]
pub fn validate_caller_column<T>(
column: &'static str,
storage: &ColumnLease<'_, T>,
maximum_rows: usize,
) -> Result<(), CallerStorageError> {
if !storage.is_empty() {
return Err(CallerStorageError::ColumnNotEmpty {
column,
rows: storage.len(),
});
}
if storage.capacity() < maximum_rows {
return Err(CallerStorageError::ColumnCapacityInsufficient {
column,
required: maximum_rows,
available: storage.capacity(),
});
}
Ok(())
}
#[cfg(test)]
mod tests;
#[cfg(feature = "alloc")]
impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
pub fn try_from_storage<'storage, Columns: CallerWarpTableColumns<Row>>(
capacity: Capacity,
storage: CallerStorage<'storage, Row, Columns>,
) -> Result<
CallerWarpTable<'storage, Row, Columns, Brand>,
CallerStorageRefusal<'storage, Row, Columns>,
> {
CallerWarpTable::from_storage(capacity, storage)
}
}
#[doc(hidden)]
pub fn validate_caller_index(
field: &'static str,
required: usize,
available: usize,
empty: bool,
) -> Result<(), CallerStorageError> {
if !empty {
return Err(CallerStorageError::IndexNotEmpty { field });
}
if available != required {
return Err(CallerStorageError::IndexCapacityMismatch {
field,
required,
available,
});
}
Ok(())
}