use core::iter::FusedIterator;
use super::selection::SelectionMembership;
use crate::storage::warp_table::inconsistency::invariant_violation;
use crate::storage::warp_table::secondary_index::{
SlotSetIntoSlots, SlotSetSlots, StableSlot, StableSlotResolveOutcome, StableSlotsInconsistency,
};
use crate::storage::warp_table::{
WarpTable, WarpTableGetAtOutcome, WarpTableInconsistency, WarpTableRow,
};
pub use crate::storage::warp_table::secondary_index::StableSlotResolver;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SelectionMembershipInconsistency {
RowUnrepresentable { row: usize },
NewMembershipUnrepresentable { membership: usize },
MembershipUnrepresentable,
StoredRowUnrepresentable { stored_row: u32 },
MembershipUnavailable,
MembershipOccupied { row: u32 },
RowUnavailable { row: usize },
MembershipVacant,
RowMismatch { expected: u32, actual: u32 },
DuplicateMembership,
}
#[must_use]
pub struct SelectionRows<'table, Row: WarpTableRow, Brand = Row> {
table: &'table WarpTable<Row, Brand>,
slots: SelectionSlotIter<'table>,
}
impl<'table, Row: WarpTableRow, Brand> SelectionRows<'table, Row, Brand> {
pub(super) fn new(
table: &'table WarpTable<Row, Brand>,
membership: SelectionMembership<'table>,
) -> Self {
Self {
table,
slots: membership.into(),
}
}
}
impl<'table, Row, Brand> Iterator for SelectionRows<'table, Row, Brand>
where
Row: WarpTableRow,
Row::Indexes: StableSlotResolver,
{
type Item = Row::Ref<'table>;
fn next(&mut self) -> Option<Self::Item> {
let slot = self.slots.next()?;
Some(resolve(self.table, slot))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.slots.size_hint()
}
}
impl<'table, Row, Brand> DoubleEndedIterator for SelectionRows<'table, Row, Brand>
where
Row: WarpTableRow,
Row::Indexes: StableSlotResolver,
{
fn next_back(&mut self) -> Option<Self::Item> {
let slot = self.slots.next_back()?;
Some(resolve(self.table, slot))
}
}
#[cfg(target_pointer_width = "64")]
impl<Row, Brand> ExactSizeIterator for SelectionRows<'_, Row, Brand>
where
Row: WarpTableRow,
Row::Indexes: StableSlotResolver,
{
}
impl<Row, Brand> FusedIterator for SelectionRows<'_, Row, Brand>
where
Row: WarpTableRow,
Row::Indexes: StableSlotResolver,
{
}
pub(super) fn resolve<'table, Row, Brand>(
table: &'table WarpTable<Row, Brand>,
slot: StableSlot,
) -> Row::Ref<'table>
where
Row: WarpTableRow,
Row::Indexes: StableSlotResolver,
{
let StableSlotResolveOutcome::Resolved { dense_row, .. } = table
.__indexes()
.resolve_stable_slot(slot)
.unwrap_or_else(|reason| invariant_violation(reason));
match table.get_at(dense_row) {
WarpTableGetAtOutcome::Present { row, .. } => row,
WarpTableGetAtOutcome::Absent { .. } => {
invariant_violation(WarpTableInconsistency::DenseRowUnavailable {
row: dense_row,
rows: table.len(),
})
}
}
}
impl From<StableSlotsInconsistency> for SelectionMembershipInconsistency {
fn from(reason: StableSlotsInconsistency) -> Self {
match reason {
StableSlotsInconsistency::RowUnrepresentable { row } => {
Self::RowUnrepresentable { row }
}
StableSlotsInconsistency::NewSlotUnrepresentable { slot } => {
Self::NewMembershipUnrepresentable { membership: slot }
}
StableSlotsInconsistency::SlotUnrepresentable { .. } => Self::MembershipUnrepresentable,
StableSlotsInconsistency::StoredRowUnrepresentable { stored_row, .. } => {
Self::StoredRowUnrepresentable { stored_row }
}
StableSlotsInconsistency::SlotUnavailable { .. } => Self::MembershipUnavailable,
StableSlotsInconsistency::SlotOccupied { row, .. } => Self::MembershipOccupied { row },
StableSlotsInconsistency::RowUnavailable { row } => Self::RowUnavailable { row },
StableSlotsInconsistency::SlotVacant { .. } => Self::MembershipVacant,
StableSlotsInconsistency::SlotRowMismatch {
expected, actual, ..
} => Self::RowMismatch { expected, actual },
StableSlotsInconsistency::DuplicateSlot { .. } => Self::DuplicateMembership,
}
}
}
enum SelectionSlotIter<'table> {
Empty,
Borrowed(SlotSetSlots<'table>),
Owned(SlotSetIntoSlots),
}
impl<'table> From<SelectionMembership<'table>> for SelectionSlotIter<'table> {
fn from(membership: SelectionMembership<'table>) -> Self {
match membership {
SelectionMembership::Empty => Self::Empty,
SelectionMembership::Borrowed(slots) => Self::Borrowed(slots.slots()),
SelectionMembership::Owned(slots) => Self::Owned(slots.into_slots()),
}
}
}
impl Iterator for SelectionSlotIter<'_> {
type Item = StableSlot;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Empty => None,
Self::Borrowed(slots) => slots.next(),
Self::Owned(slots) => slots.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::Empty => (0, Some(0)),
Self::Borrowed(slots) => slots.size_hint(),
Self::Owned(slots) => slots.size_hint(),
}
}
}
impl DoubleEndedIterator for SelectionSlotIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
Self::Empty => None,
Self::Borrowed(slots) => slots.next_back(),
Self::Owned(slots) => slots.next_back(),
}
}
}
impl FusedIterator for SelectionSlotIter<'_> {}
#[cfg(test)]
mod tests {
use core::{convert::Infallible, num::NonZeroU32};
extern crate std;
use self::std::panic::{AssertUnwindSafe, catch_unwind};
use super::*;
use crate::storage::warp_table::{
Capacity, WarpTableRowGetError, WarpTableRowGetOutcome, WarpTableRowReplaceError,
WarpTableRowReplaceOutcome,
};
const SLOT: StableSlot = StableSlot::new(7);
#[test]
fn stable_slot_failures_translate_without_exposing_slots() {
let translations = [
SelectionMembershipInconsistency::from(StableSlotsInconsistency::RowUnrepresentable {
row: 3,
}),
SelectionMembershipInconsistency::from(
StableSlotsInconsistency::NewSlotUnrepresentable { slot: 4 },
),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::SlotUnrepresentable {
slot: SLOT,
}),
SelectionMembershipInconsistency::from(
StableSlotsInconsistency::StoredRowUnrepresentable {
slot: SLOT,
stored_row: 5,
},
),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::SlotUnavailable {
slot: SLOT,
}),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::SlotOccupied {
slot: SLOT,
row: 6,
}),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::RowUnavailable {
row: 7,
}),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::SlotVacant {
slot: SLOT,
}),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::SlotRowMismatch {
slot: SLOT,
expected: 8,
actual: 9,
}),
SelectionMembershipInconsistency::from(StableSlotsInconsistency::DuplicateSlot {
slot: SLOT,
}),
];
assert_eq!(
translations,
[
SelectionMembershipInconsistency::RowUnrepresentable { row: 3 },
SelectionMembershipInconsistency::NewMembershipUnrepresentable { membership: 4 },
SelectionMembershipInconsistency::MembershipUnrepresentable,
SelectionMembershipInconsistency::StoredRowUnrepresentable { stored_row: 5 },
SelectionMembershipInconsistency::MembershipUnavailable,
SelectionMembershipInconsistency::MembershipOccupied { row: 6 },
SelectionMembershipInconsistency::RowUnavailable { row: 7 },
SelectionMembershipInconsistency::MembershipVacant,
SelectionMembershipInconsistency::RowMismatch {
expected: 8,
actual: 9,
},
SelectionMembershipInconsistency::DuplicateMembership,
]
);
}
#[test]
fn selection_rows_fail_fast_for_membership_resolution_failures() -> Result<(), &'static str> {
let table = WarpTable::<FailingRow>::try_new(Capacity::new(NonZeroU32::MIN))
.map_err(|_| "table storage was unavailable")?;
assert!(catch_unwind(AssertUnwindSafe(|| resolve(&table, SLOT))).is_err());
assert_eq!(FailingRow::query(&table), ());
let mut indexes = FailingIndexes;
FailingRow::try_prepare_indexes_insert(&mut indexes, &FailingRow)
.unwrap_or_else(|never| match never {});
FailingRow::commit_indexes_insert(());
FailingRow::try_prepare_indexes_remove(&mut indexes, (), 0)
.unwrap_or_else(|never| match never {});
FailingRow::commit_indexes_remove(());
FailingRow::try_prepare_indexes_replace(&mut indexes, (), &FailingRow, 0)
.unwrap_or_else(|never| match never {});
FailingRow::commit_indexes_replace(());
let mut columns = ();
FailingRow::append(&mut columns, FailingRow);
assert_eq!(FailingRow::swap_remove(&mut columns, 0), FailingRow);
assert_eq!(
FailingRow::get(&0, &columns, 0),
Ok(WarpTableRowGetOutcome::Present { row: () })
);
assert_eq!(
FailingRow::replace(&mut columns, 0, FailingRow),
Ok(WarpTableRowReplaceOutcome::Replaced {
previous: FailingRow,
})
);
Ok(())
}
#[derive(Debug, Eq, PartialEq)]
struct FailingRow;
struct FailingIndexes;
impl StableSlotResolver for FailingIndexes {
fn resolve_stable_slot(
&self,
_: StableSlot,
) -> Result<StableSlotResolveOutcome, StableSlotsInconsistency> {
Err(StableSlotsInconsistency::RowUnavailable { row: 11 })
}
}
impl WarpTableRow for FailingRow {
type Id = u8;
type Columns = ();
type ColumnsCreationError = Infallible;
type Indexes = FailingIndexes;
type IndexesCreationError = Infallible;
type UniqueConflict = Infallible;
type IndexesInsertError = Infallible;
type PreparedIndexesInsert<'indexes> = ();
type IndexesRemoveError = Infallible;
type PreparedIndexesRemove<'indexes> = ();
type IndexesReplaceError = Infallible;
type PreparedIndexesReplace<'indexes> = ();
type Ref<'row> = ();
type Mut<'row> = ();
type Query<'table, Brand: 'table> = ();
type MutQuery<'table, Brand: 'table> = ();
type DetachedQuery<'indexes> = ();
fn try_new_columns(_: usize) -> Result<Self::Columns, Self::ColumnsCreationError> {
Ok(())
}
fn try_new_indexes(_: usize) -> Result<Self::Indexes, Self::IndexesCreationError> {
Ok(FailingIndexes)
}
fn query<'table, Brand>(_: &'table WarpTable<Self, Brand>) -> Self::Query<'table, Brand>
where
Self: 'table,
Brand: 'table,
{
}
fn query_mut<'table, Brand>(
_: &'table mut WarpTable<Self, Brand>,
) -> Self::MutQuery<'table, Brand>
where
Self: 'table,
Brand: 'table,
{
}
fn detached_query<'indexes>(_: &'indexes Self::Indexes) -> Self::DetachedQuery<'indexes>
where
Self: 'indexes,
{
}
fn unique_insert_conflict(_: &Self::Indexes, _: &Self) -> Option<Self::UniqueConflict> {
None
}
fn unique_replace_conflict<'row>(
_: &Self::Indexes,
_: Self::Ref<'row>,
_: &Self,
) -> Option<Self::UniqueConflict>
where
Self: 'row,
{
None
}
fn try_prepare_indexes_insert<'indexes>(
_: &'indexes mut Self::Indexes,
_: &Self,
) -> Result<Self::PreparedIndexesInsert<'indexes>, Self::IndexesInsertError>
where
Self: 'indexes,
{
Ok(())
}
fn commit_indexes_insert<'indexes>(_: Self::PreparedIndexesInsert<'indexes>)
where
Self: 'indexes,
{
}
fn try_prepare_indexes_remove<'indexes, 'row>(
_: &'indexes mut Self::Indexes,
_: Self::Ref<'row>,
_: usize,
) -> Result<Self::PreparedIndexesRemove<'indexes>, Self::IndexesRemoveError>
where
Self: 'indexes + 'row,
{
Ok(())
}
fn commit_indexes_remove<'indexes>(_: Self::PreparedIndexesRemove<'indexes>)
where
Self: 'indexes,
{
}
fn try_prepare_indexes_replace<'indexes, 'row>(
_: &'indexes mut Self::Indexes,
_: Self::Ref<'row>,
_: &Self,
_: usize,
) -> Result<Self::PreparedIndexesReplace<'indexes>, Self::IndexesReplaceError>
where
Self: 'indexes + 'row,
{
Ok(())
}
fn commit_indexes_replace<'indexes>(_: Self::PreparedIndexesReplace<'indexes>)
where
Self: 'indexes,
{
}
fn append(_: &mut Self::Columns, _: Self) {}
fn swap_remove(_: &mut Self::Columns, _: usize) -> Self {
Self
}
fn get<'row>(
_: &'row Self::Id,
_: &'row Self::Columns,
_: usize,
) -> Result<WarpTableRowGetOutcome<Self::Ref<'row>>, WarpTableRowGetError>
where
Self: 'row,
{
Ok(WarpTableRowGetOutcome::Present { row: () })
}
fn get_mut<'row>(
_: &'row Self::Id,
_: &'row mut Self::Columns,
_: &'row mut Self::Indexes,
_: usize,
) -> Result<WarpTableRowGetOutcome<Self::Mut<'row>>, WarpTableRowGetError>
where
Self: 'row,
{
Ok(WarpTableRowGetOutcome::Present { row: () })
}
fn replace(
_: &mut Self::Columns,
_: usize,
_: Self,
) -> Result<WarpTableRowReplaceOutcome<Self>, WarpTableRowReplaceError<Self>> {
Ok(WarpTableRowReplaceOutcome::Replaced { previous: Self })
}
}
}