@@ -601,10 +601,61 @@ impl RecyclePool {
601601 } )
602602 }
603603
604+ /// Rebuild pool state so that every address in `allocated` is removed
605+ /// from the free list, matching externally known inflight state.
606+ pub fn restore_allocated ( & self , allocated : & [ u64 ] ) -> Result < ( ) , AllocError > {
607+ self . reset ( ) ;
608+
609+ if allocated. is_empty ( ) {
610+ return Ok ( ( ) ) ;
611+ }
612+
613+ let mut inner = self . inner . borrow_mut ( ) ;
614+
615+ for & addr in allocated {
616+ let pos = inner
617+ . free
618+ . iter ( )
619+ . position ( |& a| a == addr)
620+ . ok_or ( AllocError :: InvalidFree ( addr, inner. slot_size ) ) ?;
621+
622+ inner. free . swap_remove ( pos) ;
623+ }
624+
625+ Ok ( ( ) )
626+ }
627+
628+ /// Compute the address of slot `index`.
629+ ///
630+ /// Returns `None` if `index >= count`.
631+ pub fn slot_addr ( & self , index : usize ) -> Option < u64 > {
632+ let inner = self . inner . borrow ( ) ;
633+ if index < inner. count {
634+ Some ( inner. base_addr + ( index * inner. slot_size ) as u64 )
635+ } else {
636+ None
637+ }
638+ }
639+
604640 /// Number of free slots.
605641 pub fn num_free ( & self ) -> usize {
606642 self . inner . borrow ( ) . free . len ( )
607643 }
644+
645+ /// Base address of the pool region.
646+ pub fn base_addr ( & self ) -> u64 {
647+ self . inner . borrow ( ) . base_addr
648+ }
649+
650+ /// Slot size in bytes.
651+ pub fn slot_size ( & self ) -> usize {
652+ self . inner . borrow ( ) . slot_size
653+ }
654+
655+ /// Number of slots in the pool.
656+ pub fn count ( & self ) -> usize {
657+ self . inner . borrow ( ) . count
658+ }
608659}
609660
610661impl BufferProvider for RecyclePool {
@@ -664,6 +715,11 @@ mod tests {
664715 BufferPool :: < L , U > :: new ( base, size) . unwrap ( )
665716 }
666717
718+ fn make_recycle_pool ( slot_count : usize , slot_size : usize ) -> RecyclePool {
719+ let base = 0x80000u64 ;
720+ RecyclePool :: new ( base, slot_count * slot_size, slot_size) . unwrap ( )
721+ }
722+
667723 #[ test]
668724 fn test_slab_new_success ( ) {
669725 let slab = Slab :: < 256 > :: new ( 0x10000 , 1024 ) . unwrap ( ) ;
@@ -1223,6 +1279,77 @@ mod tests {
12231279 let a = pool. inner . borrow_mut ( ) . alloc ( 256 ) . unwrap ( ) ;
12241280 assert ! ( a. len > 0 ) ;
12251281 }
1282+
1283+ #[ test]
1284+ fn test_recycle_pool_restore_allocated_removes_from_free_list ( ) {
1285+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1286+ assert_eq ! ( pool. num_free( ) , 4 ) ;
1287+
1288+ let addrs = [ 0x80000 , 0x81000 ] ; // slots 0 and 1
1289+ pool. restore_allocated ( & addrs) . unwrap ( ) ;
1290+ assert_eq ! ( pool. num_free( ) , 2 ) ;
1291+
1292+ // Allocating should only return the two remaining slots
1293+ let a1 = pool. alloc ( 4096 ) . unwrap ( ) ;
1294+ let a2 = pool. alloc ( 4096 ) . unwrap ( ) ;
1295+ assert ! ( pool. alloc( 4096 ) . is_err( ) ) ;
1296+
1297+ // The allocated addresses should be the non-restored ones
1298+ let mut got = [ a1. addr , a2. addr ] ;
1299+ got. sort ( ) ;
1300+ assert_eq ! ( got, [ 0x82000 , 0x83000 ] ) ;
1301+ }
1302+
1303+ #[ test]
1304+ fn test_recycle_pool_restore_allocated_invalid_addr_returns_error ( ) {
1305+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1306+ let result = pool. restore_allocated ( & [ 0xDEAD ] ) ;
1307+ assert ! ( result. is_err( ) ) ;
1308+ }
1309+
1310+ #[ test]
1311+ fn test_recycle_pool_restore_allocated_then_dealloc_roundtrip ( ) {
1312+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1313+ let addr = 0x81000u64 ;
1314+
1315+ pool. restore_allocated ( & [ addr] ) . unwrap ( ) ;
1316+ assert_eq ! ( pool. num_free( ) , 3 ) ;
1317+
1318+ // Dealloc the restored address
1319+ pool. dealloc ( Allocation { addr, len : 4096 } ) . unwrap ( ) ;
1320+ assert_eq ! ( pool. num_free( ) , 4 ) ;
1321+ }
1322+
1323+ #[ test]
1324+ fn test_recycle_pool_restore_allocated_all_slots ( ) {
1325+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1326+ let addrs: Vec < u64 > = ( 0 ..4 ) . map ( |i| 0x80000 + i * 4096 ) . collect ( ) ;
1327+
1328+ pool. restore_allocated ( & addrs) . unwrap ( ) ;
1329+ assert_eq ! ( pool. num_free( ) , 0 ) ;
1330+ assert ! ( pool. alloc( 4096 ) . is_err( ) ) ;
1331+ }
1332+
1333+ #[ test]
1334+ fn test_recycle_pool_restore_allocated_empty_list_is_noop ( ) {
1335+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1336+ pool. restore_allocated ( & [ ] ) . unwrap ( ) ;
1337+ assert_eq ! ( pool. num_free( ) , 4 ) ;
1338+ }
1339+
1340+ #[ test]
1341+ fn test_recycle_pool_restore_allocated_resets_first ( ) {
1342+ let pool = make_recycle_pool ( 4 , 4096 ) ;
1343+
1344+ // Allocate some slots
1345+ let _ = pool. alloc ( 4096 ) . unwrap ( ) ;
1346+ let _ = pool. alloc ( 4096 ) . unwrap ( ) ;
1347+ assert_eq ! ( pool. num_free( ) , 2 ) ;
1348+
1349+ // restore_allocated resets then removes - so 4 - 1 = 3
1350+ pool. restore_allocated ( & [ 0x80000 ] ) . unwrap ( ) ;
1351+ assert_eq ! ( pool. num_free( ) , 3 ) ;
1352+ }
12261353}
12271354
12281355#[ cfg( test) ]
0 commit comments