1
1
use core:: ptr:: Unique ;
2
2
use core:: mem:: { self , size_of} ;
3
+ use alloc:: allocator:: { Layout , AllocErr } ;
3
4
4
5
use super :: align_up;
5
6
@@ -26,11 +27,13 @@ impl HoleList {
26
27
assert ! ( size_of:: <Hole >( ) == Self :: min_size( ) ) ;
27
28
28
29
let ptr = hole_addr as * mut Hole ;
29
- mem:: replace ( & mut * ptr,
30
- Hole {
31
- size : hole_size,
32
- next : None ,
33
- } ) ;
30
+ mem:: replace (
31
+ & mut * ptr,
32
+ Hole {
33
+ size : hole_size,
34
+ next : None ,
35
+ } ,
36
+ ) ;
34
37
35
38
HoleList {
36
39
first : Hole {
@@ -41,14 +44,15 @@ impl HoleList {
41
44
}
42
45
43
46
/// 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.
47
+ /// of `layout.size()` bytes with the given `layout.align()`. If such a hole is found in the
48
+ /// list, a block of the required size is allocated from it. Then the start address of that
49
+ /// block is returned.
46
50
/// This function uses the “first fit” strategy, so it uses the first hole that is big
47
51
/// 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( ) ) ;
52
+ pub fn allocate_first_fit ( & mut self , layout : Layout ) -> Result < * mut u8 , AllocErr > {
53
+ assert ! ( layout . size( ) >= Self :: min_size( ) ) ;
50
54
51
- allocate_first_fit ( & mut self . first , size , align ) . map ( |allocation| {
55
+ allocate_first_fit ( & mut self . first , layout ) . map ( |allocation| {
52
56
if let Some ( padding) = allocation. front_padding {
53
57
deallocate ( & mut self . first , padding. addr , padding. size ) ;
54
58
}
@@ -59,14 +63,14 @@ impl HoleList {
59
63
} )
60
64
}
61
65
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
66
+ /// Frees the allocation given by `ptr` and `layout `. `ptr` must be a pointer returned by a call
67
+ /// to the `allocate_first_fit` function with identical layout . Undefined behavior may occur for
64
68
/// invalid arguments.
65
69
/// This function walks the list and inserts the given block at the correct place. If the freed
66
70
/// block is adjacent to another free block, the blocks are merged again.
67
71
/// 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)
72
+ pub unsafe fn deallocate ( & mut self , ptr : * mut u8 , layout : Layout ) {
73
+ deallocate ( & mut self . first , ptr as usize , layout . size ( ) )
70
74
}
71
75
72
76
/// Returns the minimal allocation size. Smaller allocations or deallocations are not allowed.
@@ -77,7 +81,9 @@ impl HoleList {
77
81
/// Returns information about the first hole for test purposes.
78
82
#[ cfg( test) ]
79
83
pub fn first_hole ( & self ) -> Option < ( usize , usize ) > {
80
- self . first . next . as_ref ( ) . map ( |hole| ( hole. as_ptr ( ) as usize , unsafe { hole. as_ref ( ) . size } ) )
84
+ self . first . next . as_ref ( ) . map ( |hole| {
85
+ ( hole. as_ptr ( ) as usize , unsafe { hole. as_ref ( ) . size } )
86
+ } )
81
87
}
82
88
}
83
89
@@ -125,22 +131,27 @@ struct Allocation {
125
131
}
126
132
127
133
/// 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.
134
+ /// `required_layout.size() ` bytes with the `required_layout.align() `. Else `None` is returned.
129
135
/// Front padding occurs if the required alignment is higher than the hole's alignment. Back
130
136
/// padding occurs if the required size is smaller than the size of the aligned hole. All padding
131
137
/// 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 > {
138
+ fn split_hole ( hole : HoleInfo , required_layout : Layout ) -> Option < Allocation > {
139
+ let required_size = required_layout. size ( ) ;
140
+ let required_align = required_layout. align ( ) ;
141
+
133
142
let ( aligned_addr, front_padding) = if hole. addr == align_up ( hole. addr , required_align) {
134
143
// hole has already the required alignment
135
144
( hole. addr , None )
136
145
} else {
137
146
// the required alignment causes some padding before the allocation
138
147
let aligned_addr = align_up ( hole. addr + HoleList :: min_size ( ) , required_align) ;
139
- ( aligned_addr,
140
- Some ( HoleInfo {
141
- addr : hole. addr ,
142
- size : aligned_addr - hole. addr ,
143
- } ) )
148
+ (
149
+ aligned_addr,
150
+ Some ( HoleInfo {
151
+ addr : hole. addr ,
152
+ size : aligned_addr - hole. addr ,
153
+ } ) ,
154
+ )
144
155
} ;
145
156
146
157
let aligned_hole = {
@@ -179,29 +190,30 @@ fn split_hole(hole: HoleInfo, required_size: usize, required_align: usize) -> Op
179
190
}
180
191
181
192
/// 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.
193
+ /// enough if it can hold an allocation of `layout.size()` bytes with the given `layou.align()`.
194
+ /// When a hole is used for an allocation, there may be some needed padding before and/or after
195
+ /// the allocation. This padding is returned as part of the `Allocation`. The caller must take
196
+ /// care of freeing it again.
185
197
/// This function uses the “first fit” strategy, so it breaks as soon as a big enough hole is
186
198
/// found (and returns it).
187
- fn allocate_first_fit ( mut previous : & mut Hole , size : usize , align : usize ) -> Option < Allocation > {
199
+ fn allocate_first_fit ( mut previous : & mut Hole , layout : Layout ) -> Result < Allocation , AllocErr > {
188
200
loop {
189
- let allocation: Option < Allocation > = previous. next
190
- . as_mut ( )
191
- . and_then ( |current| split_hole ( unsafe { current . as_ref ( ) } . info ( ) , size , align ) ) ;
201
+ let allocation: Option < Allocation > = previous. next . as_mut ( ) . and_then ( |current| {
202
+ split_hole ( unsafe { current . as_ref ( ) } . info ( ) , layout . clone ( ) )
203
+ } ) ;
192
204
match allocation {
193
205
Some ( allocation) => {
194
206
// hole is big enough, so remove it from the list by updating the previous pointer
195
207
previous. next = previous. next_unwrap ( ) . next . take ( ) ;
196
- return Some ( allocation) ;
208
+ return Ok ( allocation) ;
197
209
}
198
210
None if previous. next . is_some ( ) => {
199
211
// try next hole
200
212
previous = move_helper ( previous) . next_unwrap ( ) ;
201
213
}
202
214
None => {
203
215
// this was the last hole, so no hole is big enough -> allocation not possible
204
- return None ;
216
+ return Err ( AllocErr :: Exhausted { request : layout } ) ;
205
217
}
206
218
}
207
219
}
@@ -225,11 +237,15 @@ fn deallocate(mut hole: &mut Hole, addr: usize, mut size: usize) {
225
237
226
238
// Each freed block must be handled by the previous hole in memory. Thus the freed
227
239
// address must be always behind the current hole.
228
- assert ! ( hole_addr + hole. size <= addr,
229
- "invalid deallocation (probably a double free)" ) ;
240
+ assert ! (
241
+ hole_addr + hole. size <= addr,
242
+ "invalid deallocation (probably a double free)"
243
+ ) ;
230
244
231
245
// get information about the next block
232
- let next_hole_info = hole. next . as_ref ( ) . map ( |next| unsafe { next. as_ref ( ) . info ( ) } ) ;
246
+ let next_hole_info = hole. next
247
+ . as_ref ( )
248
+ . map ( |next| unsafe { next. as_ref ( ) . info ( ) } ) ;
233
249
234
250
match next_hole_info {
235
251
Some ( next) if hole_addr + hole. size == addr && addr + size == next. addr => {
0 commit comments