use std::collections::HashMap;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Loc {
tf: u32,
band: u8,
slot: u32,
}
fn band_of(dl: u32) -> u8 {
dl.max(1).ilog2().min(15) as u8
}
pub(crate) const BAND_MIN_DL: [u32; 16] = [
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
];
type Bands = Vec<(u8, Vec<u32>)>;
#[derive(Debug)]
pub enum Buckets {
One { id: u32, tf: u32, dl: u32 },
Many(Box<ManyBuckets>),
}
#[derive(Debug, Default)]
pub struct ManyBuckets {
buckets: Vec<(u32, Bands)>,
pub(crate) index: HashMap<u32, Loc>,
}
impl Default for Buckets {
fn default() -> Self {
Buckets::Many(Box::default())
}
}
impl ManyBuckets {
fn insert(&mut self, tf: u32, dl: u32, id: u32) {
let pos = self.buckets.iter().position(|(t, _)| *t <= tf);
let slot = match pos {
Some(i) if self.buckets[i].0 == tf => &mut self.buckets[i].1,
Some(i) => {
self.buckets.insert(i, (tf, Bands::new()));
&mut self.buckets[i].1
}
None => {
self.buckets.push((tf, Bands::new()));
&mut self.buckets.last_mut().expect("just pushed").1
}
};
let band = band_of(dl);
let bi = match slot.binary_search_by_key(&band, |(b, _)| *b) {
Ok(i) => i,
Err(i) => {
slot.insert(i, (band, Vec::new()));
i
}
};
let v = &mut slot[bi].1;
self.index.insert(id, Loc { tf, band, slot: v.len() as u32 });
v.push(id);
}
fn remove(&mut self, id: u32) {
let Some(loc) = self.index.remove(&id) else { return };
if let Some(i) = self.buckets.iter().position(|(t, _)| *t == loc.tf)
&& let Ok(bi) = self.buckets[i].1.binary_search_by_key(&loc.band, |(b, _)| *b)
{
let v = &mut self.buckets[i].1[bi].1;
let si = loc.slot as usize;
v.swap_remove(si);
if let Some(&moved) = v.get(si) {
self.index.get_mut(&moved).expect("indexed posting").slot = loc.slot;
}
if v.is_empty() {
self.buckets[i].1.remove(bi);
}
if self.buckets[i].1.is_empty() {
self.buckets.remove(i);
}
}
}
}
impl Buckets {
pub(crate) fn new_one(tf: u32, dl: u32, id: u32) -> Self {
Buckets::One { id, tf, dl }
}
pub(crate) fn insert(&mut self, tf: u32, dl: u32, id: u32) {
match self {
Buckets::One { id: id0, tf: tf0, dl: dl0 } => {
let (id0, tf0, dl0) = (*id0, *tf0, *dl0);
let mut many = ManyBuckets::default();
many.insert(tf0, dl0, id0);
many.insert(tf, dl, id);
*self = Buckets::Many(Box::new(many));
}
Buckets::Many(m) => m.insert(tf, dl, id),
}
}
pub(crate) fn remove(&mut self, _tf: u32, _dl: u32, id: u32) {
match self {
Buckets::One { id: id0, .. } => {
if *id0 == id {
*self = Buckets::Many(Box::default());
}
}
Buckets::Many(m) => m.remove(id),
}
}
pub(crate) fn len(&self) -> usize {
match self {
Buckets::One { .. } => 1,
Buckets::Many(m) => m.index.len(),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.len() == 0
}
pub(crate) fn max_tf(&self) -> u32 {
match self {
Buckets::One { tf, .. } => *tf,
Buckets::Many(m) => m.buckets.first().map_or(1, |(t, _)| *t),
}
}
pub(crate) fn get(&self, id: u32) -> Option<u32> {
match self {
Buckets::One { id: id0, tf, .. } => (*id0 == id).then_some(*tf),
Buckets::Many(m) => m.index.get(&id).map(|l| l.tf),
}
}
pub(crate) fn tf_groups(&self) -> Vec<(u32, BandsView<'_>)> {
match self {
Buckets::One { id, tf, dl } => {
vec![(*tf, BandsView::One(band_of(*dl), std::slice::from_ref(id)))]
}
Buckets::Many(m) => m
.buckets
.iter()
.map(|(t, bands)| (*t, BandsView::Slice(bands.as_slice())))
.collect(),
}
}
}
pub(crate) enum BandsView<'a> {
One(u8, &'a [u32]),
Slice(&'a [(u8, Vec<u32>)]),
}
impl BandsView<'_> {
pub(crate) fn iter(&self) -> impl Iterator<Item = (u8, &[u32])> + '_ {
let (one, slice) = match self {
BandsView::One(b, ids) => (Some((*b, *ids)), [].as_slice()),
BandsView::Slice(s) => (None, *s),
};
one.into_iter().chain(slice.iter().map(|(b, v)| (*b, v.as_slice())))
}
}