Skip to content

Commit eabe7f8

Browse files
committed
Make RawIter::{reflect_insert, reflect_remove} unsafe
Fixes #412
1 parent 59c0e15 commit eabe7f8

File tree

1 file changed

+71
-73
lines changed

1 file changed

+71
-73
lines changed

src/raw/mod.rs

Lines changed: 71 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2894,7 +2894,7 @@ impl<T> RawIter<T> {
28942894
/// This method should be called _before_ the removal is made. It is not necessary to call this
28952895
/// method if you are removing an item that this iterator yielded in the past.
28962896
#[cfg(feature = "raw")]
2897-
pub fn reflect_remove(&mut self, b: &Bucket<T>) {
2897+
pub unsafe fn reflect_remove(&mut self, b: &Bucket<T>) {
28982898
self.reflect_toggle_full(b, false);
28992899
}
29002900

@@ -2908,97 +2908,95 @@ impl<T> RawIter<T> {
29082908
///
29092909
/// This method should be called _after_ the given insert is made.
29102910
#[cfg(feature = "raw")]
2911-
pub fn reflect_insert(&mut self, b: &Bucket<T>) {
2911+
pub unsafe fn reflect_insert(&mut self, b: &Bucket<T>) {
29122912
self.reflect_toggle_full(b, true);
29132913
}
29142914

29152915
/// Refresh the iterator so that it reflects a change to the state of the given bucket.
29162916
#[cfg(feature = "raw")]
2917-
fn reflect_toggle_full(&mut self, b: &Bucket<T>, is_insert: bool) {
2918-
unsafe {
2919-
if b.as_ptr() > self.iter.data.as_ptr() {
2920-
// The iterator has already passed the bucket's group.
2921-
// So the toggle isn't relevant to this iterator.
2922-
return;
2917+
unsafe fn reflect_toggle_full(&mut self, b: &Bucket<T>, is_insert: bool) {
2918+
if b.as_ptr() > self.iter.data.as_ptr() {
2919+
// The iterator has already passed the bucket's group.
2920+
// So the toggle isn't relevant to this iterator.
2921+
return;
2922+
}
2923+
2924+
if self.iter.next_ctrl < self.iter.end
2925+
&& b.as_ptr() <= self.iter.data.next_n(Group::WIDTH).as_ptr()
2926+
{
2927+
// The iterator has not yet reached the bucket's group.
2928+
// We don't need to reload anything, but we do need to adjust the item count.
2929+
2930+
if cfg!(debug_assertions) {
2931+
// Double-check that the user isn't lying to us by checking the bucket state.
2932+
// To do that, we need to find its control byte. We know that self.iter.data is
2933+
// at self.iter.next_ctrl - Group::WIDTH, so we work from there:
2934+
let offset = offset_from(self.iter.data.as_ptr(), b.as_ptr());
2935+
let ctrl = self.iter.next_ctrl.sub(Group::WIDTH).add(offset);
2936+
// This method should be called _before_ a removal, or _after_ an insert,
2937+
// so in both cases the ctrl byte should indicate that the bucket is full.
2938+
assert!(is_full(*ctrl));
29232939
}
29242940

2925-
if self.iter.next_ctrl < self.iter.end
2926-
&& b.as_ptr() <= self.iter.data.next_n(Group::WIDTH).as_ptr()
2927-
{
2928-
// The iterator has not yet reached the bucket's group.
2929-
// We don't need to reload anything, but we do need to adjust the item count.
2941+
if is_insert {
2942+
self.items += 1;
2943+
} else {
2944+
self.items -= 1;
2945+
}
29302946

2931-
if cfg!(debug_assertions) {
2932-
// Double-check that the user isn't lying to us by checking the bucket state.
2933-
// To do that, we need to find its control byte. We know that self.iter.data is
2934-
// at self.iter.next_ctrl - Group::WIDTH, so we work from there:
2935-
let offset = offset_from(self.iter.data.as_ptr(), b.as_ptr());
2936-
let ctrl = self.iter.next_ctrl.sub(Group::WIDTH).add(offset);
2937-
// This method should be called _before_ a removal, or _after_ an insert,
2938-
// so in both cases the ctrl byte should indicate that the bucket is full.
2939-
assert!(is_full(*ctrl));
2940-
}
2947+
return;
2948+
}
2949+
2950+
// The iterator is at the bucket group that the toggled bucket is in.
2951+
// We need to do two things:
2952+
//
2953+
// - Determine if the iterator already yielded the toggled bucket.
2954+
// If it did, we're done.
2955+
// - Otherwise, update the iterator cached group so that it won't
2956+
// yield a to-be-removed bucket, or _will_ yield a to-be-added bucket.
2957+
// We'll also need to update the item count accordingly.
2958+
if let Some(index) = self.iter.current_group.lowest_set_bit() {
2959+
let next_bucket = self.iter.data.next_n(index);
2960+
if b.as_ptr() > next_bucket.as_ptr() {
2961+
// The toggled bucket is "before" the bucket the iterator would yield next. We
2962+
// therefore don't need to do anything --- the iterator has already passed the
2963+
// bucket in question.
2964+
//
2965+
// The item count must already be correct, since a removal or insert "prior" to
2966+
// the iterator's position wouldn't affect the item count.
2967+
} else {
2968+
// The removed bucket is an upcoming bucket. We need to make sure it does _not_
2969+
// get yielded, and also that it's no longer included in the item count.
2970+
//
2971+
// NOTE: We can't just reload the group here, both since that might reflect
2972+
// inserts we've already passed, and because that might inadvertently unset the
2973+
// bits for _other_ removals. If we do that, we'd have to also decrement the
2974+
// item count for those other bits that we unset. But the presumably subsequent
2975+
// call to reflect for those buckets might _also_ decrement the item count.
2976+
// Instead, we _just_ flip the bit for the particular bucket the caller asked
2977+
// us to reflect.
2978+
let our_bit = offset_from(self.iter.data.as_ptr(), b.as_ptr());
2979+
let was_full = self.iter.current_group.flip(our_bit);
2980+
debug_assert_ne!(was_full, is_insert);
29412981

29422982
if is_insert {
29432983
self.items += 1;
29442984
} else {
29452985
self.items -= 1;
29462986
}
29472987

2948-
return;
2949-
}
2950-
2951-
// The iterator is at the bucket group that the toggled bucket is in.
2952-
// We need to do two things:
2953-
//
2954-
// - Determine if the iterator already yielded the toggled bucket.
2955-
// If it did, we're done.
2956-
// - Otherwise, update the iterator cached group so that it won't
2957-
// yield a to-be-removed bucket, or _will_ yield a to-be-added bucket.
2958-
// We'll also need to update the item count accordingly.
2959-
if let Some(index) = self.iter.current_group.lowest_set_bit() {
2960-
let next_bucket = self.iter.data.next_n(index);
2961-
if b.as_ptr() > next_bucket.as_ptr() {
2962-
// The toggled bucket is "before" the bucket the iterator would yield next. We
2963-
// therefore don't need to do anything --- the iterator has already passed the
2964-
// bucket in question.
2965-
//
2966-
// The item count must already be correct, since a removal or insert "prior" to
2967-
// the iterator's position wouldn't affect the item count.
2968-
} else {
2969-
// The removed bucket is an upcoming bucket. We need to make sure it does _not_
2970-
// get yielded, and also that it's no longer included in the item count.
2971-
//
2972-
// NOTE: We can't just reload the group here, both since that might reflect
2973-
// inserts we've already passed, and because that might inadvertently unset the
2974-
// bits for _other_ removals. If we do that, we'd have to also decrement the
2975-
// item count for those other bits that we unset. But the presumably subsequent
2976-
// call to reflect for those buckets might _also_ decrement the item count.
2977-
// Instead, we _just_ flip the bit for the particular bucket the caller asked
2978-
// us to reflect.
2979-
let our_bit = offset_from(self.iter.data.as_ptr(), b.as_ptr());
2980-
let was_full = self.iter.current_group.flip(our_bit);
2981-
debug_assert_ne!(was_full, is_insert);
2982-
2983-
if is_insert {
2984-
self.items += 1;
2988+
if cfg!(debug_assertions) {
2989+
if b.as_ptr() == next_bucket.as_ptr() {
2990+
// The removed bucket should no longer be next
2991+
debug_assert_ne!(self.iter.current_group.lowest_set_bit(), Some(index));
29852992
} else {
2986-
self.items -= 1;
2987-
}
2988-
2989-
if cfg!(debug_assertions) {
2990-
if b.as_ptr() == next_bucket.as_ptr() {
2991-
// The removed bucket should no longer be next
2992-
debug_assert_ne!(self.iter.current_group.lowest_set_bit(), Some(index));
2993-
} else {
2994-
// We should not have changed what bucket comes next.
2995-
debug_assert_eq!(self.iter.current_group.lowest_set_bit(), Some(index));
2996-
}
2993+
// We should not have changed what bucket comes next.
2994+
debug_assert_eq!(self.iter.current_group.lowest_set_bit(), Some(index));
29972995
}
29982996
}
2999-
} else {
3000-
// We must have already iterated past the removed item.
30012997
}
2998+
} else {
2999+
// We must have already iterated past the removed item.
30023000
}
30033001
}
30043002

0 commit comments

Comments
 (0)