use core::num::NonZeroU64;
use super::inconsistency::invariant_violation;
use super::{
WarpId, WarpTable, WarpTableEntry, WarpTableInconsistency, WarpTableRow,
WarpTableVacantInsertOutcome,
};
#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertOutcome<Id, Row: WarpTableRow> {
Inserted {
id: Id,
row: usize,
},
Full {
rejected: Row,
},
IdentifiersExhausted {
rejected: Row,
},
UniqueConflict {
conflict: Row::UniqueConflict,
rejected: Row,
},
}
#[derive(Debug, Eq, PartialEq)]
#[must_use]
pub enum WarpTableInsertWithIdOutcome<Id, Row: WarpTableRow> {
Inserted {
id: Id,
row: usize,
},
AlreadyPresent {
id: Id,
existing_row: usize,
rejected: Row,
},
Full {
id: Id,
rejected: Row,
},
UniqueConflict {
id: Id,
conflict: Row::UniqueConflict,
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 vacancy = match self.entry(id) {
WarpTableEntry::Present {
dense_row: existing_row,
..
} => {
return WarpTableInsertWithIdOutcome::AlreadyPresent {
id,
existing_row,
rejected: row,
};
}
WarpTableEntry::Vacant { vacancy } => vacancy,
};
match vacancy.insert(row) {
WarpTableVacantInsertOutcome::Inserted { id, row } => {
WarpTableInsertWithIdOutcome::Inserted { id, row }
}
WarpTableVacantInsertOutcome::Full { id, rejected } => {
WarpTableInsertWithIdOutcome::Full { id, rejected }
}
WarpTableVacantInsertOutcome::UniqueConflict {
id,
conflict,
rejected,
} => WarpTableInsertWithIdOutcome::UniqueConflict {
id,
conflict,
rejected,
},
}
}
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, IdRow, Brand> WarpTable<Row, Brand>
where
Row: WarpTableRow<Id = WarpId<IdRow>>,
{
pub fn insert(&mut self, mut row: Row) -> WarpTableInsertOutcome<WarpId<IdRow>, 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::UniqueConflict {
conflict, rejected, ..
} => {
return WarpTableInsertOutcome::UniqueConflict { conflict, 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<IdRow>, Row>),
) -> WarpTableInsertAllOutcome {
for row in rows {
observe(self.insert(row));
}
WarpTableInsertAllOutcome::Completed
}
}
#[inline]
fn following_sequence(sequence: NonZeroU64) -> Option<NonZeroU64> {
sequence.get().checked_add(1).and_then(NonZeroU64::new)
}