use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct AllocSet {
blocks: Vec<u64>,
size: usize,
alloc_count: usize,
hint_block_index: usize,
}
impl AllocSet {
pub fn with_capacity(capacity: usize) -> Self {
let size = 0;
let num_blocks = (capacity + 63) / 64;
return AllocSet {
blocks: Vec::with_capacity(num_blocks),
size,
alloc_count: size,
hint_block_index: 0,
};
}
pub fn size(&self) -> usize {
return self.size;
}
pub fn alloc_count(&self) -> usize {
return self.alloc_count;
}
pub fn is_full(&self) -> bool {
return self.alloc_count == self.size;
}
fn push_allocated(&mut self) -> usize {
let current_size = self.size;
if current_size % 64 == 0 {
self.blocks.push(u64::MAX);
}
self.size += 1;
self.alloc_count += 1;
return current_size;
}
pub fn free(&mut self, index: usize) {
assert!(index < self.size, "Invalid index detected");
let block_index = index / 64;
let mask = 1 << (index % 64);
assert!(
(self.blocks[block_index] & mask) != 0,
"Double free detected"
);
self.hint_block_index = block_index;
self.blocks[block_index] &= !mask;
self.alloc_count -= 1;
}
pub fn alloc(&mut self) -> usize {
let idx = self.try_alloc();
if let Some(idx) = idx {
return idx;
}
return self.push_allocated();
}
fn try_alloc(&mut self) -> Option<usize> {
if self.is_full() {
return None;
}
let num_blocks = self.blocks.len();
let mut block_index = self.hint_block_index;
loop {
let block = self.blocks[block_index];
if block == u64::MAX {
block_index = (block_index + 1) % num_blocks;
continue;
}
self.hint_block_index = block_index;
let free_bit_idx = (!block).trailing_zeros() as usize;
self.blocks[block_index] |= 1u64 << free_bit_idx;
self.alloc_count += 1;
return Some(64 * block_index + free_bit_idx);
}
}
pub fn clear(&mut self) {
self.blocks.clear();
self.size = 0;
self.alloc_count = 0;
self.hint_block_index = 0;
}
}
impl Default for AllocSet {
fn default() -> Self {
return Self::with_capacity(0);
}
}
impl Drop for AllocSet {
fn drop(&mut self) {
assert!(
self.alloc_count == 0,
"Memory leak detected: {} allocated words were not freed.",
self.alloc_count
);
}
}