@@ -1736,6 +1736,40 @@ impl RawTableInner {
1736
1736
}
1737
1737
}
1738
1738
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
+ 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
+
1739
1773
impl RawTableInner {
1740
1774
/// Allocates a new [`RawTableInner`] with the given number of buckets.
1741
1775
/// The control bytes and buckets are left uninitialized.
@@ -1753,7 +1787,7 @@ impl RawTableInner {
1753
1787
unsafe fn new_uninitialized < A > (
1754
1788
alloc : & A ,
1755
1789
table_layout : TableLayout ,
1756
- buckets : usize ,
1790
+ mut buckets : usize ,
1757
1791
fallibility : Fallibility ,
1758
1792
) -> Result < Self , TryReserveError >
1759
1793
where
@@ -1762,13 +1796,29 @@ impl RawTableInner {
1762
1796
debug_assert ! ( buckets. is_power_of_two( ) ) ;
1763
1797
1764
1798
// 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) {
1766
1800
Some ( lco) => lco,
1767
1801
None => return Err ( fallibility. capacity_overflow ( ) ) ,
1768
1802
} ;
1769
1803
1770
1804
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
+ }
1772
1822
Err ( _) => return Err ( fallibility. alloc_err ( layout) ) ,
1773
1823
} ;
1774
1824
@@ -4586,6 +4636,23 @@ impl<T, A: Allocator> RawExtractIf<'_, T, A> {
4586
4636
mod test_map {
4587
4637
use super :: * ;
4588
4638
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
+
4589
4656
#[ test]
4590
4657
fn test_minimum_capacity_for_small_types ( ) {
4591
4658
#[ track_caller]
0 commit comments