@@ -6631,3 +6631,136 @@ mod test_map {
66316631        ) ; 
66326632    } 
66336633} 
6634+ 
6635+ #[ cfg( all( test,  unix,  any( feature = "nightly" ,  feature = "allocator-api2" ) ) ) ]  
6636+ mod  test_map_with_mmap_allocations { 
6637+     use  super :: HashMap ; 
6638+     use  crate :: raw:: prev_pow2; 
6639+     use  core:: alloc:: Layout ; 
6640+     use  core:: ptr:: { null_mut,  NonNull } ; 
6641+ 
6642+     #[ cfg( feature = "nightly" ) ]  
6643+     use  core:: alloc:: { AllocError ,  Allocator } ; 
6644+ 
6645+     #[ cfg( all( feature = "allocator-api2" ,  not( feature = "nightly" ) ) ) ]  
6646+     use  allocator_api2:: alloc:: { AllocError ,  Allocator } ; 
6647+ 
6648+     /// This is not a production quality allocator, just good enough for 
6649+ /// some basic tests. 
6650+ #[ derive( Clone ,  Copy ,  Debug ) ]  
6651+     struct  MmapAllocator  { 
6652+         /// Guarantee this is a power of 2. 
6653+ page_size :  usize , 
6654+     } 
6655+ 
6656+     impl  MmapAllocator  { 
6657+         fn  new ( )  -> Result < Self ,  AllocError >  { 
6658+             let  result = unsafe  {  libc:: sysconf ( libc:: _SC_PAGESIZE)  } ; 
6659+             if  result < 1  { 
6660+                 return  Err ( AllocError ) ; 
6661+             } 
6662+ 
6663+             let  page_size = result as  usize ; 
6664+             if  !page_size. is_power_of_two ( )  { 
6665+                 Err ( AllocError ) 
6666+             }  else  { 
6667+                 Ok ( Self  {  page_size } ) 
6668+             } 
6669+         } 
6670+ 
6671+         fn  fit_to_page_size ( & self ,  n :  usize )  -> Result < usize ,  AllocError >  { 
6672+             // If n=0, give a single page (wasteful, I know). 
6673+             let  n = if  n == 0  {  self . page_size  }  else  {  n } ; 
6674+ 
6675+             match  n &  ( self . page_size  - 1 )  { 
6676+                 0  => Ok ( n) , 
6677+                 rem => n. checked_add ( self . page_size  - rem) . ok_or ( AllocError ) , 
6678+             } 
6679+         } 
6680+     } 
6681+ 
6682+     unsafe  impl  Allocator  for  MmapAllocator  { 
6683+         fn  allocate ( & self ,  layout :  Layout )  -> Result < NonNull < [ u8 ] > ,  AllocError >  { 
6684+             if  layout. align ( )  > self . page_size  { 
6685+                 return  Err ( AllocError ) ; 
6686+             } 
6687+ 
6688+             let  null = null_mut ( ) ; 
6689+             let  len = self . fit_to_page_size ( layout. size ( ) ) ? as  libc:: size_t ; 
6690+             let  prot = libc:: PROT_READ  | libc:: PROT_WRITE ; 
6691+             let  flags = libc:: MAP_PRIVATE  | libc:: MAP_ANON ; 
6692+             let  addr = unsafe  {  libc:: mmap ( null,  len,  prot,  flags,  -1 ,  0 )  } ; 
6693+ 
6694+             // mmap returns MAP_FAILED on failure, not Null. 
6695+             if  addr == libc:: MAP_FAILED  { 
6696+                 return  Err ( AllocError ) ; 
6697+             } 
6698+ 
6699+             match  NonNull :: new ( addr. cast ( ) )  { 
6700+                 Some ( data)  => { 
6701+                     // SAFETY: this is NonNull::slice_from_raw_parts. 
6702+                     Ok ( unsafe  { 
6703+                         NonNull :: new_unchecked ( core:: ptr:: slice_from_raw_parts_mut ( 
6704+                             data. as_ptr ( ) , 
6705+                             len, 
6706+                         ) ) 
6707+                     } ) 
6708+                 } 
6709+ 
6710+                 // This branch shouldn't be taken in practice, but since we 
6711+                 // cannot return null as a valid pointer in our type system, 
6712+                 // we attempt to handle it. 
6713+                 None  => { 
6714+                     _ = unsafe  {  libc:: munmap ( addr,  len)  } ; 
6715+                     Err ( AllocError ) 
6716+                 } 
6717+             } 
6718+         } 
6719+ 
6720+         unsafe  fn  deallocate ( & self ,  ptr :  NonNull < u8 > ,  layout :  Layout )  { 
6721+             // If they allocated it with this layout, it must round correctly. 
6722+             let  size = self . fit_to_page_size ( layout. size ( ) ) . unwrap ( ) ; 
6723+             let  _result = libc:: munmap ( ptr. as_ptr ( ) . cast ( ) ,  size) ; 
6724+             debug_assert_eq ! ( 0 ,  _result) 
6725+         } 
6726+     } 
6727+ 
6728+     #[ test]  
6729+     fn  test_tiny_allocation_gets_rounded_to_page_size ( )  { 
6730+         let  alloc = MmapAllocator :: new ( ) . unwrap ( ) ; 
6731+         let  mut  map:  HashMap < usize ,  ( ) ,  _ ,  _ >  = HashMap :: with_capacity_in ( 1 ,  alloc) ; 
6732+ 
6733+         // Size of an element plus its control byte. 
6734+         let  rough_bucket_size = core:: mem:: size_of :: < ( usize ,  ( ) ) > ( )  + 1 ; 
6735+ 
6736+         // Accounting for some misc. padding that's likely in the allocation 
6737+         // due to rounding to group width, etc. 
6738+         let  overhead = 3  *  core:: mem:: size_of :: < usize > ( ) ; 
6739+         let  num_buckets = ( alloc. page_size  - overhead)  / rough_bucket_size; 
6740+         // Buckets are always powers of 2. 
6741+         let  min_elems = prev_pow2 ( num_buckets) ; 
6742+         // Real load-factor is 7/8, but this is a lower estimation, so 1/2. 
6743+         let  min_capacity = min_elems >> 1 ; 
6744+         let  capacity = map. capacity ( ) ; 
6745+         assert ! ( 
6746+             capacity >= min_capacity, 
6747+             "failed: {capacity} >= {min_capacity}" 
6748+         ) ; 
6749+ 
6750+         // Fill it up. 
6751+         for  i in  0 ..capacity { 
6752+             map. insert ( i,  ( ) ) ; 
6753+         } 
6754+         // Capacity should not have changed and it should be full. 
6755+         assert_eq ! ( capacity,  map. len( ) ) ; 
6756+         assert_eq ! ( capacity,  map. capacity( ) ) ; 
6757+ 
6758+         // Alright, make it grow. 
6759+         map. insert ( capacity,  ( ) ) ; 
6760+         assert ! ( 
6761+             capacity < map. capacity( ) , 
6762+             "failed: {capacity} < {}" , 
6763+             map. capacity( ) 
6764+         ) ; 
6765+     } 
6766+ } 
0 commit comments