use std::{cmp::Reverse, collections::BinaryHeap};
#[derive(Debug)]
pub(crate) struct SlotReservations {
next: u64,
free: BinaryHeap<Reverse<u64>>,
check_reserved: bool,
}
impl SlotReservations {
#[allow(unused)]
pub(crate) fn new() -> Self {
Self {
next: 0,
free: BinaryHeap::new(),
check_reserved: false,
}
}
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
next: 0,
free: BinaryHeap::with_capacity(capacity),
check_reserved: false,
}
}
pub(crate) fn set_check_reserved(&mut self, check_reserved: bool) {
self.check_reserved = check_reserved;
}
#[inline]
pub(crate) fn reserve(&mut self) -> u64 {
if let Some(slot) = self.free.pop() {
slot.0
} else {
let slot = self.next;
self.next += 1;
slot
}
}
#[inline]
pub(crate) fn release(&mut self, slot: u64) {
if self.check_reserved {
assert!(self.check_reserved(slot), "slot {slot} is reserved");
}
self.free.push(Reverse(slot));
}
#[must_use]
pub(crate) fn check_reserved(&self, slot: u64) -> bool {
if slot >= self.next {
return false;
}
if self.free.iter().any(|s| s.0 == slot) {
return false;
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn release_in_order() {
let mut slots = SlotReservations::new();
slots.set_check_reserved(true);
assert_reserved(&slots, &[]);
let slot1 = slots.reserve();
assert_eq!(slot1, 0);
assert_reserved(&slots, &[0]);
let slot2 = slots.reserve();
assert_eq!(slot2, 1);
assert_reserved(&slots, &[0, 1]);
slots.release(slot1);
assert_reserved(&slots, &[1]);
slots.release(slot2);
assert_reserved(&slots, &[]);
assert_eq!(slots.reserve(), 0);
assert_reserved(&slots, &[0]);
assert_eq!(slots.reserve(), 1);
assert_reserved(&slots, &[0, 1]);
}
#[test]
fn release_out_of_order() {
let mut slots = SlotReservations::new();
slots.set_check_reserved(true);
assert_reserved(&slots, &[]);
let slot1 = slots.reserve();
assert_eq!(slot1, 0);
assert_reserved(&slots, &[0]);
let slot2 = slots.reserve();
assert_eq!(slot2, 1);
assert_reserved(&slots, &[0, 1]);
slots.release(slot2);
assert_reserved(&slots, &[0]);
slots.release(slot1);
assert_reserved(&slots, &[]);
assert_eq!(slots.reserve(), slot1);
assert_reserved(&slots, &[0]);
assert_eq!(slots.reserve(), slot2);
assert_reserved(&slots, &[0, 1]);
}
fn assert_reserved(slots: &SlotReservations, expected: &[u64]) {
for &slot in expected {
assert!(slots.check_reserved(slot), "slot {} is not reserved", slot);
}
for slot in (expected.iter().max().map_or(0, |max| max + 1))..64 {
assert!(
!slots.check_reserved(slot),
"slot {slot} is not reserved (slots: {slots:?})",
);
}
}
}