@@ -83,9 +83,7 @@ impl Cursor {
8383 // it will just have a smaller size after we have chopped off the "tail" for
8484 // the allocation.
8585 addr : hole_addr_u8,
86- size : unsafe { aligned_addr. offset_from ( hole_addr_u8) }
87- . try_into ( )
88- . unwrap ( ) ,
86+ size : ( aligned_addr as usize ) - ( hole_addr_u8 as usize ) ,
8987 } ) ;
9088 aligned_addr
9189 } ;
@@ -109,24 +107,25 @@ impl Cursor {
109107 // sort of assume that the allocation is actually a bit larger than it
110108 // actually needs to be.
111109 //
112- // TODO(AJM): Write a test for this - does the code actually handle this
113- // correctly? Do we need to check the delta is less than an aligned hole
114- // size?
110+ // NOTE: Because we always use `HoleList::align_layout`, the size of
111+ // the new allocation is always "rounded up" to cover any partial gaps that
112+ // would have occurred. For this reason, we DON'T need to "round up"
113+ // to account for an unaligned hole spot.
115114 let hole_layout = Layout :: new :: < Hole > ( ) ;
116115 let back_padding_start = align_up ( allocation_end, hole_layout. align ( ) ) ;
117116 let back_padding_end = back_padding_start. wrapping_add ( hole_layout. size ( ) ) ;
118117
119118 // Will the proposed new back padding actually fit in the old hole slot?
120119 back_padding = if back_padding_end <= hole_end {
121- // Yes, it does!
120+ // Yes, it does! Place a back padding node
122121 Some ( HoleInfo {
123122 addr : back_padding_start,
124- size : unsafe { hole_end. offset_from ( back_padding_start) }
125- . try_into ( )
126- . unwrap ( ) ,
123+ size : ( hole_end as usize ) - ( back_padding_start as usize ) ,
127124 } )
128125 } else {
129- // No, it does not.
126+ // No, it does not. We are now pretending the allocation now
127+ // holds the extra 0..size_of::<Hole>() bytes that are not
128+ // big enough to hold what SHOULD be back_padding
130129 None
131130 } ;
132131 }
@@ -236,6 +235,7 @@ impl HoleList {
236235 }
237236
238237 #[ cfg( test) ]
238+ #[ allow( dead_code) ]
239239 pub ( crate ) fn debug ( & mut self ) {
240240 if let Some ( cursor) = self . cursor ( ) {
241241 let mut cursor = cursor;
@@ -261,11 +261,11 @@ impl HoleList {
261261
262262 /// Creates a `HoleList` that contains the given hole.
263263 ///
264- /// ## Safety
264+ /// # Safety
265265 ///
266- /// This function is unsafe because it
267- /// creates a hole at the given `hole_addr`. This can cause undefined behavior if this address
268- /// is invalid or if memory from the `[hole_addr, hole_addr+size)` range is used somewhere else.
266+ /// This function is unsafe because it creates a hole at the given `hole_addr`.
267+ /// This can cause undefined behavior if this address is invalid or if memory from the
268+ /// `[hole_addr, hole_addr+size)` range is used somewhere else.
269269 ///
270270 /// The pointer to `hole_addr` is automatically aligned.
271271 pub unsafe fn new ( hole_addr : * mut u8 , hole_size : usize ) -> HoleList {
@@ -314,6 +314,10 @@ impl HoleList {
314314 ///
315315 /// This function uses the “first fit” strategy, so it uses the first hole that is big
316316 /// enough. Thus the runtime is in O(n) but it should be reasonably fast for small allocations.
317+ //
318+ // NOTE: We could probably replace this with an `Option` instead of a `Result` in a later
319+ // release to remove this clippy warning
320+ #[ allow( clippy:: result_unit_err) ]
317321 pub fn allocate_first_fit ( & mut self , layout : Layout ) -> Result < ( NonNull < u8 > , Layout ) , ( ) > {
318322 let aligned_layout = Self :: align_layout ( layout) ;
319323 let mut cursor = self . cursor ( ) . ok_or ( ( ) ) ?;
@@ -332,16 +336,18 @@ impl HoleList {
332336
333337 /// Frees the allocation given by `ptr` and `layout`.
334338 ///
335- /// `ptr` must be a pointer returned by a call to the [`allocate_first_fit`] function with
336- /// identical layout. Undefined behavior may occur for invalid arguments.
337- /// The function performs exactly the same layout adjustments as [`allocate_first_fit`] and
338- /// returns the aligned layout.
339- ///
340339 /// This function walks the list and inserts the given block at the correct place. If the freed
341340 /// block is adjacent to another free block, the blocks are merged again.
342341 /// This operation is in `O(n)` since the list needs to be sorted by address.
343342 ///
344343 /// [`allocate_first_fit`]: HoleList::allocate_first_fit
344+ ///
345+ /// # Safety
346+ ///
347+ /// `ptr` must be a pointer returned by a call to the [`allocate_first_fit`] function with
348+ /// identical layout. Undefined behavior may occur for invalid arguments.
349+ /// The function performs exactly the same layout adjustments as [`allocate_first_fit`] and
350+ /// returns the aligned layout.
345351 pub unsafe fn deallocate ( & mut self , ptr : NonNull < u8 > , layout : Layout ) -> Layout {
346352 let aligned_layout = Self :: align_layout ( layout) ;
347353 deallocate ( self , ptr. as_ptr ( ) , aligned_layout. size ( ) ) ;
@@ -379,7 +385,11 @@ struct HoleInfo {
379385
380386unsafe fn make_hole ( addr : * mut u8 , size : usize ) -> NonNull < Hole > {
381387 let hole_addr = addr. cast :: < Hole > ( ) ;
382- debug_assert_eq ! ( addr as usize % align_of:: <Hole >( ) , 0 ) ;
388+ debug_assert_eq ! (
389+ addr as usize % align_of:: <Hole >( ) ,
390+ 0 ,
391+ "Hole address not aligned!" ,
392+ ) ;
383393 hole_addr. write ( Hole { size, next : None } ) ;
384394 NonNull :: new_unchecked ( hole_addr)
385395}
@@ -393,8 +403,11 @@ impl Cursor {
393403 let node_size = unsafe { node. as_ref ( ) . size } ;
394404 let hole_u8 = self . hole . as_ptr ( ) . cast :: < u8 > ( ) ;
395405
396- assert ! ( node_u8. wrapping_add( node_size) <= hole_u8) ;
397- assert_eq ! ( self . previous( ) . size, 0 ) ;
406+ assert ! (
407+ node_u8. wrapping_add( node_size) <= hole_u8,
408+ "Freed node aliases existing hole! Bad free?" ,
409+ ) ;
410+ debug_assert_eq ! ( self . previous( ) . size, 0 ) ;
398411
399412 let Cursor { mut prev, hole } = self ;
400413 unsafe {
@@ -415,12 +428,18 @@ impl Cursor {
415428 let hole_size = self . current ( ) . size ;
416429
417430 // Does hole overlap node?
418- assert ! ( hole_u8. wrapping_add( hole_size) <= node_u8) ;
431+ assert ! (
432+ hole_u8. wrapping_add( hole_size) <= node_u8,
433+ "Freed node aliases existing hole! Bad free?" ,
434+ ) ;
419435
420436 // If we have a next, does the node overlap next?
421437 if let Some ( next) = self . current ( ) . next . as_ref ( ) {
422438 let node_u8 = node_u8 as * const u8 ;
423- assert ! ( node_u8. wrapping_add( node_size) <= next. as_ptr( ) . cast:: <u8 >( ) )
439+ assert ! (
440+ node_u8. wrapping_add( node_size) <= next. as_ptr( ) . cast:: <u8 >( ) ,
441+ "Freed node aliases existing hole! Bad free?" ,
442+ ) ;
424443 }
425444
426445 // All good! Let's insert that after.
@@ -447,6 +466,11 @@ impl Cursor {
447466 } ;
448467
449468 // Can we directly merge these? e.g. are they touching?
469+ //
470+ // NOTE: Because we always use `HoleList::align_layout`, the size of
471+ // the new hole is always "rounded up" to cover any partial gaps that
472+ // would have occurred. For this reason, we DON'T need to "round up"
473+ // to account for an unaligned hole spot.
450474 let hole_u8 = hole. as_ptr ( ) . cast :: < u8 > ( ) ;
451475 let hole_sz = unsafe { hole. as_ref ( ) . size } ;
452476 let next_u8 = next. as_ptr ( ) . cast :: < u8 > ( ) ;
@@ -510,7 +534,9 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
510534 Err ( mut cursor) => {
511535 // Nope. It lives somewhere else. Advance the list until we find its home
512536 while let Err ( ( ) ) = cursor. try_insert_after ( hole) {
513- cursor = cursor. next ( ) . unwrap ( ) ;
537+ cursor = cursor
538+ . next ( )
539+ . expect ( "Reached end of holes without finding deallocation hole!" ) ;
514540 }
515541 // Great! We found a home for it, our cursor is now JUST BEFORE the new
516542 // node we inserted, so we need to try to merge up to twice: One to combine
0 commit comments