use super::{Posting, RecordId, Weight};
pub trait PostingCursor {
fn peek(&self) -> Option<Posting>;
fn advance(&mut self);
fn seek(&mut self, target: RecordId) -> Option<Posting>;
fn remaining(&self) -> usize;
fn last_id(&self) -> Option<RecordId>;
fn exhaust(&mut self);
fn upper_bound(&self) -> Weight {
self.peek().map_or(Weight::NEG_INFINITY, |p| p.tail_max)
}
fn lower_bound(&self) -> Weight {
Weight::NEG_INFINITY
}
fn drain_through(&mut self, hi: RecordId, mut visit: impl FnMut(RecordId, Weight)) {
while let Some(p) = self.peek() {
if p.id > hi {
break;
}
visit(p.id, p.weight);
self.advance();
}
}
fn is_exhausted(&self) -> bool {
self.peek().is_none()
}
}
#[derive(Clone, Debug)]
pub struct SliceCursor<'a> {
items: &'a [Posting],
pos: usize,
}
impl<'a> SliceCursor<'a> {
pub fn new(items: &'a [Posting]) -> Self {
debug_assert!(items.windows(2).all(|w| w[0].id < w[1].id));
Self { items, pos: 0 }
}
pub fn position(&self) -> usize {
self.pos
}
}
impl PostingCursor for SliceCursor<'_> {
#[inline]
fn peek(&self) -> Option<Posting> {
self.items.get(self.pos).copied()
}
#[inline]
fn advance(&mut self) {
if self.pos < self.items.len() {
self.pos += 1;
}
}
fn seek(&mut self, target: RecordId) -> Option<Posting> {
let rest = &self.items[self.pos.min(self.items.len())..];
let step = rest.partition_point(|p| p.id < target);
self.pos += step;
self.peek()
}
#[inline]
fn remaining(&self) -> usize {
self.items.len().saturating_sub(self.pos)
}
#[inline]
fn last_id(&self) -> Option<RecordId> {
self.items.last().map(|p| p.id)
}
fn exhaust(&mut self) {
self.pos = self.items.len();
}
fn drain_through(&mut self, hi: RecordId, mut visit: impl FnMut(RecordId, Weight)) {
let rest = &self.items[self.pos.min(self.items.len())..];
let mut taken = 0;
for p in rest {
if p.id > hi {
break;
}
visit(p.id, p.weight);
taken += 1;
}
self.pos += taken;
}
}