use super::super::{
RangeIndexOrderingLevel, RankingWorkspaceError, StorageLease, WarpTable, WarpTableBacking,
WarpTableGetAtOutcome, WarpTableRow,
inconsistency::invariant_violation,
range_ordering::{
RangeIndexRankingCandidates, WorkspaceRanking, rank_with, ranking_storage_bytes,
},
stable_slots::{StableSlotResolveOutcome, StableSlotResolver},
};
use super::selection::CallerSelection;
use core::num::NonZeroUsize;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CallerRankingError {
NoOrderingLevels,
DifferentTables,
Workspace(RankingWorkspaceError),
}
pub struct CallerRanking<'table, 'storage, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
{
table: &'table WarpTable<Row, Brand, Backing>,
ranking: WorkspaceRanking<'storage>,
}
impl<'table, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
CallerSelection<'table, '_, Row, Brand, Backing>
{
pub fn ranking_storage_bytes(
&self,
) -> Result<usize, pipecircuit_storage_backing::StorageRefusalReason> {
ranking_storage_bytes(self.table.maximum_rows)
}
pub fn rank_in<'storage, const LEVELS: usize>(
&self,
storage: StorageLease<'storage>,
limit: NonZeroUsize,
order: impl FnOnce(
&'table Backing::Indexes,
) -> [CallerOrderingLevel<'table, Backing::Indexes>; LEVELS],
) -> Result<CallerRanking<'table, 'storage, Row, Brand, Backing>, CallerRankingError>
where
'table: 'storage,
{
let levels = order(&self.table.indexes);
if levels
.iter()
.any(|level| !core::ptr::eq(&self.table.indexes, level.indexes))
{
return Err(CallerRankingError::DifferentTables);
}
let levels = levels.map(|level| level.ordering);
let (first, later) = levels
.split_first()
.ok_or(CallerRankingError::NoOrderingLevels)?;
let (ranking, _remaining) = rank_with(
first,
later,
RangeIndexRankingCandidates::Workspace(&self.membership),
limit,
storage,
)
.map_err(CallerRankingError::Workspace)?;
Ok(CallerRanking {
table: self.table,
ranking,
})
}
}
impl<'table, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
CallerRanking<'table, '_, Row, Brand, Backing>
where
Backing::Indexes: StableSlotResolver,
{
pub fn leaders(&self) -> impl Iterator<Item = Row::Ref<'table>> + '_ {
self.ranking.leaders().map(|slot| self.row(slot))
}
pub fn ties(&self) -> impl Iterator<Item = Row::Ref<'table>> + '_ {
self.ranking.tied().map(|slot| self.row(slot))
}
pub const fn needed_from_ties(&self) -> Option<NonZeroUsize> {
self.ranking.needed
}
pub fn leader_count(&self) -> usize {
self.ranking.leader_count()
}
pub fn tie_count(&self) -> usize {
self.ranking.tied_count()
}
fn row(&self, slot: crate::storage::StableSlot) -> Row::Ref<'table> {
let StableSlotResolveOutcome::Resolved { dense_row, .. } = self
.table
.indexes
.resolve_stable_slot(slot)
.unwrap_or_else(|reason| invariant_violation(reason));
match self.table.get_at(dense_row) {
WarpTableGetAtOutcome::Present { row, .. } => row,
WarpTableGetAtOutcome::Absent { dense_row } => invariant_violation(dense_row),
}
}
}
pub struct CallerOrderingLevel<'index, Indexes> {
indexes: &'index Indexes,
ordering: RangeIndexOrderingLevel<'index>,
}
impl<'index, Indexes> CallerOrderingLevel<'index, Indexes> {
pub fn new(indexes: &'index Indexes, ordering: RangeIndexOrderingLevel<'index>) -> Self {
Self { indexes, ordering }
}
}