use std::sync::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MkHandle {
pub(crate) index: u32,
pub(crate) generation: u32,
}
struct HandleEntry {
ptr: *mut u8,
generation: u32,
}
unsafe impl Send for HandleEntry {}
unsafe impl Sync for HandleEntry {}
pub struct MkHandleAllocator {
entries: RwLock<Vec<HandleEntry>>,
free_indices: RwLock<Vec<u32>>,
}
impl MkHandleAllocator {
pub fn new() -> Self {
Self {
entries: RwLock::new(Vec::with_capacity(1024)),
free_indices: RwLock::new(Vec::with_capacity(128)),
}
}
pub fn allocate(&self, ptr: *mut u8) -> MkHandle {
let mut free_indices = self.free_indices.write().unwrap();
if let Some(index) = free_indices.pop() {
let mut entries = self.entries.write().unwrap();
let entry = &mut entries[index as usize];
entry.ptr = ptr;
entry.generation += 1;
MkHandle {
index,
generation: entry.generation,
}
} else {
let mut entries = self.entries.write().unwrap();
let index = entries.len() as u32;
let generation = 1;
entries.push(HandleEntry {
ptr,
generation,
});
MkHandle {
index,
generation,
}
}
}
pub fn update(&self, handle: MkHandle, new_ptr: *mut u8) -> bool {
let mut entries = self.entries.write().unwrap();
if let Some(entry) = entries.get_mut(handle.index as usize) {
if entry.generation == handle.generation {
entry.ptr = new_ptr;
return true;
}
}
false
}
pub fn resolve(&self, handle: MkHandle) -> Option<*mut u8> {
let entries = self.entries.read().unwrap();
entries.get(handle.index as usize).and_then(|entry| {
if entry.generation == handle.generation && !entry.ptr.is_null() {
Some(entry.ptr)
} else {
None
}
})
}
pub fn deallocate(&self, handle: MkHandle) -> bool {
let mut entries = self.entries.write().unwrap();
if let Some(entry) = entries.get_mut(handle.index as usize) {
if entry.generation == handle.generation {
entry.ptr = std::ptr::null_mut();
self.free_indices.write().unwrap().push(handle.index);
return true;
}
}
false
}
pub fn len(&self) -> usize {
let entries = self.entries.read().unwrap();
let free_indices = self.free_indices.read().unwrap();
entries.len() - free_indices.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for MkHandleAllocator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handle_allocator_basic() {
let table = MkHandleAllocator::new();
let ptr = 0x1234 as *mut u8;
let handle = table.allocate(ptr);
assert_eq!(table.resolve(handle), Some(ptr));
let new_ptr = 0x5678 as *mut u8;
assert!(table.update(handle, new_ptr));
assert_eq!(table.resolve(handle), Some(new_ptr));
assert!(table.deallocate(handle));
assert_eq!(table.resolve(handle), None);
}
#[test]
fn test_handle_generation() {
let table = MkHandleAllocator::new();
let ptr1 = 0x1 as *mut u8;
let handle1 = table.allocate(ptr1);
table.deallocate(handle1);
let ptr2 = 0x2 as *mut u8;
let handle2 = table.allocate(ptr2);
assert_eq!(handle1.index, handle2.index);
assert_ne!(handle1.generation, handle2.generation);
assert_eq!(table.resolve(handle1), None);
assert_eq!(table.resolve(handle2), Some(ptr2));
}
}