Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ impl<const ORDER: usize> FrameAllocator<ORDER> {
self.alloc_power_of_two(size)
}

/// Try to allocate a specific range of frames `[start, start + count)` from the allocator.
///
/// The `count` will be rounded up to the next power of two, same as [`alloc`]. The `start`
/// address must be aligned to this rounded-up size (buddy system invariant).
///
/// Returns `Some(start)` if the range was successfully allocated, or `None` if the range is
/// unavailable (not managed, already allocated, or misaligned).
pub fn alloc_at(&mut self, start: usize, count: usize) -> Option<usize> {
let size = count.next_power_of_two();
self.alloc_at_power_of_two(start, size)
}

/// Allocate a range of frames of the given size from the allocator. The size must be a power of
/// two. The allocated range will have alignment equal to the size.
fn alloc_power_of_two(&mut self, size: usize) -> Option<usize> {
Expand Down Expand Up @@ -130,6 +142,36 @@ impl<const ORDER: usize> FrameAllocator<ORDER> {
None
}

/// Allocate a specific range of frames at the given start address with the given power-of-two
/// size. The start address must be aligned to the size.
fn alloc_at_power_of_two(&mut self, start: usize, size: usize) -> Option<usize> {
let class = size.trailing_zeros() as usize;

if start & (size - 1) != 0 {
return None;
}

for i in class..self.free_list.len() {
let block_start = start & !((1 << i) - 1);
if self.free_list[i].remove(&block_start) {
let mut current_start = block_start;
for j in (class..i).rev() {
let mid = current_start + (1 << j);
if start >= mid {
self.free_list[j].insert(current_start);
current_start = mid;
} else {
self.free_list[j].insert(mid);
}
}
self.allocated += size;
return Some(start);
}
}

None
}

/// Deallocate a range of frames [frame, frame+count) from the frame allocator.
///
/// The range should be exactly the same when it was allocated, as in heap allocator
Expand Down
70 changes: 70 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,73 @@ fn test_heap_merge_final_order() {
heap.dealloc(alloc, layout);
}
}

#[test]
fn test_frame_allocator_alloc_at_basic() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 4);
assert_eq!(frame.alloc_at(0, 4), Some(0));
assert!(frame.alloc(1).is_none());
}

#[test]
fn test_frame_allocator_alloc_at_split() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 8);
// Alloc 2 frames at address 2 (requires splitting the order-3 block)
assert_eq!(frame.alloc_at(2, 2), Some(2));
// Remaining: [0..2) at order 1, [4..8) at order 2
assert_eq!(frame.alloc(2), Some(0));
assert_eq!(frame.alloc(4), Some(4));
assert!(frame.alloc(1).is_none());
}

#[test]
fn test_frame_allocator_alloc_at_unavailable() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 8);
assert_eq!(frame.alloc(4), Some(0));
// [0..4) is allocated, try to alloc_at within it
assert_eq!(frame.alloc_at(0, 2), None);
assert_eq!(frame.alloc_at(2, 2), None);
}

#[test]
fn test_frame_allocator_alloc_at_misaligned() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 16);
// 4 frames at address 3: not aligned to 4
assert_eq!(frame.alloc_at(3, 4), None);
// 2 frames at address 1: not aligned to 2
assert_eq!(frame.alloc_at(1, 2), None);
// 1 frame at address 1: aligned to 1, should work
assert_eq!(frame.alloc_at(1, 1), Some(1));
}

#[test]
fn test_frame_allocator_alloc_at_then_dealloc() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 16);
assert_eq!(frame.alloc_at(4, 4), Some(4));
frame.dealloc(4, 4);
// Buddies should merge back; full 16-frame alloc should succeed
assert_eq!(frame.alloc(16), Some(0));
}

#[test]
fn test_frame_allocator_alloc_at_outside_range() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 8);
assert_eq!(frame.alloc_at(16, 2), None);
}

#[test]
fn test_frame_allocator_alloc_at_multiple() {
let mut frame = FrameAllocator::<32>::new();
frame.add_frame(0, 16);
assert_eq!(frame.alloc_at(0, 4), Some(0));
assert_eq!(frame.alloc_at(4, 4), Some(4));
assert_eq!(frame.alloc_at(8, 4), Some(8));
assert_eq!(frame.alloc_at(12, 4), Some(12));
assert!(frame.alloc(1).is_none());
}
Loading