@@ -1586,16 +1586,46 @@ impl<A: Allocator + Clone> RawTableInner<A> {
1586
1586
}
1587
1587
1588
1588
/// Searches for an empty or deleted bucket which is suitable for inserting
1589
- /// a new element.
1589
+ /// a new element, returning the `index` for the new [`Bucket`] .
1590
1590
///
1591
- /// There must be at least 1 empty bucket in the table.
1591
+ /// The table must have at least 1 empty or deleted `bucket`, otherwise this function
1592
+ /// will never return (will go into an infinite loop) for tables larger than the group
1593
+ /// width, or return an index outside of the table indices range if the table is less
1594
+ /// than the group width.
1595
+ ///
1596
+ /// # Safety
1597
+ ///
1598
+ /// Actually, calling this function is always safe, but attempting to write data at
1599
+ /// the index returned by this function when the table is less than the group width
1600
+ /// and if there was not at least one empty bucket in the table will cause immediate
1601
+ /// [`undefined behavior`].
1602
+ ///
1603
+ /// This is because in this case the function will return `self.bucket_mask + 1`
1604
+ /// as an index due to the trailing EMPTY control bytes outside the table range.
1605
+ ///
1606
+ /// [`undefined behavior`]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1592
1607
#[ inline]
1593
- fn find_insert_slot ( & self , hash : u64 ) -> usize {
1608
+ unsafe fn find_insert_slot ( & self , hash : u64 ) -> usize {
1594
1609
let mut probe_seq = self . probe_seq ( hash) ;
1595
1610
loop {
1611
+ // SAFETY:
1612
+ // * `ProbeSeq.pos` cannot be greater than `self.bucket_mask = self.buckets() - 1`
1613
+ // of the table due to masking with `self.bucket_mask` and also because mumber of
1614
+ // buckets is a power of two (see comment for masking below).
1615
+ //
1616
+ // * Even if `ProbeSeq.pos` returns `position == self.bucket_mask`, it is safe to
1617
+ // call `Group::load` due to the extended control bytes range, which is
1618
+ // `self.bucket_mask + 1 + Group::WIDTH` (in fact, this means that the last control
1619
+ // byte will never be read for the allocated table);
1620
+ //
1621
+ // * Also, even if `RawTableInner` is not already allocated, `ProbeSeq.pos` will
1622
+ // always return "0" (zero), so Group::load will read unaligned `Group::static_empty()`
1623
+ // bytes, which is safe (see RawTableInner::new_in).
1596
1624
unsafe {
1597
1625
let group = Group :: load ( self . ctrl ( probe_seq. pos ) ) ;
1598
1626
if let Some ( bit) = group. match_empty_or_deleted ( ) . lowest_set_bit ( ) {
1627
+ // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
1628
+ // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
1599
1629
let result = ( probe_seq. pos + bit) & self . bucket_mask ;
1600
1630
1601
1631
// In tables smaller than the group width, trailing control
@@ -1607,9 +1637,26 @@ impl<A: Allocator + Clone> RawTableInner<A> {
1607
1637
// table. This second scan is guaranteed to find an empty
1608
1638
// slot (due to the load factor) before hitting the trailing
1609
1639
// control bytes (containing EMPTY).
1640
+ //
1641
+ // SAFETY: The `result` is guaranteed to be in range `0..self.bucket_mask`
1642
+ // due to masking with `self.bucket_mask`
1610
1643
if unlikely ( self . is_bucket_full ( result) ) {
1611
1644
debug_assert ! ( self . bucket_mask < Group :: WIDTH ) ;
1612
1645
debug_assert_ne ! ( probe_seq. pos, 0 ) ;
1646
+ // SAFETY:
1647
+ //
1648
+ // * We are in range and `ptr = self.ctrl(0)` are valid for reads
1649
+ // and properly aligned, because the table is already allocated
1650
+ // (see `TableLayout::calculate_layout_for` and `ptr::read`);
1651
+ //
1652
+ // * For tables larger than the group width, we will never end up in the given
1653
+ // branch, since `(probe_seq.pos + bit) & self.bucket_mask` cannot return a
1654
+ // full bucket index. For tables smaller than the group width, calling the
1655
+ // `lowest_set_bit_nonzero` function (when `nightly` feature enabled) is also
1656
+ // safe, as the trailing control bytes outside the range of the table are filled
1657
+ // with EMPTY bytes, so this second scan either finds an empty slot (due to the
1658
+ // load factor) or hits the trailing control bytes (containing EMPTY). See
1659
+ // `intrinsics::cttz_nonzero` for more information.
1613
1660
return Group :: load_aligned ( self . ctrl ( 0 ) )
1614
1661
. match_empty_or_deleted ( )
1615
1662
. lowest_set_bit_nonzero ( ) ;
0 commit comments