1
1
use core:: alloc:: Layout ;
2
- use core:: convert:: TryInto ;
3
2
use core:: mem;
4
3
use core:: mem:: { align_of, size_of} ;
4
+ use core:: ptr:: null_mut;
5
5
use core:: ptr:: NonNull ;
6
6
7
7
use crate :: align_up_size;
@@ -11,11 +11,27 @@ use super::align_up;
11
11
/// A sorted list of holes. It uses the the holes itself to store its nodes.
12
12
pub struct HoleList {
13
13
pub ( crate ) first : Hole , // dummy
14
+ pub ( crate ) bottom : * mut u8 ,
15
+ pub ( crate ) top : * mut u8 ,
14
16
}
15
17
16
18
pub ( crate ) struct Cursor {
17
19
prev : NonNull < Hole > ,
18
20
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 ,
19
35
}
20
36
21
37
impl Cursor {
@@ -24,6 +40,7 @@ impl Cursor {
24
40
self . hole . as_mut ( ) . next . map ( |nhole| Cursor {
25
41
prev : self . hole ,
26
42
hole : nhole,
43
+ top : self . top ,
27
44
} )
28
45
}
29
46
}
@@ -133,7 +150,9 @@ impl Cursor {
133
150
////////////////////////////////////////////////////////////////////////////
134
151
// This is where we actually perform surgery on the linked list.
135
152
////////////////////////////////////////////////////////////////////////////
136
- let Cursor { mut prev, mut hole } = self ;
153
+ let Cursor {
154
+ mut prev, mut hole, ..
155
+ } = self ;
137
156
// Remove the current location from the previous node
138
157
unsafe {
139
158
prev. as_mut ( ) . next = None ;
@@ -200,6 +219,41 @@ impl Cursor {
200
219
}
201
220
}
202
221
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
+
203
257
impl HoleList {
204
258
/// Creates an empty `HoleList`.
205
259
#[ cfg( not( feature = "const_mut_refs" ) ) ]
@@ -209,6 +263,8 @@ impl HoleList {
209
263
size : 0 ,
210
264
next : None ,
211
265
} ,
266
+ bottom : null_mut ( ) ,
267
+ top : null_mut ( ) ,
212
268
}
213
269
}
214
270
@@ -220,6 +276,8 @@ impl HoleList {
220
276
size : 0 ,
221
277
next : None ,
222
278
} ,
279
+ bottom : null_mut ( ) ,
280
+ top : null_mut ( ) ,
223
281
}
224
282
}
225
283
@@ -228,6 +286,7 @@ impl HoleList {
228
286
Some ( Cursor {
229
287
hole,
230
288
prev : NonNull :: new ( & mut self . first ) ?,
289
+ top : self . top ,
231
290
} )
232
291
} else {
233
292
None
@@ -274,8 +333,7 @@ impl HoleList {
274
333
let aligned_hole_addr = align_up ( hole_addr, align_of :: < Hole > ( ) ) ;
275
334
let ptr = aligned_hole_addr as * mut Hole ;
276
335
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 ) ) ,
279
337
next : None ,
280
338
} ) ;
281
339
@@ -284,6 +342,8 @@ impl HoleList {
284
342
size : 0 ,
285
343
next : Some ( NonNull :: new_unchecked ( ptr) ) ,
286
344
} ,
345
+ bottom : aligned_hole_addr,
346
+ top : hole_addr. wrapping_add ( hole_size) ,
287
347
}
288
348
}
289
349
@@ -370,19 +430,6 @@ impl HoleList {
370
430
}
371
431
}
372
432
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
-
386
433
unsafe fn make_hole ( addr : * mut u8 , size : usize ) -> NonNull < Hole > {
387
434
let hole_addr = addr. cast :: < Hole > ( ) ;
388
435
debug_assert_eq ! (
@@ -395,7 +442,7 @@ unsafe fn make_hole(addr: *mut u8, size: usize) -> NonNull<Hole> {
395
442
}
396
443
397
444
impl 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 > {
399
446
// Covers the case where the new hole exists BEFORE the current pointer,
400
447
// which only happens when previous is the stub pointer
401
448
if node < self . hole {
@@ -409,59 +456,86 @@ impl Cursor {
409
456
) ;
410
457
debug_assert_eq ! ( self . previous( ) . size, 0 ) ;
411
458
412
- let Cursor { mut prev, hole } = self ;
459
+ let Cursor {
460
+ mut prev,
461
+ hole,
462
+ top,
463
+ } = self ;
413
464
unsafe {
465
+ let mut node = check_merge_bottom ( node, bottom) ;
414
466
prev. as_mut ( ) . next = Some ( node) ;
415
467
node. as_mut ( ) . next = Some ( hole) ;
416
468
}
417
- Ok ( Cursor { prev, hole : node } )
469
+ Ok ( Cursor {
470
+ prev,
471
+ hole : node,
472
+ top,
473
+ } )
418
474
} else {
419
475
Err ( self )
420
476
}
421
477
}
422
478
423
479
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 } ;
429
482
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 {
438
486
let node_u8 = node_u8 as * const u8 ;
439
487
assert ! (
440
488
node_u8. wrapping_add( node_size) <= next. as_ptr( ) . cast:: <u8 >( ) ,
441
489
"Freed node aliases existing hole! Bad free?" ,
442
490
) ;
491
+ } else {
492
+ // The new hole isn't between current and next.
493
+ return Err ( ( ) ) ;
443
494
}
495
+ }
444
496
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;
453
515
}
516
+
517
+ Ok ( ( ) )
454
518
}
455
519
456
520
// Merge the current node with up to n following nodes
457
521
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 ;
459
528
460
529
for _ in 0 ..max {
461
530
// Is there a next node?
462
531
let mut next = if let Some ( next) = unsafe { hole. as_mut ( ) } . next . as_ref ( ) {
463
532
* next
464
533
} 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) ;
465
539
return ;
466
540
} ;
467
541
@@ -515,7 +589,10 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
515
589
cursor
516
590
} else {
517
591
// 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 ) ;
519
596
list. first . next = Some ( hole) ;
520
597
return ;
521
598
} ;
@@ -525,7 +602,7 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
525
602
// previous location the cursor was pointing to.
526
603
//
527
604
// 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 ) {
529
606
Ok ( cursor) => {
530
607
// Yup! It lives at the front of the list. Hooray! Attempt to merge
531
608
// it with just ONE next node, since it is at the front of the list
@@ -578,8 +655,8 @@ pub mod test {
578
655
let assumed_location = data. as_mut_ptr ( ) . cast ( ) ;
579
656
580
657
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 ) ;
583
660
heap
584
661
}
585
662
0 commit comments