@@ -1736,6 +1736,40 @@ impl RawTableInner {
17361736 }
17371737}
17381738
1739+ /// Find the previous power of 2. If it's already a power of 2, it's unchanged.
1740+ /// Passing zero is undefined behavior.
1741+ fn prev_pow2 ( z : usize ) -> usize {
1742+ let shift = mem:: size_of :: < usize > ( ) * 8 - 1 ;
1743+ return 1 << ( shift - ( z. leading_zeros ( ) as usize ) ) ;
1744+ }
1745+
1746+ fn maximum_buckets_in (
1747+ allocation_size : usize ,
1748+ table_layout : TableLayout ,
1749+ group_width : usize ,
1750+ ) -> usize {
1751+ // Given an equation like:
1752+ // z >= x * y + x + g
1753+ // x can be maximized by doing:
1754+ // x = (z - g) / (y + 1)
1755+ // If you squint:
1756+ // x is the number of buckets
1757+ // y is the table_layout.size
1758+ // z is the size of the allocation
1759+ // g is the group width
1760+ // But this is ignoring the padding needed for ctrl_align.
1761+ // If we remember these restrictions:
1762+ // x is always a power of 2
1763+ // Layout size for T must always be a multiple of T
1764+ // Then the alignment can be ignored if we add the constraint:
1765+ // x * y >= table_layout.ctrl_align
1766+ // This is taken care of by `capacity_to_buckets`.
1767+ let numerator = allocation_size - group_width;
1768+ let denominator = table_layout. size + 1 ; // todo: ZSTs?
1769+ let quotient = numerator / denominator;
1770+ prev_pow2 ( quotient)
1771+ }
1772+
17391773impl RawTableInner {
17401774 /// Allocates a new [`RawTableInner`] with the given number of buckets.
17411775 /// The control bytes and buckets are left uninitialized.
@@ -1753,7 +1787,7 @@ impl RawTableInner {
17531787 unsafe fn new_uninitialized < A > (
17541788 alloc : & A ,
17551789 table_layout : TableLayout ,
1756- buckets : usize ,
1790+ mut buckets : usize ,
17571791 fallibility : Fallibility ,
17581792 ) -> Result < Self , TryReserveError >
17591793 where
@@ -1762,13 +1796,29 @@ impl RawTableInner {
17621796 debug_assert ! ( buckets. is_power_of_two( ) ) ;
17631797
17641798 // Avoid `Option::ok_or_else` because it bloats LLVM IR.
1765- let ( layout, ctrl_offset) = match table_layout. calculate_layout_for ( buckets) {
1799+ let ( layout, mut ctrl_offset) = match table_layout. calculate_layout_for ( buckets) {
17661800 Some ( lco) => lco,
17671801 None => return Err ( fallibility. capacity_overflow ( ) ) ,
17681802 } ;
17691803
17701804 let ptr: NonNull < u8 > = match do_alloc ( alloc, layout) {
1771- Ok ( block) => block. cast ( ) ,
1805+ Ok ( block) => {
1806+ // Utilize over-sized allocations.
1807+ let x = maximum_buckets_in ( block. len ( ) , table_layout, Group :: WIDTH ) ;
1808+ debug_assert ! ( x >= buckets) ;
1809+ // Calculate the new ctrl_offset.
1810+ let ( _oversized_layout, oversized_ctrl_offset) =
1811+ match table_layout. calculate_layout_for ( x) {
1812+ Some ( lco) => lco,
1813+ None => unsafe { hint:: unreachable_unchecked ( ) } ,
1814+ } ;
1815+ debug_assert ! ( _oversized_layout. size( ) <= block. len( ) ) ;
1816+ debug_assert ! ( oversized_ctrl_offset >= ctrl_offset) ;
1817+ ctrl_offset = oversized_ctrl_offset;
1818+ buckets = x;
1819+
1820+ block. cast ( )
1821+ }
17721822 Err ( _) => return Err ( fallibility. alloc_err ( layout) ) ,
17731823 } ;
17741824
@@ -4586,6 +4636,23 @@ impl<T, A: Allocator> RawExtractIf<'_, T, A> {
45864636mod test_map {
45874637 use super :: * ;
45884638
4639+ #[ test]
4640+ fn test_prev_pow2 ( ) {
4641+ // Skip 0, not defined for that input.
4642+ let mut pow2: usize = 1 ;
4643+ while ( pow2 << 1 ) > 0 {
4644+ let next_pow2 = pow2 << 1 ;
4645+ assert_eq ! ( pow2, prev_pow2( pow2) ) ;
4646+ // Need to skip 2, because it's also a power of 2, so it doesn't
4647+ // return the previous power of 2.
4648+ if next_pow2 > 2 {
4649+ assert_eq ! ( pow2, prev_pow2( pow2 + 1 ) ) ;
4650+ assert_eq ! ( pow2, prev_pow2( next_pow2 - 1 ) ) ;
4651+ }
4652+ pow2 = next_pow2;
4653+ }
4654+ }
4655+
45894656 #[ test]
45904657 fn test_minimum_capacity_for_small_types ( ) {
45914658 #[ track_caller]
0 commit comments