use std::collections::BTreeMap;
pub(crate) enum LeafPolicy {
Fifo(FifoPolicy),
Tick(TickPolicy),
}
impl LeafPolicy {
pub(crate) fn fifo(capacity: usize) -> Self {
Self::Fifo(FifoPolicy::with_capacity(capacity))
}
pub(crate) fn tick(capacity: usize) -> Self {
Self::Tick(TickPolicy::with_capacity(capacity))
}
pub(crate) fn on_node_inserted(&mut self, idx: u32) {
match self {
Self::Fifo(_) => {} Self::Tick(p) => p.on_node_inserted(idx),
}
}
pub(crate) fn on_leaf_added(&mut self, idx: u32) {
match self {
Self::Fifo(p) => p.on_leaf_added(idx),
Self::Tick(p) => p.on_leaf_added(idx),
}
}
pub(crate) fn on_leaf_demoted(&mut self, idx: u32) {
match self {
Self::Fifo(p) => p.unlink(idx),
Self::Tick(p) => p.on_leaf_demoted(idx),
}
}
pub(crate) fn on_node_removed(&mut self, idx: u32) {
match self {
Self::Fifo(p) => p.unlink(idx),
Self::Tick(p) => p.on_node_removed(idx),
}
}
pub(crate) fn next_victim(&self) -> Option<u32> {
match self {
Self::Fifo(p) => p.next_victim(),
Self::Tick(p) => p.next_victim(),
}
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
match self {
Self::Fifo(p) => p.len(),
Self::Tick(p) => p.queue.len(),
}
}
}
#[derive(Clone, Copy)]
struct FifoLink {
prev: Option<u32>,
next: Option<u32>,
}
pub(crate) struct FifoPolicy {
links: Vec<Option<FifoLink>>,
head: Option<u32>,
tail: Option<u32>,
}
impl FifoPolicy {
fn with_capacity(capacity: usize) -> Self {
Self {
links: Vec::with_capacity(capacity),
head: None,
tail: None,
}
}
fn ensure(&mut self, idx: u32) {
if idx as usize >= self.links.len() {
self.links.resize(idx as usize + 1, None);
}
}
fn on_leaf_added(&mut self, idx: u32) {
self.ensure(idx);
debug_assert!(
self.links[idx as usize].is_none(),
"FifoPolicy: leaf {idx} added while already linked"
);
self.links[idx as usize] = Some(FifoLink {
prev: self.tail,
next: None,
});
match self.tail {
Some(t) => self.links[t as usize].as_mut().unwrap().next = Some(idx),
None => self.head = Some(idx),
}
self.tail = Some(idx);
}
fn unlink(&mut self, idx: u32) {
let Some(link) = self.links.get_mut(idx as usize).and_then(Option::take) else {
return;
};
match link.prev {
Some(p) => self.links[p as usize].as_mut().unwrap().next = link.next,
None => self.head = link.next,
}
match link.next {
Some(n) => self.links[n as usize].as_mut().unwrap().prev = link.prev,
None => self.tail = link.prev,
}
}
fn next_victim(&self) -> Option<u32> {
self.head
}
#[cfg(test)]
fn len(&self) -> usize {
let mut n = 0;
let mut cur = self.head;
while let Some(i) = cur {
n += 1;
cur = self.links[i as usize].unwrap().next;
}
n
}
}
pub(crate) struct TickPolicy {
ticks: Vec<Option<u64>>,
queue: BTreeMap<(u64, u32), ()>,
next_tick: u64,
}
impl TickPolicy {
fn with_capacity(capacity: usize) -> Self {
Self {
ticks: Vec::with_capacity(capacity),
queue: BTreeMap::new(),
next_tick: 0,
}
}
fn ensure(&mut self, idx: u32) {
if idx as usize >= self.ticks.len() {
self.ticks.resize(idx as usize + 1, None);
}
}
fn on_node_inserted(&mut self, idx: u32) {
self.ensure(idx);
let tick = self.next_tick;
self.next_tick += 1;
self.ticks[idx as usize] = Some(tick);
}
fn on_leaf_added(&mut self, idx: u32) {
let tick =
self.ticks[idx as usize].expect("TickPolicy: on_leaf_added before on_node_inserted");
self.queue.insert((tick, idx), ());
}
fn on_leaf_demoted(&mut self, idx: u32) {
if let Some(tick) = self.ticks[idx as usize] {
self.queue.remove(&(tick, idx));
}
}
fn on_node_removed(&mut self, idx: u32) {
if let Some(tick) = self.ticks[idx as usize].take() {
self.queue.remove(&(tick, idx));
}
}
fn next_victim(&self) -> Option<u32> {
self.queue.first_key_value().map(|(&(_, idx), _)| idx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fifo_order_and_unlink() {
let mut p = FifoPolicy::with_capacity(8);
for i in 0..4 {
p.on_leaf_added(i);
}
assert_eq!(p.next_victim(), Some(0));
assert_eq!(p.len(), 4);
p.unlink(2);
assert_eq!(p.len(), 3);
p.unlink(0);
assert_eq!(p.next_victim(), Some(1));
p.on_leaf_added(0);
assert_eq!(p.next_victim(), Some(1)); let mut order = Vec::new();
while let Some(v) = p.next_victim() {
order.push(v);
p.unlink(v);
}
assert_eq!(order, vec![1, 3, 0]);
}
#[test]
fn tick_re_leafed_node_keeps_original_position() {
let mut p = TickPolicy::with_capacity(8);
for i in 0..3 {
p.on_node_inserted(i);
p.on_leaf_added(i);
}
assert_eq!(p.next_victim(), Some(0));
p.on_leaf_demoted(0);
assert_eq!(p.next_victim(), Some(1)); p.on_leaf_added(0);
assert_eq!(p.next_victim(), Some(0));
p.on_node_removed(0);
p.on_node_inserted(0);
p.on_leaf_added(0);
let mut order = Vec::new();
while let Some(v) = p.next_victim() {
order.push(v);
p.on_node_removed(v);
}
assert_eq!(order, vec![1, 2, 0]);
}
}