citadeldb_buffer/
allocator.rs1use citadel_core::types::PageId;
9
10pub struct PageAllocator {
12 next_page_id: u32,
14 ready_to_use: Vec<PageId>,
16 freed_this_txn: Vec<PageId>,
18}
19
20impl PageAllocator {
21 pub fn new(high_water_mark: u32) -> Self {
23 Self {
24 next_page_id: high_water_mark,
25 ready_to_use: Vec::new(),
26 freed_this_txn: Vec::new(),
27 }
28 }
29
30 pub fn allocate(&mut self) -> PageId {
33 if let Some(id) = self.ready_to_use.pop() {
34 id
35 } else {
36 let id = PageId(self.next_page_id);
37 self.next_page_id += 1;
38 id
39 }
40 }
41
42 pub fn free(&mut self, page_id: PageId) {
45 self.freed_this_txn.push(page_id);
46 }
47
48 pub fn high_water_mark(&self) -> u32 {
50 self.next_page_id
51 }
52
53 pub fn freed_this_txn(&self) -> &[PageId] {
55 &self.freed_this_txn
56 }
57
58 pub fn add_ready_to_use(&mut self, pages: Vec<PageId>) {
60 self.ready_to_use.extend(pages);
61 }
62
63 pub fn commit(&mut self) -> Vec<PageId> {
66 std::mem::take(&mut self.freed_this_txn)
67 }
68
69 pub fn rollback(&mut self) {
71 self.freed_this_txn.clear();
72 }
73
74 pub fn ready_count(&self) -> usize {
76 self.ready_to_use.len()
77 }
78
79 pub fn freed_count(&self) -> usize {
81 self.freed_this_txn.len()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn allocate_from_hwm() {
91 let mut alloc = PageAllocator::new(0);
92 assert_eq!(alloc.allocate(), PageId(0));
93 assert_eq!(alloc.allocate(), PageId(1));
94 assert_eq!(alloc.allocate(), PageId(2));
95 assert_eq!(alloc.high_water_mark(), 3);
96 }
97
98 #[test]
99 fn allocate_from_ready_to_use() {
100 let mut alloc = PageAllocator::new(10);
101 alloc.add_ready_to_use(vec![PageId(3), PageId(7)]);
102 assert_eq!(alloc.allocate(), PageId(7));
104 assert_eq!(alloc.allocate(), PageId(3));
105 assert_eq!(alloc.allocate(), PageId(10));
107 }
108
109 #[test]
110 fn free_and_commit() {
111 let mut alloc = PageAllocator::new(5);
112 alloc.free(PageId(1));
113 alloc.free(PageId(3));
114 assert_eq!(alloc.freed_count(), 2);
115
116 let freed = alloc.commit();
117 assert_eq!(freed.len(), 2);
118 assert_eq!(alloc.freed_count(), 0);
119 }
120
121 #[test]
122 fn rollback_clears_freed() {
123 let mut alloc = PageAllocator::new(5);
124 alloc.free(PageId(1));
125 alloc.free(PageId(3));
126 alloc.rollback();
127 assert_eq!(alloc.freed_count(), 0);
128 }
129}