#[derive(Debug, Clone)]
pub struct FreeList {
head: u64,
next_pointers: hashbrown::HashMap<u64, u64>,
}
impl FreeList {
pub fn new() -> Self {
Self {
head: u64::MAX,
next_pointers: hashbrown::HashMap::new(),
}
}
pub fn with_head(head: u64) -> Self {
Self {
head,
next_pointers: hashbrown::HashMap::new(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.head == u64::MAX
}
#[inline]
pub fn head(&self) -> u64 {
self.head
}
pub fn allocate(&mut self, current_count: u64) -> u64 {
if self.head != u64::MAX {
let slot_id = self.head;
self.head = self.next_pointers.remove(&slot_id).unwrap_or(u64::MAX);
slot_id
} else {
current_count
}
}
pub fn free(&mut self, slot_id: u64) {
self.next_pointers.insert(slot_id, self.head);
self.head = slot_id;
}
#[inline]
pub fn next(&self, slot_id: u64) -> Option<u64> {
self.next_pointers.get(&slot_id).copied()
}
#[inline]
pub fn len(&self) -> usize {
if self.head == u64::MAX {
0
} else {
self.next_pointers.len()
}
}
pub fn clear(&mut self) {
self.head = u64::MAX;
self.next_pointers.clear();
}
pub fn rebuild(&mut self, head: u64, links: impl Iterator<Item = (u64, u64)>) {
self.head = head;
self.next_pointers.clear();
for (slot_id, next_id) in links {
self.next_pointers.insert(slot_id, next_id);
}
}
pub fn iter(&self) -> FreeListIter<'_> {
FreeListIter {
free_list: self,
current: self.head,
}
}
}
impl Default for FreeList {
fn default() -> Self {
Self::new()
}
}
pub struct FreeListIter<'a> {
free_list: &'a FreeList,
current: u64,
}
impl<'a> Iterator for FreeListIter<'a> {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if self.current == u64::MAX {
return None;
}
let slot_id = self.current;
self.current = self
.free_list
.next_pointers
.get(&slot_id)
.copied()
.unwrap_or(u64::MAX);
Some(slot_id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_free_list_is_empty() {
let free_list = FreeList::new();
assert!(free_list.is_empty());
assert_eq!(free_list.head(), u64::MAX);
assert_eq!(free_list.len(), 0);
}
#[test]
fn test_with_head() {
let free_list = FreeList::with_head(42);
assert!(!free_list.is_empty());
assert_eq!(free_list.head(), 42);
}
#[test]
fn test_with_head_max_is_empty() {
let free_list = FreeList::with_head(u64::MAX);
assert!(free_list.is_empty());
}
#[test]
fn test_allocate_empty_list_returns_current_count() {
let mut free_list = FreeList::new();
assert_eq!(free_list.allocate(0), 0);
assert_eq!(free_list.allocate(5), 5);
assert_eq!(free_list.allocate(100), 100);
assert!(free_list.is_empty());
}
#[test]
fn test_free_adds_to_list() {
let mut free_list = FreeList::new();
free_list.free(5);
assert!(!free_list.is_empty());
assert_eq!(free_list.head(), 5);
assert_eq!(free_list.len(), 1);
}
#[test]
fn test_free_multiple_slots_lifo_order() {
let mut free_list = FreeList::new();
free_list.free(3);
free_list.free(7);
free_list.free(5);
assert_eq!(free_list.head(), 5);
assert_eq!(free_list.next(5), Some(7));
assert_eq!(free_list.next(7), Some(3));
assert_eq!(free_list.next(3), Some(u64::MAX));
assert_eq!(free_list.len(), 3);
}
#[test]
fn test_allocate_reuses_freed_slot() {
let mut free_list = FreeList::new();
free_list.free(5);
assert_eq!(free_list.head(), 5);
let slot = free_list.allocate(10); assert_eq!(slot, 5);
assert!(free_list.is_empty());
assert_eq!(free_list.head(), u64::MAX);
}
#[test]
fn test_allocate_reuses_slots_in_lifo_order() {
let mut free_list = FreeList::new();
free_list.free(3);
free_list.free(7);
free_list.free(5);
assert_eq!(free_list.allocate(100), 5);
assert_eq!(free_list.allocate(100), 7);
assert_eq!(free_list.allocate(100), 3);
assert!(free_list.is_empty());
assert_eq!(free_list.allocate(100), 100); }
#[test]
fn test_multiple_allocate_free_cycles() {
let mut free_list = FreeList::new();
free_list.free(10);
assert_eq!(free_list.allocate(5), 10);
assert!(free_list.is_empty());
free_list.free(20);
free_list.free(30);
assert_eq!(free_list.allocate(5), 30);
assert_eq!(free_list.head(), 20);
free_list.free(40);
assert_eq!(free_list.head(), 40);
assert_eq!(free_list.allocate(5), 40);
assert_eq!(free_list.allocate(5), 20);
assert!(free_list.is_empty());
}
#[test]
fn test_free_after_allocate() {
let mut free_list = FreeList::new();
free_list.free(5);
let slot = free_list.allocate(10);
assert_eq!(slot, 5);
assert!(free_list.is_empty());
free_list.free(5);
assert_eq!(free_list.head(), 5);
assert!(!free_list.is_empty());
}
#[test]
fn test_clear() {
let mut free_list = FreeList::new();
free_list.free(1);
free_list.free(2);
free_list.free(3);
assert!(!free_list.is_empty());
assert_eq!(free_list.len(), 3);
free_list.clear();
assert!(free_list.is_empty());
assert_eq!(free_list.head(), u64::MAX);
assert_eq!(free_list.len(), 0);
}
#[test]
fn test_rebuild() {
let mut free_list = FreeList::new();
let links = vec![(5u64, 7u64), (7, 3), (3, u64::MAX)];
free_list.rebuild(5, links.into_iter());
assert_eq!(free_list.head(), 5);
assert_eq!(free_list.next(5), Some(7));
assert_eq!(free_list.next(7), Some(3));
assert_eq!(free_list.next(3), Some(u64::MAX));
assert_eq!(free_list.len(), 3);
assert_eq!(free_list.allocate(100), 5);
assert_eq!(free_list.allocate(100), 7);
assert_eq!(free_list.allocate(100), 3);
assert!(free_list.is_empty());
}
#[test]
fn test_rebuild_empty() {
let mut free_list = FreeList::new();
free_list.free(1);
free_list.free(2);
free_list.rebuild(u64::MAX, std::iter::empty());
assert!(free_list.is_empty());
assert_eq!(free_list.len(), 0);
}
#[test]
fn test_iter() {
let mut free_list = FreeList::new();
free_list.free(3);
free_list.free(7);
free_list.free(5);
let slots: Vec<u64> = free_list.iter().collect();
assert_eq!(slots, vec![5, 7, 3]); }
#[test]
fn test_iter_empty() {
let free_list = FreeList::new();
let slots: Vec<u64> = free_list.iter().collect();
assert!(slots.is_empty());
}
#[test]
fn test_default() {
let free_list = FreeList::default();
assert!(free_list.is_empty());
assert_eq!(free_list.head(), u64::MAX);
}
#[test]
fn test_clone() {
let mut free_list = FreeList::new();
free_list.free(1);
free_list.free(2);
free_list.free(3);
let cloned = free_list.clone();
assert_eq!(free_list.head(), cloned.head());
assert_eq!(free_list.len(), cloned.len());
free_list.allocate(10);
assert_ne!(free_list.head(), cloned.head());
}
#[test]
fn test_large_number_of_slots() {
let mut free_list = FreeList::new();
for i in 0..1000 {
free_list.free(i);
}
assert_eq!(free_list.len(), 1000);
assert_eq!(free_list.head(), 999);
for i in (0..1000).rev() {
assert_eq!(free_list.allocate(2000), i);
}
assert!(free_list.is_empty());
}
#[test]
fn test_non_sequential_slot_ids() {
let mut free_list = FreeList::new();
free_list.free(1000);
free_list.free(5);
free_list.free(999999);
free_list.free(42);
assert_eq!(free_list.allocate(0), 42);
assert_eq!(free_list.allocate(0), 999999);
assert_eq!(free_list.allocate(0), 5);
assert_eq!(free_list.allocate(0), 1000);
assert!(free_list.is_empty());
}
#[test]
fn test_next_returns_none_for_non_free_slot() {
let mut free_list = FreeList::new();
free_list.free(5);
assert!(free_list.next(5).is_some());
assert!(free_list.next(10).is_none());
}
#[test]
fn test_next_returns_max_for_tail() {
let mut free_list = FreeList::new();
free_list.free(3); free_list.free(5);
assert_eq!(free_list.next(3), Some(u64::MAX));
assert_eq!(free_list.next(5), Some(3));
}
}