Skip to content

Commit 5c0b59f

Browse files
committed
Implement allocator::Alloc for Heap and use allocator::Layout
1 parent 07a9205 commit 5c0b59f

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

src/hole.rs

+26-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::ptr::Unique;
22
use core::mem::{self, size_of};
3+
use alloc::allocator::{Layout, AllocErr};
34

45
use super::align_up;
56

@@ -41,14 +42,15 @@ impl HoleList {
4142
}
4243

4344
/// Searches the list for a big enough hole. A hole is big enough if it can hold an allocation
44-
/// of `size` bytes with the given `align`. If such a hole is found in the list, a block of the
45-
/// required size is allocated from it. Then the start address of that block is returned.
45+
/// of `layout.size()` bytes with the given `layout.align()`. If such a hole is found in the
46+
/// list, a block of the required size is allocated from it. Then the start address of that
47+
/// block is returned.
4648
/// This function uses the “first fit” strategy, so it uses the first hole that is big
4749
/// enough. Thus the runtime is in O(n) but it should be reasonably fast for small allocations.
48-
pub fn allocate_first_fit(&mut self, size: usize, align: usize) -> Option<*mut u8> {
49-
assert!(size >= Self::min_size());
50+
pub fn allocate_first_fit(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
51+
assert!(layout.size() >= Self::min_size());
5052

51-
allocate_first_fit(&mut self.first, size, align).map(|allocation| {
53+
allocate_first_fit(&mut self.first, layout).map(|allocation| {
5254
if let Some(padding) = allocation.front_padding {
5355
deallocate(&mut self.first, padding.addr, padding.size);
5456
}
@@ -59,14 +61,14 @@ impl HoleList {
5961
})
6062
}
6163

62-
/// Frees the allocation given by `ptr` and `size`. `ptr` must be a pointer returned by a call
63-
/// to the `allocate_first_fit` function with identical size. Undefined behavior may occur for
64+
/// Frees the allocation given by `ptr` and `layout`. `ptr` must be a pointer returned by a call
65+
/// to the `allocate_first_fit` function with identical layout. Undefined behavior may occur for
6466
/// invalid arguments.
6567
/// This function walks the list and inserts the given block at the correct place. If the freed
6668
/// block is adjacent to another free block, the blocks are merged again.
6769
/// This operation is in `O(n)` since the list needs to be sorted by address.
68-
pub unsafe fn deallocate(&mut self, ptr: *mut u8, size: usize) {
69-
deallocate(&mut self.first, ptr as usize, size)
70+
pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout) {
71+
deallocate(&mut self.first, ptr as usize, layout.size())
7072
}
7173

7274
/// Returns the minimal allocation size. Smaller allocations or deallocations are not allowed.
@@ -125,11 +127,14 @@ struct Allocation {
125127
}
126128

127129
/// Splits the given hole into `(front_padding, hole, back_padding)` if it's big enough to allocate
128-
/// `required_size` bytes with the `required_align`. Else `None` is returned.
130+
/// `required_layout.size()` bytes with the `required_layout.align()`. Else `None` is returned.
129131
/// Front padding occurs if the required alignment is higher than the hole's alignment. Back
130132
/// padding occurs if the required size is smaller than the size of the aligned hole. All padding
131133
/// must be at least `HoleList::min_size()` big or the hole is unusable.
132-
fn split_hole(hole: HoleInfo, required_size: usize, required_align: usize) -> Option<Allocation> {
134+
fn split_hole(hole: HoleInfo, required_layout: Layout) -> Option<Allocation> {
135+
let required_size = required_layout.size();
136+
let required_align = required_layout.align();
137+
133138
let (aligned_addr, front_padding) = if hole.addr == align_up(hole.addr, required_align) {
134139
// hole has already the required alignment
135140
(hole.addr, None)
@@ -179,29 +184,32 @@ fn split_hole(hole: HoleInfo, required_size: usize, required_align: usize) -> Op
179184
}
180185

181186
/// Searches the list starting at the next hole of `previous` for a big enough hole. A hole is big
182-
/// enough if it can hold an allocation of `size` bytes with the given `align`. When a hole is used
183-
/// for an allocation, there may be some needed padding before and/or after the allocation. This
184-
/// padding is returned as part of the `Allocation`. The caller must take care of freeing it again.
187+
/// enough if it can hold an allocation of `layout.size()` bytes with the given `layou.align()`.
188+
/// When a hole is used for an allocation, there may be some needed padding before and/or after
189+
/// the allocation. This padding is returned as part of the `Allocation`. The caller must take
190+
/// care of freeing it again.
185191
/// This function uses the “first fit” strategy, so it breaks as soon as a big enough hole is
186192
/// found (and returns it).
187-
fn allocate_first_fit(mut previous: &mut Hole, size: usize, align: usize) -> Option<Allocation> {
193+
fn allocate_first_fit(mut previous: &mut Hole, layout: Layout) -> Result<Allocation, AllocErr> {
188194
loop {
189195
let allocation: Option<Allocation> = previous.next
190196
.as_mut()
191-
.and_then(|current| split_hole(unsafe { current.as_ref() }.info(), size, align));
197+
.and_then(|current| split_hole(unsafe { current.as_ref() }.info(), layout.clone()));
192198
match allocation {
193199
Some(allocation) => {
194200
// hole is big enough, so remove it from the list by updating the previous pointer
195201
previous.next = previous.next_unwrap().next.take();
196-
return Some(allocation);
202+
return Ok(allocation);
197203
}
198204
None if previous.next.is_some() => {
199205
// try next hole
200206
previous = move_helper(previous).next_unwrap();
201207
}
202208
None => {
203209
// this was the last hole, so no hole is big enough -> allocation not possible
204-
return None;
210+
return Err(AllocErr::Exhausted {
211+
request: layout,
212+
});
205213
}
206214
}
207215
}

src/lib.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
#![feature(unique)]
22
#![feature(const_fn)]
3+
#![feature(alloc, allocator_api)]
34
#![no_std]
45

6+
extern crate alloc;
7+
58
#[cfg(test)]
69
#[macro_use]
710
extern crate std;
811

912
use hole::{Hole, HoleList};
1013
use core::mem;
14+
use alloc::allocator::{Alloc, Layout, AllocErr};
1115

1216
mod hole;
1317
#[cfg(test)]
@@ -59,13 +63,15 @@ impl Heap {
5963
/// This function scans the list of free memory blocks and uses the first block that is big
6064
/// enough. The runtime is in O(n) where n is the number of free blocks, but it should be
6165
/// reasonably fast for small allocations.
62-
pub fn allocate_first_fit(&mut self, mut size: usize, align: usize) -> Option<*mut u8> {
66+
pub fn allocate_first_fit(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
67+
let mut size = layout.size();
6368
if size < HoleList::min_size() {
6469
size = HoleList::min_size();
6570
}
6671
let size = align_up(size, mem::align_of::<Hole>());
72+
let layout = Layout::from_size_align(size, layout.align()).unwrap();
6773

68-
self.holes.allocate_first_fit(size, align)
74+
self.holes.allocate_first_fit(layout)
6975
}
7076

7177
/// Frees the given allocation. `ptr` must be a pointer returned
@@ -75,13 +81,15 @@ impl Heap {
7581
/// This function walks the list of free memory blocks and inserts the freed block at the
7682
/// correct place. If the freed block is adjacent to another free block, the blocks are merged
7783
/// again. This operation is in `O(n)` since the list needs to be sorted by address.
78-
pub unsafe fn deallocate(&mut self, ptr: *mut u8, mut size: usize, _align: usize) {
84+
pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout) {
85+
let mut size = layout.size();
7986
if size < HoleList::min_size() {
8087
size = HoleList::min_size();
8188
}
8289
let size = align_up(size, mem::align_of::<Hole>());
90+
let layout = Layout::from_size_align(size, layout.align()).unwrap();
8391

84-
self.holes.deallocate(ptr, size);
92+
self.holes.deallocate(ptr, layout);
8593
}
8694

8795
/// Returns the bottom address of the heap.
@@ -106,11 +114,22 @@ impl Heap {
106114
/// The new extended area must be valid
107115
pub unsafe fn extend(&mut self, by: usize) {
108116
let top = self.top();
109-
self.holes.deallocate(top as *mut u8, by);
117+
let layout = Layout::from_size_align(by, 1).unwrap();
118+
self.holes.deallocate(top as *mut u8, layout);
110119
self.size += by;
111120
}
112121
}
113122

123+
unsafe impl Alloc for Heap {
124+
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
125+
self.allocate_first_fit(layout)
126+
}
127+
128+
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
129+
self.deallocate(ptr, layout)
130+
}
131+
}
132+
114133
/// Align downwards. Returns the greatest x with alignment `align`
115134
/// so that x <= addr. The alignment must be a power of 2.
116135
pub fn align_down(addr: usize, align: usize) -> usize {

0 commit comments

Comments
 (0)