@@ -97,6 +97,8 @@ impl ProbeSeq {
9797/// taking the maximum load factor into account.
9898///
9999/// Returns `None` if an overflow occurs.
100+ ///
101+ /// This ensures that `buckets * table_layout.size >= table_layout.ctrl_align`.
100102// Workaround for emscripten bug emscripten-core/emscripten-fastcomp#258
101103#[ cfg_attr( target_os = "emscripten" , inline( never) ) ]
102104#[ cfg_attr( not( target_os = "emscripten" ) , inline) ]
@@ -138,13 +140,15 @@ fn capacity_to_buckets(cap: usize, table_layout: TableLayout) -> Option<usize> {
138140 // We don't bother with a table size of 2 buckets since that can only
139141 // hold a single element. Instead, we skip directly to a 4 bucket table
140142 // which can hold 3 elements.
141- return Some ( if cap < 4 {
143+ let buckets = if cap < 4 {
142144 4
143145 } else if cap < 8 {
144146 8
145147 } else {
146148 16
147- } ) ;
149+ } ;
150+ ensure_bucket_bytes_at_least_ctrl_align ( table_layout, buckets) ;
151+ Some ( buckets)
148152 }
149153
150154 // Otherwise require 1/8 buckets to be empty (87.5% load)
@@ -156,7 +160,22 @@ fn capacity_to_buckets(cap: usize, table_layout: TableLayout) -> Option<usize> {
156160 // Any overflows will have been caught by the checked_mul. Also, any
157161 // rounding errors from the division above will be cleaned up by
158162 // next_power_of_two (which can't overflow because of the previous division).
159- Some ( adjusted_cap. next_power_of_two ( ) )
163+ let buckets = adjusted_cap. next_power_of_two ( ) ;
164+ ensure_bucket_bytes_at_least_ctrl_align ( table_layout, buckets) ;
165+ Some ( buckets)
166+ }
167+
168+ // `maximum_buckets_in` relies on the property that for non-ZST `T`, any
169+ // chosen `buckets` will satisfy `buckets * table_layout.size >=
170+ // table_layout.ctrl_align`, so `calculate_layout_for` does not need to add
171+ // extra padding beyond `table_layout.size * buckets`. If small-table bucket
172+ // selection or growth policy changes, revisit `maximum_buckets_in`.
173+ #[ inline]
174+ fn ensure_bucket_bytes_at_least_ctrl_align ( table_layout : TableLayout , buckets : usize ) {
175+ if table_layout. size != 0 {
176+ let prod = table_layout. size . saturating_mul ( buckets) ;
177+ debug_assert ! ( prod >= table_layout. ctrl_align) ;
178+ }
160179}
161180
162181/// Returns the maximum effective capacity for the given bucket mask, taking
@@ -1449,6 +1468,11 @@ pub(crate) fn prev_pow2(z: usize) -> usize {
14491468 1 << ( shift - ( z. leading_zeros ( ) as usize ) )
14501469}
14511470
1471+ /// Finds the largest number of buckets that can fit in `allocation_size`
1472+ /// provided the given TableLayout.
1473+ ///
1474+ /// This relies on some invariants of `capacity_to_buckets`, so only feed in
1475+ /// an `allocation_size` calculated from `capacity_to_buckets`.
14521476fn maximum_buckets_in (
14531477 allocation_size : usize ,
14541478 table_layout : TableLayout ,
@@ -1470,10 +1494,10 @@ fn maximum_buckets_in(
14701494 // Then the alignment can be ignored if we add the constraint:
14711495 // x * y >= table_layout.ctrl_align
14721496 // This is taken care of by `capacity_to_buckets`.
1473- let numerator = allocation_size - group_width ;
1474- let denominator = table_layout . size + 1 ; // todo: ZSTs?
1475- let quotient = numerator / denominator ;
1476- prev_pow2 ( quotient )
1497+ // It may be helpful to understand this if you remember that:
1498+ // ctrl_offset = align(x * y, ctrl_align)
1499+ let x = ( allocation_size - group_width ) / ( table_layout . size + 1 ) ;
1500+ prev_pow2 ( x )
14771501}
14781502
14791503impl RawTableInner {
@@ -1509,17 +1533,19 @@ impl RawTableInner {
15091533
15101534 let ptr: NonNull < u8 > = match do_alloc ( alloc, layout) {
15111535 Ok ( block) => {
1512- if block. len ( ) > layout. size ( ) {
1536+ // The allocator can't return a value smaller than was
1537+ // requested, so this can be != instead of >=.
1538+ if block. len ( ) != layout. size ( ) {
15131539 // Utilize over-sized allocations.
15141540 let x = maximum_buckets_in ( block. len ( ) , table_layout, Group :: WIDTH ) ;
15151541 debug_assert ! ( x >= buckets) ;
15161542 // Calculate the new ctrl_offset.
1517- let ( _oversized_layout , oversized_ctrl_offset) =
1543+ let ( oversized_layout , oversized_ctrl_offset) =
15181544 match table_layout. calculate_layout_for ( x) {
15191545 Some ( lco) => lco,
15201546 None => unsafe { hint:: unreachable_unchecked ( ) } ,
15211547 } ;
1522- debug_assert ! ( _oversized_layout . size( ) <= block. len( ) ) ;
1548+ debug_assert ! ( oversized_layout . size( ) <= block. len( ) ) ;
15231549 debug_assert ! ( oversized_ctrl_offset >= ctrl_offset) ;
15241550 ctrl_offset = oversized_ctrl_offset;
15251551 buckets = x;
0 commit comments