pub struct KvCachePool {
pages: Vec<Box<[f32]>>,
free_list: Vec<usize>,
page_size: usize,
}
impl KvCachePool {
pub fn new(page_size: usize, initial_pages: usize) -> Self {
let mut pages = Vec::with_capacity(initial_pages);
let mut free_list = Vec::with_capacity(initial_pages);
for i in 0..initial_pages {
pages.push(vec![0.0f32; page_size].into_boxed_slice());
free_list.push(i);
}
Self {
pages,
free_list,
page_size,
}
}
pub fn alloc(&mut self) -> Option<usize> {
self.free_list.pop()
}
pub fn free(&mut self, page_idx: usize) {
debug_assert!(
page_idx < self.pages.len(),
"KvCachePool::free: page_idx {page_idx} out of range (total {})",
self.pages.len()
);
self.free_list.push(page_idx);
}
pub fn page(&self, page_idx: usize) -> &[f32] {
&self.pages[page_idx]
}
pub fn page_mut(&mut self, page_idx: usize) -> &mut [f32] {
&mut self.pages[page_idx]
}
pub fn total_pages(&self) -> usize {
self.pages.len()
}
pub fn free_pages(&self) -> usize {
self.free_list.len()
}
pub fn used_pages(&self) -> usize {
self.pages.len() - self.free_list.len()
}
pub fn page_size(&self) -> usize {
self.page_size
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alloc_and_free() {
let mut pool = KvCachePool::new(64, 4);
assert_eq!(pool.total_pages(), 4);
assert_eq!(pool.free_pages(), 4);
assert_eq!(pool.used_pages(), 0);
let idx0 = pool.alloc().expect("should allocate");
assert_eq!(pool.used_pages(), 1);
assert_eq!(pool.free_pages(), 3);
let idx1 = pool.alloc().expect("should allocate");
assert_ne!(idx0, idx1);
pool.free(idx0);
assert_eq!(pool.used_pages(), 1);
assert_eq!(pool.free_pages(), 3);
}
#[test]
fn test_pool_exhaustion_returns_none() {
let mut pool = KvCachePool::new(16, 2);
let _a = pool.alloc().expect("first alloc");
let _b = pool.alloc().expect("second alloc");
assert!(pool.alloc().is_none());
}
#[test]
fn test_free_then_realloc() {
let mut pool = KvCachePool::new(8, 1);
let idx = pool.alloc().expect("alloc");
pool.free(idx);
let idx2 = pool.alloc().expect("re-alloc after free");
assert_eq!(idx, idx2);
}
#[test]
fn test_page_read_write() {
let mut pool = KvCachePool::new(4, 2);
let idx = pool.alloc().expect("alloc");
{
let page = pool.page_mut(idx);
page[0] = 1.0;
page[1] = 2.5;
}
let page = pool.page(idx);
assert!((page[0] - 1.0).abs() < 1e-9);
assert!((page[1] - 2.5).abs() < 1e-9);
}
#[test]
fn test_page_size_accessor() {
let pool = KvCachePool::new(128, 0);
assert_eq!(pool.page_size(), 128);
assert_eq!(pool.total_pages(), 0);
assert_eq!(pool.free_pages(), 0);
}
}