Skip to content

Commit 3a25d3d

Browse files
committed
Make it possible to extend the size of the heap
Add a max_size parameter to the heap. Allocations exceeding the size of the heap will fail, but one can extend the size of the heap with the call to extend. That allows heap to be more memory efficient, consuming more memory only when necessary, eg. os allocator might map additional virtual memory when the allocation fails.
1 parent ea8548d commit 3a25d3d

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

src/hole.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,38 @@ impl HoleList {
4040
}
4141
}
4242

43+
/// Places a hole of a given size at a given address after a given hole
44+
unsafe fn put_hole(hole: &mut Hole, addr: usize, size: usize) {
45+
mem::forget(mem::replace(&mut *(addr as *mut Hole),
46+
Hole {
47+
size: size,
48+
next: None,
49+
}));
50+
hole.next = Some(Unique::new(addr as *mut Hole));
51+
}
52+
53+
/// Extends the size of the list by increasing the size of the last hole
54+
/// or appending new hole at the end of the list
55+
pub fn extend(&mut self, addr: usize, size: usize) {
56+
let mut last: &mut Hole = &mut self.first;
57+
58+
// Find the last hole in the list
59+
while !last.next.is_none() {
60+
last = move_helper(last).next_unwrap();
61+
}
62+
63+
if last.size == 0 || (last as *const _ as usize) + last.size != addr {
64+
// Create new hole at the end of the list if the list is empty
65+
// or if after last hole there is some allocated memory
66+
unsafe {
67+
HoleList::put_hole(last, addr, size);
68+
}
69+
} else {
70+
// Last hole reaches the end of the heap so just increase its size
71+
last.size += size;
72+
}
73+
}
74+
4375
/// Searches the list for a big enough hole. A hole is big enough if it can hold an allocation
4476
/// of `size` bytes with the given `align`. If such a hole is found in the list, a block of the
4577
/// required size is allocated from it. Then the start address of that block is returned.

src/lib.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod test;
1717
pub struct Heap {
1818
bottom: usize,
1919
size: usize,
20+
max_size: usize,
2021
holes: HoleList,
2122
}
2223

@@ -26,6 +27,7 @@ impl Heap {
2627
Heap {
2728
bottom: 0,
2829
size: 0,
30+
max_size: 0,
2931
holes: HoleList::empty(),
3032
}
3133
}
@@ -39,6 +41,7 @@ impl Heap {
3941
pub unsafe fn init(&mut self, heap_bottom: usize, heap_size: usize) {
4042
self.bottom = heap_bottom;
4143
self.size = heap_size;
44+
self.max_size = heap_size;
4245
self.holes = HoleList::new(heap_bottom, heap_size);
4346
}
4447

@@ -50,6 +53,22 @@ impl Heap {
5053
Heap {
5154
bottom: heap_bottom,
5255
size: heap_size,
56+
max_size: heap_size,
57+
holes: HoleList::new(heap_bottom, heap_size),
58+
}
59+
}
60+
61+
/// Creates a new heap with the given `bottom` and `size` and `max_size`. The bottom address must be valid
62+
/// and the memory in the `[heap_bottom, heap_bottom + heap_size)` range must not be used for
63+
/// anything else. This function is unsafe because it can cause undefined behavior if the
64+
/// given address is invalid.
65+
/// Heap size may be extended up to the maximum size with the call to extent
66+
pub unsafe fn new_max(heap_bottom: usize, heap_size: usize, heap_max_size: usize) -> Heap {
67+
assert!(heap_size <= heap_max_size);
68+
Heap {
69+
bottom: heap_bottom,
70+
size: heap_size,
71+
max_size: heap_max_size,
5372
holes: HoleList::new(heap_bottom, heap_size),
5473
}
5574
}
@@ -93,6 +112,34 @@ impl Heap {
93112
pub fn size(&self) -> usize {
94113
self.size
95114
}
115+
116+
/// Return the maximum size of the heap
117+
pub fn max_size(&self) -> usize {
118+
self.max_size
119+
}
120+
121+
/// Return the top address of the heap
122+
pub fn top(&self) -> usize {
123+
self.bottom + self.size
124+
}
125+
126+
/// Returns the maximum top address of the heap
127+
pub fn max_top(&self) -> usize {
128+
self.bottom + self.max_size
129+
}
130+
131+
/// Extends the size of the heap if it won't exceed max_size
132+
/// Returns true if the call succeeded
133+
pub fn extend(&mut self, by: usize) -> bool {
134+
let top = self.top();
135+
if self.size + by <= self.max_size {
136+
self.holes.extend(top, by);
137+
self.size += by;
138+
true
139+
} else {
140+
false
141+
}
142+
}
96143
}
97144

98145
/// Align downwards. Returns the greatest x with alignment `align`

src/test.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ fn new_heap() -> Heap {
1212
heap
1313
}
1414

15+
fn new_max_heap() -> Heap {
16+
const HEAP_SIZE: usize = 1024;
17+
const HEAP_SIZE_MAX: usize = 2048;
18+
let heap_space = Box::into_raw(Box::new([0u8; HEAP_SIZE_MAX]));
19+
20+
let heap = unsafe { Heap::new_max(heap_space as usize, HEAP_SIZE, HEAP_SIZE_MAX) };
21+
assert!(heap.bottom == heap_space as usize);
22+
assert!(heap.size == HEAP_SIZE);
23+
assert!(heap.max_size == HEAP_SIZE_MAX);
24+
heap
25+
}
26+
1527
#[test]
1628
fn empty() {
1729
let mut heap = Heap::empty();
@@ -201,3 +213,45 @@ fn align_from_small_to_big() {
201213
// try to allocate a 8 byte aligned block
202214
assert!(heap.allocate_first_fit(8, 8).is_some());
203215
}
216+
217+
#[test]
218+
fn extend_empty_heap() {
219+
let mut heap = new_max_heap();
220+
221+
assert!(heap.extend(1024));
222+
223+
// Try to allocate full heap after extend
224+
assert!(heap.allocate_first_fit(2048, 1).is_some());
225+
}
226+
227+
#[test]
228+
fn extend_full_heap() {
229+
let mut heap = new_max_heap();
230+
231+
// Allocate full heap, extend and allocate again to the max
232+
assert!(heap.allocate_first_fit(1024, 1).is_some());
233+
assert!(heap.extend(1024));
234+
assert!(heap.allocate_first_fit(1024, 1).is_some());
235+
}
236+
237+
#[test]
238+
fn extend_fragmented_heap() {
239+
let mut heap = new_max_heap();
240+
241+
let alloc1 = heap.allocate_first_fit(512, 1);
242+
let alloc2 = heap.allocate_first_fit(512, 1);
243+
244+
assert!(alloc1.is_some());
245+
assert!(alloc2.is_some());
246+
247+
unsafe {
248+
// Create a hole at the beginning of the heap
249+
heap.deallocate(alloc1.unwrap(), 512, 1);
250+
}
251+
252+
assert!(heap.extend(1024));
253+
254+
// We got additional 1024 bytes hole at the end of the heap
255+
// Try to allocate there
256+
assert!(heap.allocate_first_fit(1024, 1).is_some());
257+
}

0 commit comments

Comments
 (0)