summaryrefslogtreecommitdiffstats
path: root/third_party/rust/thunderdome/src/drain.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/thunderdome/src/drain.rs')
-rw-r--r--third_party/rust/thunderdome/src/drain.rs96
1 files changed, 96 insertions, 0 deletions
diff --git a/third_party/rust/thunderdome/src/drain.rs b/third_party/rust/thunderdome/src/drain.rs
new file mode 100644
index 0000000000..930c0962fa
--- /dev/null
+++ b/third_party/rust/thunderdome/src/drain.rs
@@ -0,0 +1,96 @@
+use std::iter::{ExactSizeIterator, FusedIterator};
+
+use crate::arena::{Arena, Index};
+
+/// See [`Arena::drain`][Arena::drain].
+pub struct Drain<'a, T> {
+ pub(crate) arena: &'a mut Arena<T>,
+ pub(crate) slot: u32,
+}
+
+impl<'a, T> Iterator for Drain<'a, T> {
+ type Item = (Index, T);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ // If there are no entries remaining in the arena, we should always
+ // return None. Using this check instead of comparing with the
+ // arena's size allows us to skip any trailing empty entries.
+ if self.arena.is_empty() {
+ return None;
+ }
+
+ // slot may overflow if the arena's underlying storage contains more
+ // than 2^32 elements, but its internal length value was not
+ // changed, as it overflowing would panic before reaching this code.
+ let slot = self.slot;
+ self.slot = self
+ .slot
+ .checked_add(1)
+ .unwrap_or_else(|| panic!("Overflowed u32 trying to drain Arena"));
+
+ // If this entry is occupied, this method will mark it as an empty.
+ // Otherwise, we'll continue looping until we've drained all
+ // occupied entries from the arena.
+ if let Some((index, value)) = self.arena.remove_entry_by_slot(slot) {
+ return Some((index, value));
+ }
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (self.arena.len(), Some(self.arena.len()))
+ }
+}
+
+impl<'a, T> FusedIterator for Drain<'a, T> {}
+impl<'a, T> ExactSizeIterator for Drain<'a, T> {}
+
+impl<'a, T> Drop for Drain<'a, T> {
+ // Continue iterating/dropping if there are any elements left.
+ fn drop(&mut self) {
+ self.for_each(drop);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::Arena;
+
+ use std::collections::HashSet;
+
+ #[test]
+ fn drain() {
+ let mut arena = Arena::with_capacity(2);
+ let one = arena.insert(1);
+ let two = arena.insert(2);
+
+ let mut drained_pairs = HashSet::new();
+ {
+ let mut drain = arena.drain();
+ assert_eq!(drain.size_hint(), (2, Some(2)));
+
+ drained_pairs.insert(drain.next().unwrap());
+ assert_eq!(drain.size_hint(), (1, Some(1)));
+
+ // Do not fully drain so we can ensure everything is dropped when the
+ // `Drain` is dropped.
+ assert_eq!(drain.size_hint(), (1, Some(1)));
+ }
+
+ assert_eq!(arena.len(), 0);
+ assert_eq!(arena.capacity(), 2);
+ assert_eq!(drained_pairs.len(), 1);
+
+ // We should still be able to use the arena after this.
+ let one_prime = arena.insert(1);
+ let two_prime = arena.insert(2);
+
+ assert_eq!(arena.len(), 2);
+ assert_eq!(arena.capacity(), 2);
+ assert_eq!(arena.get(one_prime), Some(&1));
+ assert_eq!(arena.get(two_prime), Some(&2));
+ assert_eq!(arena.get(one), None);
+ assert_eq!(arena.get(two), None);
+ }
+}