use crate::{BuddyCollection, BuddyLine, OligarchyCollection, Order};
use core::{fmt, ptr::NonNull};
pub struct LinkedListBuddy {
free_list: Node,
order: Order,
}
unsafe impl Send for LinkedListBuddy {}
impl BuddyLine for LinkedListBuddy {
const INTRUSIVE_META_SIZE: usize = core::mem::size_of::<Node>();
const EMPTY: Self = Self {
free_list: Node { next: None },
order: Order::new(0),
};
#[inline]
fn init(&mut self, order: usize, _base: usize) {
self.order = Order::new(order);
}
fn take(&mut self, _idx: usize) -> bool {
todo!("not efficient")
}
}
impl OligarchyCollection for LinkedListBuddy {
#[inline]
fn take_any(&mut self, align_order: usize, count: usize) -> Option<usize> {
if count > 1 || align_order > 0 {
None
} else {
self.free_list
.take_any()
.map(|ptr| self.order.ptr_to_idx(ptr))
}
}
#[inline]
fn put(&mut self, idx: usize) {
let ptr = self.order.idx_to_ptr(idx).expect("block address is null");
self.free_list.insert_unordered(ptr);
}
}
impl BuddyCollection for LinkedListBuddy {
#[inline]
fn take_any(&mut self, align_order: usize) -> Option<usize> {
if align_order != 0 {
None
} else {
self.free_list
.take_any()
.map(|ptr| self.order.ptr_to_idx(ptr))
}
}
fn put(&mut self, idx: usize) -> Option<usize> {
let node = self.order.idx_to_ptr(idx).expect("block address is null");
let Some(buddy) = self.order.idx_to_ptr(idx ^ 1) else {
self.free_list.insert_unordered(node);
return None;
};
if self.free_list.insert(node, buddy) {
None
} else {
Some(idx >> 1)
}
}
}
impl fmt::Debug for LinkedListBuddy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
let mut cursor = &self.free_list;
while let Some(next) = cursor.next {
write!(f, "{:#x}, ", self.order.ptr_to_idx(next))?;
cursor = unsafe { next.as_ref() };
}
write!(f, "]")
}
}
#[repr(transparent)]
struct Node {
next: Option<NonNull<Node>>,
}
impl Node {
#[inline]
fn insert(&mut self, mut node: NonNull<Node>, buddy: NonNull<Node>) -> bool {
let mut cursor = self;
loop {
if let Some(mut next) = cursor.next {
use core::cmp::Ordering::*;
match next.cmp(&buddy) {
Less => cursor = unsafe { next.as_mut() },
Equal => {
cursor.next = unsafe { next.as_ref().next };
unsafe { node.as_mut() }.next = None;
break false;
}
Greater => {
cursor.next = Some(node);
unsafe { node.as_mut() }.next = Some(next);
break true;
}
}
} else {
cursor.next = Some(node);
unsafe { node.as_mut() }.next = None;
break true;
}
}
}
#[inline]
fn insert_unordered(&mut self, mut node: NonNull<Node>) {
unsafe { node.as_mut() }.next = self.next.replace(node);
}
#[inline]
fn take_any(&mut self) -> Option<NonNull<Node>> {
let root = self.next;
self.next = root.and_then(|node| unsafe { node.as_ref().next });
root
}
}
#[cfg(test)]
mod tests {
use super::*;
#[repr(C, align(16))]
struct TestMemory {
data: [u8; 256],
}
#[test]
fn test_empty() {
let list = LinkedListBuddy::EMPTY;
assert!(list.free_list.next.is_none());
}
#[test]
fn test_init() {
let mut list = LinkedListBuddy::EMPTY;
list.init(3, 0);
assert_eq!(list.order.0, 3);
}
#[test]
fn test_insert_unordered() {
let mut list = LinkedListBuddy::EMPTY;
list.init(4, 0);
let mut memory = TestMemory { data: [0; 256] };
let node_ptr = NonNull::new(memory.data.as_mut_ptr().cast::<Node>()).unwrap();
let idx = list.order.ptr_to_idx(node_ptr);
OligarchyCollection::put(&mut list, idx);
assert!(list.free_list.next.is_some());
}
#[test]
fn test_take_and_put_buddy() {
let mut list = LinkedListBuddy::EMPTY;
list.init(4, 0);
let mut memory = TestMemory { data: [0; 256] };
let node_ptr = NonNull::new(memory.data.as_mut_ptr().cast::<Node>()).unwrap();
let idx = list.order.ptr_to_idx(node_ptr);
OligarchyCollection::put(&mut list, idx);
let taken_idx = OligarchyCollection::take_any(&mut list, 0, 1);
assert_eq!(taken_idx, Some(idx));
}
#[test]
fn test_buddy_merge() {
let mut list = LinkedListBuddy::EMPTY;
list.init(4, 0);
let mut memory = TestMemory { data: [0; 256] };
let base = memory.data.as_mut_ptr() as usize;
let ptr0 = (base + 15) & !15;
let idx0 = ptr0 >> 4;
let idx0 = idx0 & !1; let idx1 = idx0 ^ 1;
OligarchyCollection::put(&mut list, idx0);
let result = BuddyCollection::put(&mut list, idx1);
assert_eq!(result, Some(idx0 >> 1));
}
#[test]
fn test_buddy_collection_take_any() {
let mut list = LinkedListBuddy::EMPTY;
list.init(0, 0);
assert_eq!(BuddyCollection::take_any(&mut list, 0), None);
assert_eq!(BuddyCollection::take_any(&mut list, 1), None);
}
#[test]
fn test_oligarchy_collection_take_any() {
let mut list = LinkedListBuddy::EMPTY;
list.init(0, 0);
assert_eq!(OligarchyCollection::take_any(&mut list, 0, 2), None);
assert_eq!(OligarchyCollection::take_any(&mut list, 1, 1), None);
}
#[test]
fn test_node_insert() {
let mut head = Node { next: None };
let mut memory = TestMemory { data: [0; 256] };
let ptr1 = NonNull::new(memory.data.as_mut_ptr().cast::<Node>()).unwrap();
let ptr2 = NonNull::new(memory.data.as_mut_ptr().wrapping_add(16).cast::<Node>()).unwrap();
let ptr3 = NonNull::new(memory.data.as_mut_ptr().wrapping_add(32).cast::<Node>()).unwrap();
head.insert_unordered(ptr1);
assert!(head.next.is_some());
head.insert_unordered(ptr2);
let buddy = NonNull::new(memory.data.as_mut_ptr().wrapping_add(64).cast::<Node>()).unwrap();
assert!(head.insert(ptr3, buddy));
}
#[test]
fn test_node_take_any() {
let mut head = Node { next: None };
let mut memory = TestMemory { data: [0; 256] };
let ptr = NonNull::new(memory.data.as_mut_ptr().cast::<Node>()).unwrap();
head.insert_unordered(ptr);
let taken = head.take_any();
assert!(taken.is_some());
assert!(head.take_any().is_none());
}
}