use core::num::NonZeroU64;
use super::dense_key_index::{CommitOutcome, PrepareInsertOutcome};
use super::inconsistency::invariant_violation;
use super::{WarpId, WarpTable, WarpTableInconsistency, WarpTableRow};
#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertOutcome<Id, Row> {
Inserted { id: Id, row: usize },
Full { rejected: Row },
IdentifiersExhausted { rejected: Row },
}
#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertWithIdOutcome<Id, Row> {
Inserted {
id: Id,
row: usize,
},
AlreadyPresent {
id: Id,
existing_row: usize,
rejected: Row,
},
Full {
id: Id,
rejected: Row,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertAllOutcome {
Completed,
}
impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
pub fn insert_with_id(
&mut self,
id: Row::Id,
row: Row,
) -> WarpTableInsertWithIdOutcome<Row::Id, Row> {
let ids = &self.ids;
if ids.len() != self.len {
invariant_violation(WarpTableInconsistency::IdColumnLengthMismatch {
ids: ids.len(),
rows: self.len,
});
}
let vacancy = match self.primary_index.prepare_insert(id, ids) {
Ok(PrepareInsertOutcome::Vacant(vacancy)) => vacancy,
Ok(PrepareInsertOutcome::Present { row: existing_row }) => {
return WarpTableInsertWithIdOutcome::AlreadyPresent {
id,
existing_row,
rejected: row,
};
}
Err(reason) => invariant_violation(WarpTableInconsistency::from(reason)),
};
if self.len >= self.maximum_rows {
return WarpTableInsertWithIdOutcome::Full { id, rejected: row };
}
let warp_table_row = self.len;
let primary_insert = self
.primary_index
.prepare_commit(vacancy, warp_table_row)
.unwrap_or_else(|reason| invariant_violation(WarpTableInconsistency::from(reason)));
let indexes_insert = Row::try_prepare_indexes_insert(&mut self.indexes, &row)
.unwrap_or_else(|reason| invariant_violation(reason));
let CommitOutcome::Committed {
row: warp_table_row,
} = self.primary_index.commit_prepared(primary_insert);
self.ids.push(id);
Row::commit_indexes_insert(indexes_insert);
Row::append(&mut self.columns, row);
self.len = self.len.saturating_add(1);
WarpTableInsertWithIdOutcome::Inserted {
id,
row: warp_table_row,
}
}
pub fn insert_all_with_id(
&mut self,
rows: impl IntoIterator<Item = (Row::Id, Row)>,
mut observe: impl FnMut(WarpTableInsertWithIdOutcome<Row::Id, Row>),
) -> WarpTableInsertAllOutcome {
for (id, row) in rows {
observe(self.insert_with_id(id, row));
}
WarpTableInsertAllOutcome::Completed
}
}
impl<Row, Brand> WarpTable<Row, Brand>
where
Row: WarpTableRow<Id = WarpId<Row>>,
{
pub fn insert(&mut self, mut row: Row) -> WarpTableInsertOutcome<WarpId<Row>, Row> {
if self.ids.len() != self.len {
invariant_violation(WarpTableInconsistency::IdColumnLengthMismatch {
ids: self.ids.len(),
rows: self.len,
});
}
if self.len >= self.maximum_rows {
return WarpTableInsertOutcome::Full { rejected: row };
}
let attempts = self.len.saturating_add(1);
for _ in 0..attempts {
let Some(sequence) = self.next_id else {
return WarpTableInsertOutcome::IdentifiersExhausted { rejected: row };
};
let id = WarpId::new(sequence);
match self.insert_with_id(id, row) {
WarpTableInsertWithIdOutcome::Inserted {
row: warp_table_row,
..
} => {
self.next_id = following_sequence(sequence);
return WarpTableInsertOutcome::Inserted {
id,
row: warp_table_row,
};
}
WarpTableInsertWithIdOutcome::AlreadyPresent { rejected, .. } => {
self.next_id = following_sequence(sequence);
row = rejected;
}
WarpTableInsertWithIdOutcome::Full { .. } => invariant_violation(
WarpTableInconsistency::CapacityReachedDuringGeneratedInsert,
),
}
}
invariant_violation(WarpTableInconsistency::IdentifierSequenceBlocked { attempts })
}
pub fn insert_all(
&mut self,
rows: impl IntoIterator<Item = Row>,
mut observe: impl FnMut(WarpTableInsertOutcome<WarpId<Row>, Row>),
) -> WarpTableInsertAllOutcome {
for row in rows {
observe(self.insert(row));
}
WarpTableInsertAllOutcome::Completed
}
}
#[inline(always)]
fn following_sequence(sequence: NonZeroU64) -> Option<NonZeroU64> {
sequence.get().checked_add(1).and_then(NonZeroU64::new)
}