buddy_slab_allocator/buddy/
global_node_pool.rs1use super::buddy_block::BuddyBlock;
7
8#[derive(Debug, Clone, Copy)]
10pub struct ListNode<T> {
11 pub data: T,
12 pub next: Option<usize>,
13}
14
15fn align_up(addr: usize, align: usize) -> usize {
16 debug_assert!(align.is_power_of_two());
17 (addr + align - 1) & !(align - 1)
18}
19
20pub struct GlobalNodePool {
26 free_head: Option<usize>,
28 total_nodes: usize,
30 free_nodes: usize,
32 total_allocations: usize,
34 total_deallocations: usize,
35}
36
37impl GlobalNodePool {
38 pub const fn new() -> Self {
40 Self {
41 free_head: None,
42 total_nodes: 0,
43 free_nodes: 0,
44 total_allocations: 0,
45 total_deallocations: 0,
46 }
47 }
48
49 pub fn init(&mut self, region_start: usize, region_size: usize) {
55 self.free_head = None;
56 self.total_nodes = 0;
57 self.free_nodes = 0;
58 self.total_allocations = 0;
59 self.total_deallocations = 0;
60 self.add_region(region_start, region_size);
61 }
62
63 pub fn add_region(&mut self, region_start: usize, region_size: usize) {
68 let node_size = core::mem::size_of::<ListNode<BuddyBlock>>();
69 let align = core::mem::align_of::<ListNode<BuddyBlock>>();
70 if region_size < node_size {
71 return;
72 }
73
74 let mut current = align_up(region_start, align);
75 let end = region_start + region_size;
76
77 while current + node_size <= end {
78 unsafe {
79 let node_ptr = current as *mut ListNode<BuddyBlock>;
80 core::ptr::write(
81 node_ptr,
82 ListNode {
83 data: core::mem::zeroed(),
84 next: self.free_head,
85 },
86 );
87 }
88
89 self.free_head = Some(current);
90 self.total_nodes += 1;
91 self.free_nodes += 1;
92 current += node_size;
93 }
94 }
95
96 pub fn alloc_node(&mut self) -> Option<usize> {
100 let node_addr = self.free_head?;
101
102 unsafe {
103 let node = &mut *(node_addr as *mut ListNode<BuddyBlock>);
104 self.free_head = node.next;
105 node.next = None;
106 }
107
108 self.total_allocations += 1;
109 if self.free_nodes > 0 {
110 self.free_nodes -= 1;
111 } else {
112 panic!("free_nodes: {}", self.free_nodes);
113 }
114
115 Some(node_addr)
116 }
117
118 pub fn dealloc_node(&mut self, node_idx: usize) {
122 unsafe {
123 let node_ptr = node_idx as *mut ListNode<BuddyBlock>;
124 core::ptr::write(
125 node_ptr,
126 ListNode {
127 data: core::mem::zeroed(),
128 next: self.free_head,
129 },
130 );
131 }
132
133 self.free_head = Some(node_idx);
134 self.total_deallocations += 1;
135 self.free_nodes += 1;
136 }
137
138 pub fn get_node(&self, node_idx: usize) -> Option<&ListNode<BuddyBlock>> {
140 Some(unsafe { &*(node_idx as *const ListNode<BuddyBlock>) })
141 }
142
143 pub fn get_node_mut(&mut self, node_idx: usize) -> Option<&mut ListNode<BuddyBlock>> {
145 Some(unsafe { &mut *(node_idx as *mut ListNode<BuddyBlock>) })
146 }
147
148 pub fn free_node_count(&self) -> usize {
150 self.free_nodes
151 }
152
153 pub fn allocated_node_count(&self) -> usize {
155 self.total_nodes.saturating_sub(self.free_nodes)
156 }
157
158 pub fn get_stats(&self) -> GlobalPoolStats {
160 GlobalPoolStats {
161 total_nodes: self.total_nodes,
162 free_nodes: self.free_nodes,
163 allocated_nodes: self.allocated_node_count(),
164 total_allocations: self.total_allocations,
165 total_deallocations: self.total_deallocations,
166 }
167 }
168}
169
170impl Default for GlobalNodePool {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176#[derive(Debug, Default, Clone)]
178pub struct GlobalPoolStats {
179 pub total_nodes: usize,
180 pub free_nodes: usize,
181 pub allocated_nodes: usize,
182 pub total_allocations: usize,
183 pub total_deallocations: usize,
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 const TEST_NODE_COUNT: usize = 512;
191 #[test]
192 fn test_pool_init() {
193 let mut pool: GlobalNodePool = GlobalNodePool::new();
194 let mut backing = [ListNode {
195 data: BuddyBlock { order: 0, addr: 0 },
196 next: None,
197 }; TEST_NODE_COUNT];
198
199 let region_start = backing.as_mut_ptr() as usize;
200 let region_size = core::mem::size_of_val(&backing);
201 pool.init(region_start, region_size);
202
203 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT);
204 assert_eq!(pool.allocated_node_count(), 0);
205 }
206
207 #[test]
208 fn test_alloc_dealloc() {
209 let mut pool: GlobalNodePool = GlobalNodePool::new();
210 let mut backing = [ListNode {
211 data: BuddyBlock { order: 0, addr: 0 },
212 next: None,
213 }; TEST_NODE_COUNT];
214
215 let region_start = backing.as_mut_ptr() as usize;
216 let region_size = core::mem::size_of_val(&backing);
217 pool.init(region_start, region_size);
218
219 let idx1 = pool.alloc_node().unwrap();
220 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 1);
221 assert_eq!(pool.allocated_node_count(), 1);
222
223 let idx2 = pool.alloc_node().unwrap();
224 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 2);
225
226 pool.dealloc_node(idx1);
227 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 1);
228
229 pool.dealloc_node(idx2);
230 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT);
231 }
232
233 #[test]
234 fn test_pool_exhaustion() {
235 let mut pool: GlobalNodePool = GlobalNodePool::new();
236 let mut backing = [ListNode {
237 data: BuddyBlock { order: 0, addr: 0 },
238 next: None,
239 }; TEST_NODE_COUNT];
240
241 let region_start = backing.as_mut_ptr() as usize;
242 let region_size = core::mem::size_of_val(&backing);
243 pool.init(region_start, region_size);
244
245 let mut indices = alloc::vec::Vec::new();
247 for _ in 0..TEST_NODE_COUNT {
248 indices.push(pool.alloc_node().unwrap());
249 }
250
251 assert_eq!(pool.free_node_count(), 0);
252 assert!(pool.alloc_node().is_none());
253
254 pool.dealloc_node(indices[0]);
256 assert_eq!(pool.free_node_count(), 1);
257 assert!(pool.alloc_node().is_some());
258 }
259
260 #[test]
261 fn test_node_access() {
262 let mut pool: GlobalNodePool = GlobalNodePool::new();
263 let mut backing = [ListNode {
264 data: BuddyBlock { order: 0, addr: 0 },
265 next: None,
266 }; TEST_NODE_COUNT];
267
268 let region_start = backing.as_mut_ptr() as usize;
269 let region_size = core::mem::size_of_val(&backing);
270 pool.init(region_start, region_size);
271
272 let idx = pool.alloc_node().unwrap();
273
274 if let Some(node) = pool.get_node_mut(idx) {
276 node.data = BuddyBlock {
277 order: 0,
278 addr: 0x1000,
279 };
280 }
281
282 if let Some(node) = pool.get_node(idx) {
284 assert_eq!(node.data.order, 0);
285 assert_eq!(node.data.addr, 0x1000);
286 }
287 }
288
289 #[test]
290 fn test_stats() {
291 let mut pool: GlobalNodePool = GlobalNodePool::new();
292 let mut backing = [ListNode {
293 data: BuddyBlock { order: 0, addr: 0 },
294 next: None,
295 }; TEST_NODE_COUNT];
296
297 let region_start = backing.as_mut_ptr() as usize;
298 let region_size = core::mem::size_of_val(&backing);
299 pool.init(region_start, region_size);
300
301 let idx1 = pool.alloc_node().unwrap();
302 let _idx2 = pool.alloc_node().unwrap();
303
304 let stats = pool.get_stats();
305 assert_eq!(stats.total_nodes, TEST_NODE_COUNT);
306 assert_eq!(stats.free_nodes, TEST_NODE_COUNT - 2);
307 assert_eq!(stats.allocated_nodes, 2);
308 assert_eq!(stats.total_allocations, 2);
309 assert_eq!(stats.total_deallocations, 0);
310
311 pool.dealloc_node(idx1);
312 let stats2 = pool.get_stats();
313 assert_eq!(stats2.free_nodes, TEST_NODE_COUNT - 1);
314 assert_eq!(stats2.total_deallocations, 1);
315 }
316}