buddy_slab_allocator/buddy/
pooled_list.rs1#[cfg(feature = "log")]
7use log::{error, warn};
8
9use super::{buddy_block::BuddyBlock, global_node_pool::GlobalNodePool};
10
11pub struct PooledLinkedList {
16 head: Option<usize>,
17 tail: Option<usize>,
18 len: usize,
19}
20
21impl PooledLinkedList {
22 pub const fn new() -> Self {
24 Self {
25 head: None,
26 tail: None,
27 len: 0,
28 }
29 }
30
31 pub fn insert_sorted(&mut self, pool: &mut GlobalNodePool, data: BuddyBlock) -> bool {
34 let new_node_idx = match pool.alloc_node() {
35 Some(idx) => idx,
36 None => {
37 error!("Global node pool exhausted");
38 return false;
39 }
40 };
41
42 let mut prev_idx = None;
44 let mut current_idx = self.head;
45 let mut visited = 0;
46
47 while let Some(idx) = current_idx {
48 if visited > self.len {
49 error!("Potential cycle detected during insert");
50 pool.dealloc_node(new_node_idx);
52 return false;
53 }
54
55 if let Some(node) = pool.get_node(idx) {
56 if node.data.addr == data.addr {
57 pool.dealloc_node(new_node_idx);
60 return true;
61 }
62 if node.data.addr > data.addr {
63 break; }
65 prev_idx = current_idx;
66 current_idx = node.next;
67 } else {
68 error!("Invalid node reference in list");
69 pool.dealloc_node(new_node_idx);
70 return false;
71 }
72 visited += 1;
73 }
74
75 if let Some(node) = pool.get_node_mut(new_node_idx) {
77 node.data = data;
78 node.next = current_idx;
79 } else {
80 error!("Failed to get mutable reference to newly allocated node");
81 pool.dealloc_node(new_node_idx);
82 return false;
83 }
84
85 if let Some(prev) = prev_idx {
87 if let Some(prev_node) = pool.get_node_mut(prev) {
88 prev_node.next = Some(new_node_idx);
89 }
90 } else {
91 self.head = Some(new_node_idx);
92 }
93
94 if current_idx.is_none() {
96 self.tail = Some(new_node_idx);
97 }
98
99 self.len += 1;
100 true
101 }
102
103 pub fn has_block_in_range(&self, pool: &GlobalNodePool, start: usize, end: usize) -> bool {
105 let mut current_idx = self.head;
106 let mut visited = 0;
107
108 while let Some(idx) = current_idx {
109 if visited > self.len {
110 break;
111 }
112 if let Some(node) = pool.get_node(idx) {
113 if node.data.addr >= end {
115 break;
116 }
117 if node.data.addr >= start {
122 return true;
123 }
124 current_idx = node.next;
125 } else {
126 break;
127 }
128 visited += 1;
129 }
130 false
131 }
132
133 pub fn pop_front(&mut self, pool: &mut GlobalNodePool) -> Option<BuddyBlock> {
135 self.head?;
136
137 let head_idx = self.head?;
138
139 if let Some(head_node) = pool.get_node_mut(head_idx) {
140 self.head = head_node.next;
141 if self.head.is_none() {
142 self.tail = None;
143 }
144
145 let data = head_node.data;
146
147 pool.dealloc_node(head_idx);
149 self.len -= 1;
150
151 Some(data)
152 } else {
153 error!("Head node {head_idx} is corrupted");
154 None
155 }
156 }
157
158 pub fn is_empty(&self) -> bool {
160 self.len == 0
161 }
162
163 pub fn len(&self) -> usize {
165 self.len
166 }
167
168 pub fn find_by_addr(
172 &self,
173 pool: &GlobalNodePool,
174 addr: usize,
175 ) -> Option<(usize, Option<usize>)> {
176 let mut prev_idx = None;
177 let mut current_idx = self.head;
178 let mut visited = 0;
179
180 while let Some(idx) = current_idx {
181 if visited > self.len {
182 error!("Potential cycle detected during search");
183 return None;
184 }
185
186 if let Some(node) = pool.get_node(idx) {
187 if node.data.addr > addr {
189 break;
190 }
191 if node.data.addr == addr {
192 return Some((idx, prev_idx));
193 }
194 prev_idx = current_idx;
195 current_idx = node.next;
196 } else {
197 break;
198 }
199 visited += 1;
200 }
201
202 None
203 }
204
205 pub fn remove(&mut self, pool: &mut GlobalNodePool, node_idx: usize) -> bool {
207 let mut prev_idx = None;
209 let mut current_idx = self.head;
210 let mut visited = 0;
211
212 while let Some(idx) = current_idx {
213 if visited > self.len {
214 warn!("Potential cycle detected during remove");
215 return false;
216 }
217
218 if idx == node_idx {
219 break;
220 }
221 prev_idx = current_idx;
222 if let Some(node) = pool.get_node(idx) {
223 current_idx = node.next;
224 } else {
225 break;
226 }
227 visited += 1;
228 }
229
230 if current_idx != Some(node_idx) {
231 return false;
232 }
233
234 self.remove_with_prev_impl(pool, node_idx, prev_idx)
235 }
236
237 pub fn remove_with_prev(
242 &mut self,
243 pool: &mut GlobalNodePool,
244 node_idx: usize,
245 prev_idx: Option<usize>,
246 ) -> bool {
247 if pool.get_node(node_idx).is_none() {
249 warn!("Invalid node index {node_idx} for remove_with_prev");
250 return false;
251 }
252
253 if let Some(prev) = prev_idx {
255 if let Some(prev_node) = pool.get_node(prev) {
256 if prev_node.next != Some(node_idx) {
257 warn!("prev_idx {prev} does not point to node_idx {node_idx}");
258 return false;
259 }
260 } else {
261 warn!("Invalid prev_idx {prev}");
262 return false;
263 }
264 } else if self.head != Some(node_idx) {
265 warn!("prev_idx is None but node_idx {node_idx} is not head");
267 return false;
268 }
269
270 self.remove_with_prev_impl(pool, node_idx, prev_idx)
271 }
272
273 fn remove_with_prev_impl(
275 &mut self,
276 pool: &mut GlobalNodePool,
277 node_idx: usize,
278 prev_idx: Option<usize>,
279 ) -> bool {
280 let next_idx = pool.get_node(node_idx).and_then(|n| n.next);
282
283 if let Some(prev) = prev_idx {
285 if let Some(prev_node) = pool.get_node_mut(prev) {
286 prev_node.next = next_idx;
287 }
288 } else {
289 self.head = next_idx;
290 }
291
292 if self.tail == Some(node_idx) {
294 if next_idx.is_none() {
295 self.tail = prev_idx;
296 }
297 } else if self.head.is_none() {
298 self.tail = None;
299 }
300
301 pool.dealloc_node(node_idx);
303 self.len -= 1;
304 true
305 }
306
307 pub fn iter<'a>(&'a self, pool: &'a GlobalNodePool) -> PooledListIter<'a> {
309 PooledListIter {
310 pool,
311 current: self.head,
312 }
313 }
314
315 pub fn clear(&mut self, pool: &mut GlobalNodePool) {
319 while self.pop_front(pool).is_some() {
320 }
322 }
323}
324
325impl Default for PooledLinkedList {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330
331pub struct PooledListIter<'a> {
333 pool: &'a GlobalNodePool,
334 current: Option<usize>,
335}
336
337impl<'a> Iterator for PooledListIter<'a> {
338 type Item = &'a BuddyBlock;
339
340 fn next(&mut self) -> Option<Self::Item> {
341 self.current.and_then(|idx| {
342 if let Some(node) = self.pool.get_node(idx) {
343 self.current = node.next;
344 Some(&node.data)
345 } else {
346 self.current = None;
347 None
348 }
349 })
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use crate::buddy::ListNode;
357
358 const TEST_NODE_COUNT: usize = 512;
359
360 #[test]
361 fn test_pooled_list_basic() {
362 let mut pool: GlobalNodePool = GlobalNodePool::new();
363 let mut backing = [ListNode {
364 data: BuddyBlock { order: 0, addr: 0 },
365 next: None,
366 }; TEST_NODE_COUNT];
367
368 let region_start = backing.as_mut_ptr() as usize;
369 let region_size = core::mem::size_of_val(&backing);
370 pool.init(region_start, region_size);
371
372 let mut list: PooledLinkedList = PooledLinkedList::new();
373
374 assert!(list.is_empty());
375 assert_eq!(list.len(), 0);
376 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT);
377
378 list.insert_sorted(
379 &mut pool,
380 BuddyBlock {
381 order: 0,
382 addr: 0x1000,
383 },
384 );
385 list.insert_sorted(
386 &mut pool,
387 BuddyBlock {
388 order: 0,
389 addr: 0x2000,
390 },
391 );
392 list.insert_sorted(
393 &mut pool,
394 BuddyBlock {
395 order: 0,
396 addr: 0x3000,
397 },
398 );
399
400 assert_eq!(list.len(), 3);
401 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 3);
402
403 assert_eq!(
404 list.pop_front(&mut pool),
405 Some(BuddyBlock {
406 order: 0,
407 addr: 0x1000
408 })
409 );
410 assert_eq!(
411 list.pop_front(&mut pool),
412 Some(BuddyBlock {
413 order: 0,
414 addr: 0x2000
415 })
416 );
417 assert_eq!(list.len(), 1);
418 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 1);
419
420 list.clear(&mut pool);
422 assert!(list.is_empty());
423 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT);
424 }
425
426 #[test]
427 fn test_insert_sorted() {
428 let mut pool: GlobalNodePool = GlobalNodePool::new();
429 let mut backing = [ListNode {
430 data: BuddyBlock { order: 0, addr: 0 },
431 next: None,
432 }; TEST_NODE_COUNT];
433
434 let region_start = backing.as_mut_ptr() as usize;
435 let region_size = core::mem::size_of_val(&backing);
436 pool.init(region_start, region_size);
437
438 let mut list: PooledLinkedList = PooledLinkedList::new();
439
440 list.insert_sorted(
441 &mut pool,
442 BuddyBlock {
443 order: 0,
444 addr: 0x5000,
445 },
446 );
447 list.insert_sorted(
448 &mut pool,
449 BuddyBlock {
450 order: 0,
451 addr: 0x3000,
452 },
453 );
454 list.insert_sorted(
455 &mut pool,
456 BuddyBlock {
457 order: 0,
458 addr: 0x7000,
459 },
460 );
461 list.insert_sorted(
462 &mut pool,
463 BuddyBlock {
464 order: 0,
465 addr: 0x1000,
466 },
467 );
468
469 let items: alloc::vec::Vec<_> = list.iter(&pool).collect();
470 assert_eq!(items.len(), 4);
471 assert_eq!(items[0].addr, 0x1000);
472 assert_eq!(items[1].addr, 0x3000);
473 assert_eq!(items[2].addr, 0x5000);
474 assert_eq!(items[3].addr, 0x7000);
475 }
476
477 #[test]
478 fn test_find_and_remove() {
479 let mut pool: GlobalNodePool = GlobalNodePool::new();
480 let mut backing = [ListNode {
481 data: BuddyBlock { order: 0, addr: 0 },
482 next: None,
483 }; TEST_NODE_COUNT];
484
485 let region_start = backing.as_mut_ptr() as usize;
486 let region_size = core::mem::size_of_val(&backing);
487 pool.init(region_start, region_size);
488
489 let mut list: PooledLinkedList = PooledLinkedList::new();
490
491 list.insert_sorted(
492 &mut pool,
493 BuddyBlock {
494 order: 0,
495 addr: 0x1000,
496 },
497 );
498 list.insert_sorted(
499 &mut pool,
500 BuddyBlock {
501 order: 0,
502 addr: 0x2000,
503 },
504 );
505 list.insert_sorted(
506 &mut pool,
507 BuddyBlock {
508 order: 0,
509 addr: 0x3000,
510 },
511 );
512
513 let (node_idx, _) = list.find_by_addr(&pool, 0x2000).unwrap();
514 assert!(list.remove(&mut pool, node_idx));
515
516 assert_eq!(list.len(), 2);
517 assert_eq!(pool.free_node_count(), TEST_NODE_COUNT - 2);
518
519 let items: alloc::vec::Vec<_> = list.iter(&pool).collect();
520 assert_eq!(items[0].addr, 0x1000);
521 assert_eq!(items[1].addr, 0x3000);
522 }
523}