@@ -3847,8 +3847,30 @@ impl<T> RawIterRange<T> {
3847
3847
}
3848
3848
}
3849
3849
3850
+ /// Folds every element into an accumulator by applying an operation,
3851
+ /// returning the final result.
3852
+ ///
3853
+ /// `fold_impl()` takes three arguments: a number of items in the table, an initial value,
3854
+ /// and a closure with two arguments: an 'accumulator', and an element. The closure
3855
+ /// returns the value that the accumulator should have for the next iteration.
3856
+ ///
3857
+ /// The initial value is the value the accumulator will have on the first call.
3858
+ ///
3859
+ /// After applying this closure to every element of the iterator, `fold_impl()`
3860
+ /// returns the accumulator.
3861
+ ///
3850
3862
/// # Safety
3851
- /// The provided `n` value must match the actual number of items
3863
+ ///
3864
+ /// If any of the following conditions are violated, the result is
3865
+ /// [`Undefined Behavior`]:
3866
+ ///
3867
+ /// * The [`RawTableInner`] / [`RawTable`] must be alive and not moved,
3868
+ /// i.e. table outlives the `RawIterRange`;
3869
+ ///
3870
+ /// * The provided `n` value must match the actual number of items
3871
+ /// in the table.
3872
+ ///
3873
+ /// [`Undefined Behavior`]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
3852
3874
#[ allow( clippy:: while_let_on_iterator) ]
3853
3875
#[ cfg_attr( feature = "inline-more" , inline) ]
3854
3876
unsafe fn fold_impl < F , B > ( mut self , mut n : usize , mut acc : B , mut f : F ) -> B
@@ -3861,6 +3883,8 @@ impl<T> RawIterRange<T> {
3861
3883
3862
3884
loop {
3863
3885
while let Some ( index) = self . current_group . next ( ) {
3886
+ // The returned `index` will always be in the range `0..Group::WIDTH`,
3887
+ // so that calling `self.data.next_n(index)` is safe (see detailed explanation below).
3864
3888
debug_assert ! ( n != 0 ) ;
3865
3889
let bucket = self . data . next_n ( index) ;
3866
3890
acc = f ( acc, bucket) ;
@@ -3871,11 +3895,34 @@ impl<T> RawIterRange<T> {
3871
3895
return acc;
3872
3896
}
3873
3897
3874
- // We might read past self.end up to the next group boundary,
3875
- // but this is fine because it only occurs on tables smaller
3876
- // than the group size where the trailing control bytes are all
3877
- // EMPTY. On larger tables self.end is guaranteed to be aligned
3878
- // to the group size (since tables are power-of-two sized).
3898
+ // SAFETY: The caller of this function ensures that:
3899
+ //
3900
+ // 1. The provided `n` value matches the actual number of items in the table;
3901
+ // 2. The table is alive and did not moved.
3902
+ //
3903
+ // Taking the above into account, we always stay within the bounds, because:
3904
+ //
3905
+ // 1. For tables smaller than the group width (self.buckets() <= Group::WIDTH),
3906
+ // we will never end up in the given branch, since we should have already
3907
+ // yielded all the elements of the table.
3908
+ //
3909
+ // 2. For tables larger than the group width. The the number of buckets is a
3910
+ // power of two (2 ^ n), Group::WIDTH is also power of two (2 ^ k). Sinse
3911
+ // `(2 ^ n) > (2 ^ k)`, than `(2 ^ n) % (2 ^ k) = 0`. As we start from the
3912
+ // the start of the array of control bytes, and never try to iterate after
3913
+ // getting all the elements, the last `self.current_group` will read bytes
3914
+ // from the `self.buckets() - Group::WIDTH` index. We know also that
3915
+ // `self.current_group.next()` will always retun indices within the range
3916
+ // `0..Group::WIDTH`.
3917
+ //
3918
+ // Knowing all of the above and taking into account that we are synchronizing
3919
+ // the `self.data` index with the index we used to read the `self.current_group`,
3920
+ // the subsequent `self.data.next_n(index)` will always return a bucket with
3921
+ // an index number less than `self.buckets()`.
3922
+ //
3923
+ // The last `self.next_ctrl`, whose index would be `self.buckets()`, will never
3924
+ // actually be read, since we should have already yielded all the elements of
3925
+ // the table.
3879
3926
self . current_group = Group :: load_aligned ( self . next_ctrl ) . match_full ( ) . into_iter ( ) ;
3880
3927
self . data = self . data . next_n ( Group :: WIDTH ) ;
3881
3928
self . next_ctrl = self . next_ctrl . add ( Group :: WIDTH ) ;
0 commit comments