11use core:: alloc:: Layout ;
2- use core:: convert:: TryInto ;
32use core:: mem;
43use core:: mem:: { align_of, size_of} ;
4+ use core:: ptr:: null_mut;
55use core:: ptr:: NonNull ;
66
77use crate :: align_up_size;
@@ -11,11 +11,27 @@ use super::align_up;
1111/// A sorted list of holes. It uses the the holes itself to store its nodes.
1212pub struct HoleList {
1313 pub ( crate ) first : Hole , // dummy
14+ pub ( crate ) bottom : * mut u8 ,
15+ pub ( crate ) top : * mut u8 ,
1416}
1517
1618pub ( crate ) struct Cursor {
1719 prev : NonNull < Hole > ,
1820 hole : NonNull < Hole > ,
21+ top : * mut u8 ,
22+ }
23+
24+ /// A block containing free memory. It points to the next hole and thus forms a linked list.
25+ pub ( crate ) struct Hole {
26+ pub size : usize ,
27+ pub next : Option < NonNull < Hole > > ,
28+ }
29+
30+ /// Basic information about a hole.
31+ #[ derive( Debug , Clone , Copy ) ]
32+ struct HoleInfo {
33+ addr : * mut u8 ,
34+ size : usize ,
1935}
2036
2137impl Cursor {
@@ -24,6 +40,7 @@ impl Cursor {
2440 self . hole . as_mut ( ) . next . map ( |nhole| Cursor {
2541 prev : self . hole ,
2642 hole : nhole,
43+ top : self . top ,
2744 } )
2845 }
2946 }
@@ -133,7 +150,9 @@ impl Cursor {
133150 ////////////////////////////////////////////////////////////////////////////
134151 // This is where we actually perform surgery on the linked list.
135152 ////////////////////////////////////////////////////////////////////////////
136- let Cursor { mut prev, mut hole } = self ;
153+ let Cursor {
154+ mut prev, mut hole, ..
155+ } = self ;
137156 // Remove the current location from the previous node
138157 unsafe {
139158 prev. as_mut ( ) . next = None ;
@@ -200,6 +219,41 @@ impl Cursor {
200219 }
201220}
202221
222+ // See if we can extend this hole towards the end of the allocation region
223+ // If so: increase the size of the node. If no: keep the node as-is
224+ fn check_merge_top ( mut node : NonNull < Hole > , top : * mut u8 ) {
225+ let node_u8 = node. as_ptr ( ) . cast :: < u8 > ( ) ;
226+ let node_sz = unsafe { node. as_ref ( ) . size } ;
227+
228+ // If this is the last node, we need to see if we need to merge to the end
229+ let end = node_u8. wrapping_add ( node_sz) ;
230+ let hole_layout = Layout :: new :: < Hole > ( ) ;
231+ if end < top {
232+ let next_hole_end = align_up ( end, hole_layout. align ( ) ) . wrapping_add ( hole_layout. size ( ) ) ;
233+
234+ if next_hole_end > top {
235+ let offset = ( top as usize ) - ( end as usize ) ;
236+ unsafe {
237+ node. as_mut ( ) . size += offset;
238+ }
239+ }
240+ }
241+ }
242+
243+ // See if we can scoot this hole back to the bottom of the allocation region
244+ // If so: create and return the new hole. If not: return the existing hole
245+ fn check_merge_bottom ( node : NonNull < Hole > , bottom : * mut u8 ) -> NonNull < Hole > {
246+ debug_assert_eq ! ( bottom as usize % align_of:: <Hole >( ) , 0 ) ;
247+
248+ if bottom. wrapping_add ( core:: mem:: size_of :: < Hole > ( ) ) > node. as_ptr ( ) . cast :: < u8 > ( ) {
249+ let offset = ( node. as_ptr ( ) as usize ) - ( bottom as usize ) ;
250+ let size = unsafe { node. as_ref ( ) } . size + offset;
251+ unsafe { make_hole ( bottom, size) }
252+ } else {
253+ node
254+ }
255+ }
256+
203257impl HoleList {
204258 /// Creates an empty `HoleList`.
205259 #[ cfg( not( feature = "const_mut_refs" ) ) ]
@@ -209,6 +263,8 @@ impl HoleList {
209263 size : 0 ,
210264 next : None ,
211265 } ,
266+ bottom : null_mut ( ) ,
267+ top : null_mut ( ) ,
212268 }
213269 }
214270
@@ -220,6 +276,8 @@ impl HoleList {
220276 size : 0 ,
221277 next : None ,
222278 } ,
279+ bottom : null_mut ( ) ,
280+ top : null_mut ( ) ,
223281 }
224282 }
225283
@@ -228,6 +286,7 @@ impl HoleList {
228286 Some ( Cursor {
229287 hole,
230288 prev : NonNull :: new ( & mut self . first ) ?,
289+ top : self . top ,
231290 } )
232291 } else {
233292 None
@@ -274,8 +333,7 @@ impl HoleList {
274333 let aligned_hole_addr = align_up ( hole_addr, align_of :: < Hole > ( ) ) ;
275334 let ptr = aligned_hole_addr as * mut Hole ;
276335 ptr. write ( Hole {
277- size : hole_size
278- . saturating_sub ( aligned_hole_addr. offset_from ( hole_addr) . try_into ( ) . unwrap ( ) ) ,
336+ size : hole_size - ( ( aligned_hole_addr as usize ) - ( hole_addr as usize ) ) ,
279337 next : None ,
280338 } ) ;
281339
@@ -284,6 +342,8 @@ impl HoleList {
284342 size : 0 ,
285343 next : Some ( NonNull :: new_unchecked ( ptr) ) ,
286344 } ,
345+ bottom : aligned_hole_addr,
346+ top : hole_addr. wrapping_add ( hole_size) ,
287347 }
288348 }
289349
@@ -370,19 +430,6 @@ impl HoleList {
370430 }
371431}
372432
373- /// A block containing free memory. It points to the next hole and thus forms a linked list.
374- pub ( crate ) struct Hole {
375- pub size : usize ,
376- pub next : Option < NonNull < Hole > > ,
377- }
378-
379- /// Basic information about a hole.
380- #[ derive( Debug , Clone , Copy ) ]
381- struct HoleInfo {
382- addr : * mut u8 ,
383- size : usize ,
384- }
385-
386433unsafe fn make_hole ( addr : * mut u8 , size : usize ) -> NonNull < Hole > {
387434 let hole_addr = addr. cast :: < Hole > ( ) ;
388435 debug_assert_eq ! (
@@ -395,7 +442,7 @@ unsafe fn make_hole(addr: *mut u8, size: usize) -> NonNull<Hole> {
395442}
396443
397444impl Cursor {
398- fn try_insert_back ( self , mut node : NonNull < Hole > ) -> Result < Self , Self > {
445+ fn try_insert_back ( self , node : NonNull < Hole > , bottom : * mut u8 ) -> Result < Self , Self > {
399446 // Covers the case where the new hole exists BEFORE the current pointer,
400447 // which only happens when previous is the stub pointer
401448 if node < self . hole {
@@ -409,59 +456,86 @@ impl Cursor {
409456 ) ;
410457 debug_assert_eq ! ( self . previous( ) . size, 0 ) ;
411458
412- let Cursor { mut prev, hole } = self ;
459+ let Cursor {
460+ mut prev,
461+ hole,
462+ top,
463+ } = self ;
413464 unsafe {
465+ let mut node = check_merge_bottom ( node, bottom) ;
414466 prev. as_mut ( ) . next = Some ( node) ;
415467 node. as_mut ( ) . next = Some ( hole) ;
416468 }
417- Ok ( Cursor { prev, hole : node } )
469+ Ok ( Cursor {
470+ prev,
471+ hole : node,
472+ top,
473+ } )
418474 } else {
419475 Err ( self )
420476 }
421477 }
422478
423479 fn try_insert_after ( & mut self , mut node : NonNull < Hole > ) -> Result < ( ) , ( ) > {
424- if self . hole < node {
425- let node_u8 = node. as_ptr ( ) . cast :: < u8 > ( ) ;
426- let node_size = unsafe { node. as_ref ( ) . size } ;
427- let hole_u8 = self . hole . as_ptr ( ) . cast :: < u8 > ( ) ;
428- let hole_size = self . current ( ) . size ;
480+ let node_u8 = node. as_ptr ( ) . cast :: < u8 > ( ) ;
481+ let node_size = unsafe { node. as_ref ( ) . size } ;
429482
430- // Does hole overlap node?
431- assert ! (
432- hole_u8. wrapping_add( hole_size) <= node_u8,
433- "Freed node aliases existing hole! Bad free?" ,
434- ) ;
435-
436- // If we have a next, does the node overlap next?
437- if let Some ( next) = self . current ( ) . next . as_ref ( ) {
483+ // If we have a next, does the node overlap next?
484+ if let Some ( next) = self . current ( ) . next . as_ref ( ) {
485+ if node < * next {
438486 let node_u8 = node_u8 as * const u8 ;
439487 assert ! (
440488 node_u8. wrapping_add( node_size) <= next. as_ptr( ) . cast:: <u8 >( ) ,
441489 "Freed node aliases existing hole! Bad free?" ,
442490 ) ;
491+ } else {
492+ // The new hole isn't between current and next.
493+ return Err ( ( ) ) ;
443494 }
495+ }
444496
445- // All good! Let's insert that after.
446- unsafe {
447- let maybe_next = self . hole . as_mut ( ) . next . replace ( node) ;
448- node. as_mut ( ) . next = maybe_next;
449- }
450- Ok ( ( ) )
451- } else {
452- Err ( ( ) )
497+ // At this point, we either have no "next" pointer, or the hole is
498+ // between current and "next". The following assert can only trigger
499+ // if we've gotten our list out of order.
500+ debug_assert ! ( self . hole < node, "Hole list out of order?" ) ;
501+
502+ let hole_u8 = self . hole . as_ptr ( ) . cast :: < u8 > ( ) ;
503+ let hole_size = self . current ( ) . size ;
504+
505+ // Does hole overlap node?
506+ assert ! (
507+ hole_u8. wrapping_add( hole_size) <= node_u8,
508+ "Freed node aliases existing hole! Bad free?" ,
509+ ) ;
510+
511+ // All good! Let's insert that after.
512+ unsafe {
513+ let maybe_next = self . hole . as_mut ( ) . next . replace ( node) ;
514+ node. as_mut ( ) . next = maybe_next;
453515 }
516+
517+ Ok ( ( ) )
454518 }
455519
456520 // Merge the current node with up to n following nodes
457521 fn try_merge_next_n ( self , max : usize ) {
458- let Cursor { prev : _, mut hole } = self ;
522+ let Cursor {
523+ prev : _,
524+ mut hole,
525+ top,
526+ ..
527+ } = self ;
459528
460529 for _ in 0 ..max {
461530 // Is there a next node?
462531 let mut next = if let Some ( next) = unsafe { hole. as_mut ( ) } . next . as_ref ( ) {
463532 * next
464533 } else {
534+ // Since there is no NEXT node, we need to check whether the current
535+ // hole SHOULD extend to the end, but doesn't. This would happen when
536+ // there isn't enough remaining space to place a hole after the current
537+ // node's placement.
538+ check_merge_top ( hole, top) ;
465539 return ;
466540 } ;
467541
@@ -515,7 +589,10 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
515589 cursor
516590 } else {
517591 // Oh hey, there are no "real" holes at all. That means this just
518- // becomes the only "real" hole!
592+ // becomes the only "real" hole! Check if this is touching the end
593+ // or the beginning of the allocation range
594+ let hole = check_merge_bottom ( hole, list. bottom ) ;
595+ check_merge_top ( hole, list. top ) ;
519596 list. first . next = Some ( hole) ;
520597 return ;
521598 } ;
@@ -525,7 +602,7 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
525602 // previous location the cursor was pointing to.
526603 //
527604 // Otherwise, our cursor will point at the current non-"dummy" head of the list
528- let ( cursor, n) = match cursor. try_insert_back ( hole) {
605+ let ( cursor, n) = match cursor. try_insert_back ( hole, list . bottom ) {
529606 Ok ( cursor) => {
530607 // Yup! It lives at the front of the list. Hooray! Attempt to merge
531608 // it with just ONE next node, since it is at the front of the list
@@ -578,8 +655,8 @@ pub mod test {
578655 let assumed_location = data. as_mut_ptr ( ) . cast ( ) ;
579656
580657 let heap = Heap :: from_slice ( data) ;
581- assert ! ( heap. bottom == assumed_location) ;
582- assert ! ( heap. size == HEAP_SIZE ) ;
658+ assert ! ( heap. bottom( ) == assumed_location) ;
659+ assert ! ( heap. size( ) == HEAP_SIZE ) ;
583660 heap
584661 }
585662
0 commit comments