From ed999c42222757726f2020892f0c290f3d6e74cc Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Fri, 8 Mar 2024 10:04:24 +0100 Subject: [PATCH] Implement range iterators --- Cargo.toml | 2 + src/builder.rs | 39 +++- src/entries.rs | 46 ++++- src/generic.rs | 458 ++++++++++++++++++++++++++++++++++++++++++---- src/handle.rs | 7 +- src/iter.rs | 16 +- src/key.rs | 8 + src/lib.rs | 6 + src/range_iter.rs | 285 +++++++++++++++++++++++++++++ tests/range.rs | 141 ++++++++++++++ 10 files changed, 949 insertions(+), 59 deletions(-) create mode 100644 src/range_iter.rs create mode 100644 tests/range.rs diff --git a/Cargo.toml b/Cargo.toml index 57377b0..62ff740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ exclude = [ "/.*" ] [features] std = [ ] default = [ "std" ] +range = [ ] [build-dependencies] autocfg = "1" @@ -31,3 +32,4 @@ serde = { version = "1.0", optional = true, default-features = false, features = rustversion = "1" serde = { version = "1", features = [ "derive" ] } serde_test = "1" +rand = "0.8" diff --git a/src/builder.rs b/src/builder.rs index 5b973f1..f1bfbba 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,3 +1,5 @@ +use core::mem::replace; + use crate::entries::{Entries, Entry}; use crate::generic::GenericSlab; use crate::key::Key; @@ -29,19 +31,32 @@ where let slab = &mut self.slab; if index < slab.entries.as_ref().len() { - let entry = &mut slab.entries.as_mut()[index]; - - // iterator is not sorted, might need to recreate vacant list - if let Entry::Vacant { .. } = &*entry { - self.vacant_list_broken = true; - slab.meta.len += 1; + match replace(&mut slab.entries.as_mut()[index], Entry::Unknown) { + Entry::Vacant { .. } => { + self.vacant_list_broken = true; + slab.meta.len += 1; + } + Entry::Occupied { + #[cfg(feature = "range")] + range, + .. + } => { + #[cfg(feature = "range")] + slab.remove_occupied_index(index, range); + } + _ => unreachable!(), } + #[cfg(feature = "range")] + let range = slab.insert_occupied_index(index); + // if an element with this key already exists, replace it. // This is consistent with HashMap and BtreeMap - *entry = Entry::Occupied { + slab.entries.as_mut()[index] = Entry::Occupied { value, - key_data: Default::default(), + key_data: key.into_occupied_data(), + #[cfg(feature = "range")] + range, }; } else { if self.first_vacant_index.is_none() && slab.entries.as_ref().len() < index { @@ -58,9 +73,15 @@ where key_data: Default::default(), }); } + + #[cfg(feature = "range")] + let range = slab.insert_occupied_index(index); + slab.entries.push(Entry::Occupied { value, - key_data: Default::default(), + key_data: key.into_occupied_data(), + #[cfg(feature = "range")] + range, }); slab.meta.len += 1; } diff --git a/src/entries.rs b/src/entries.rs index fb313c8..611d1c0 100644 --- a/src/entries.rs +++ b/src/entries.rs @@ -112,6 +112,10 @@ where /// Key related data for this entry key_data: TKey::OccupiedData, + + /// Indices of the range feature + #[cfg(feature = "range")] + range: RangeIndices, }, /// Unknown state for this entry @@ -128,13 +132,20 @@ where fn clone(&self) -> Self { match self { Entry::Unknown => unreachable!(), - Entry::Vacant { next, key_data } => Self::Vacant { + Entry::Vacant { next, key_data } => Entry::Vacant { next: *next, key_data: key_data.clone(), }, - Entry::Occupied { value, key_data } => Self::Occupied { + Entry::Occupied { + value, + key_data, + #[cfg(feature = "range")] + range, + } => Entry::Occupied { value: value.clone(), key_data: key_data.clone(), + #[cfg(feature = "range")] + range: *range, }, } } @@ -149,17 +160,34 @@ where { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { - Self::Unknown => write!(f, "Unknown"), - Self::Vacant { next, key_data } => f + Entry::Unknown => write!(f, "Unknown"), + Entry::Vacant { next, key_data } => f .debug_struct("Vacant") .field("next", next) .field("key_data", key_data) .finish(), - Self::Occupied { value, key_data } => f - .debug_struct("Occupied") - .field("value", value) - .field("key_data", key_data) - .finish(), + Entry::Occupied { + value, + key_data, + #[cfg(feature = "range")] + range, + } => { + let mut s = f.debug_struct("Occupied"); + s.field("value", value); + s.field("key_data", key_data); + #[cfg(feature = "range")] + s.field("range", range); + s.finish() + } } } } + +/* RangeIndices */ + +#[cfg(feature = "range")] +#[derive(Debug, Clone, Copy)] +pub struct RangeIndices { + pub(crate) prev: usize, + pub(crate) next: usize, +} diff --git a/src/generic.rs b/src/generic.rs index 666ab1c..9af9fb7 100644 --- a/src/generic.rs +++ b/src/generic.rs @@ -9,6 +9,15 @@ use crate::entries::{DynamicEntries, Entries, Entry}; use crate::iter::{DrainMapper, GenericIter, KeyMapper, KeyValueMapper, ValueMapper}; use crate::key::Key; +#[cfg(feature = "range")] +use core::ops::{Bound, RangeBounds}; + +#[cfg(feature = "range")] +use crate::{ + entries::RangeIndices, + range_iter::{EntriesMutRef, EntriesRef, GenericRangeIter}, +}; + /// Pre-allocated generic storage for a uniform data type /// /// See the [module documentation] for more details. @@ -36,6 +45,14 @@ where // Index of the first vacant entry pub(crate) first_vacant: usize, + + /// Index of the first occupied entry + #[cfg(feature = "range")] + pub(crate) first_occupied: usize, + + /// Index of the last occupied entry + #[cfg(feature = "range")] + pub(crate) last_occupied: usize, } /// Represents a vacant entry in the [`GenericSlab`]. @@ -72,6 +89,15 @@ pub type ValuesMut<'a, T, TKey> = GenericIter>, /// Iterator that emits the items stored in the slab by consuming the slab. pub type IntoValues = GenericIter<::IntoIter, ValueMapper>; +/// Iterator that emits references to the stored items in insertion order in the selected range. +#[cfg(feature = "range")] +pub type RangeIter<'a, T, TKey, TEntries> = GenericRangeIter>; + +/// Iterator that emits mutable references to the stored items in insertion order in the selected range. +#[cfg(feature = "range")] +pub type RangeIterMut<'a, T, TKey, TEntries> = + GenericRangeIter>; + /// Iterator that emits the items stored in slab by draining the slab. pub type Drain<'a, T, TKey> = GenericIter>>, DrainMapper<&'a mut Meta>>; @@ -106,7 +132,7 @@ where { /// Construct a new, empty `Slab` using the provided `entries``. /// - /// Before the slab is created [`Entries::clear`] will be called. + /// Before the slab is created the passed `entries` will be cleared. /// /// # Examples /// @@ -121,6 +147,10 @@ where len: 0, key_context: TKey::Context::default(), first_vacant: 0, + #[cfg(feature = "range")] + first_occupied: crate::INVALID_INDEX, + #[cfg(feature = "range")] + last_occupied: crate::INVALID_INDEX, }; Self { meta, entries } @@ -168,6 +198,12 @@ where self.entries.clear(); self.meta.len = 0; self.meta.first_vacant = 0; + + #[cfg(feature = "range")] + { + self.meta.first_occupied = crate::INVALID_INDEX; + self.meta.last_occupied = crate::INVALID_INDEX; + } } /// Return the number of stored values. @@ -219,16 +255,9 @@ where /// assert_eq!(slab.get(key), Some(&"hello")); /// assert_eq!(slab.get(123), None); /// ``` + #[inline] pub fn get(&self, key: TKey) -> Option<&T> { - let index = key.index(&self.meta.key_context); - match self.entries.as_ref().get(index) { - Some(Entry::Occupied { value, key_data }) - if key.verify(&self.meta.key_context, key_data) => - { - Some(value) - } - _ => None, - } + self.get_at(&key) } /// Return a mutable reference to the value associated with the given key. @@ -248,16 +277,9 @@ where /// assert_eq!(slab[key], "world"); /// assert_eq!(slab.get_mut(123), None); /// ``` + #[inline] pub fn get_mut(&mut self, key: TKey) -> Option<&mut T> { - let index = key.index(&self.meta.key_context); - match self.entries.as_mut().get_mut(index) { - Some(Entry::Occupied { value, key_data }) - if key.verify(&self.meta.key_context, key_data) => - { - Some(value) - } - _ => None, - } + self.get_mut_at(&key) } /// Return two mutable references to the values associated with the two @@ -308,10 +330,12 @@ where Some(Entry::Occupied { value: value1, key_data: data1, + .. }), Some(Entry::Occupied { value: value2, key_data: data2, + .. }), ) if key1.verify(&self.meta.key_context, data1) && key2.verify(&self.meta.key_context, data2) => @@ -377,8 +401,9 @@ where /// /// assert_eq!(slab[key], 13); /// ``` - pub unsafe fn get_unchecked_mut(&mut self, key: usize) -> &mut T { - match self.entries.as_mut().get_unchecked_mut(key) { + pub unsafe fn get_unchecked_mut(&mut self, key: TKey) -> &mut T { + let index = key.index(&self.meta.key_context); + match self.entries.as_mut().get_unchecked_mut(index) { Entry::Occupied { value, .. } => value, _ => unreachable!(), } @@ -524,6 +549,8 @@ where /// vacant. As such, a slab with a capacity of 1 million but only one /// stored value must still iterate the million slots. /// + /// If you are interested in an efficient iterator, check [`Self::ordered_iter`]. + /// /// # Examples /// /// ``` @@ -557,6 +584,8 @@ where /// vacant. As such, a slab with a capacity of 1 million but only one /// stored value must still iterate the million slots. /// + /// If you are interested in an efficient iterator, check [`Self::ordered_iter`]. + /// /// # Examples /// /// ``` @@ -589,6 +618,8 @@ where /// vacant. As such, a slab with a capacity of 1 million but only one /// stored value must still iterate the million slots. /// + /// If you are interested in an efficient iterator, check [`Self::ordered_iter`]. + /// /// # Examples /// /// ``` @@ -637,10 +668,172 @@ where GenericIter::new(self.meta.len, self.entries.as_mut().iter_mut(), ValueMapper) } + /// Return an iterator that emits references to the stored items in insertion order. + /// + /// # Examples + /// + /// ``` + /// # use generic_slab::*; + /// let mut slab = Slab::new(); + /// + /// let key1 = slab.insert(0); + /// let key2 = slab.insert(1); + /// slab.remove(key1); + /// let key3 = slab.insert(2); + /// + /// // `ordered_iter`` returns items in insertion order + /// let mut iter = slab.ordered_iter(); + /// + /// assert_eq!(iter.next(), Some((1, &1))); + /// assert_eq!(iter.next(), Some((0, &2))); + /// assert_eq!(iter.next(), None); + /// + /// // while `iter`` returns items in storage order + /// let mut iter = slab.iter(); + /// + /// assert_eq!(iter.next(), Some((0, &2))); + /// assert_eq!(iter.next(), Some((1, &1))); + /// assert_eq!(iter.next(), None); + /// ``` + #[cfg(feature = "range")] + pub fn ordered_iter(&self) -> RangeIter<'_, T, TKey, TEntries> { + GenericRangeIter::new( + Bound::Included(self.meta.first_occupied), + Bound::Included(self.meta.last_occupied), + EntriesRef::new(self), + ) + } + + /// Return an iterator that emits mutable references to the stored items in insertion order. + /// + /// # Examples + /// + /// ``` + /// # use generic_slab::*; + /// let mut slab = Slab::new(); + /// + /// let key1 = slab.insert(0); + /// let key2 = slab.insert(1); + /// slab.remove(key1); + /// let key3 = slab.insert(2); + /// + /// // `ordered_iter`` returns items in insertion order + /// let mut iter = slab.ordered_iter_mut(); + /// + /// assert_eq!(iter.next(), Some((1, &mut 1))); + /// assert_eq!(iter.next(), Some((0, &mut 2))); + /// assert_eq!(iter.next(), None); + /// + /// // while `iter`` returns items in storage order + /// let mut iter = slab.iter_mut(); + /// + /// assert_eq!(iter.next(), Some((0, &mut 2))); + /// assert_eq!(iter.next(), Some((1, &mut 1))); + /// assert_eq!(iter.next(), None); + /// ``` + #[cfg(feature = "range")] + pub fn ordered_iter_mut(&mut self) -> RangeIterMut<'_, T, TKey, TEntries> { + GenericRangeIter::new( + Bound::Included(self.meta.first_occupied), + Bound::Included(self.meta.last_occupied), + EntriesMutRef::new(self), + ) + } + + /// Return an iterator that emits references to the stored items in + /// insertion order in the passed range. + /// + /// Hint: If the keys that are used in the range are not valid, the iterator + /// will be empty! + /// + /// # Examples + /// + /// ``` + /// # use generic_slab::*; + /// let mut slab = Slab::new(); + /// + /// let key0 = slab.insert(0); + /// let key1 = slab.insert(1); + /// let key2 = slab.insert(2); + /// let key3 = slab.insert(3); + /// let key4 = slab.insert(4); + /// let key5 = slab.insert(5); + /// let key6 = slab.insert(6); + /// + /// // simple range iterator + /// let mut iter = slab.range(key2..key5); + /// assert_eq!(iter.next(), Some((2, &2))); + /// assert_eq!(iter.next(), Some((3, &3))); + /// assert_eq!(iter.next(), Some((4, &4))); + /// assert_eq!(iter.next(), None); + /// + /// // a reverse range can be used to negate the set of returned items + /// let mut iter = slab.range(key5..key2); + /// assert_eq!(iter.next(), Some((5, &5))); + /// assert_eq!(iter.next(), Some((6, &6))); + /// assert_eq!(iter.next(), Some((0, &0))); + /// assert_eq!(iter.next(), Some((1, &1))); + /// assert_eq!(iter.next(), None); + /// ``` + #[cfg(feature = "range")] + pub fn range(&self, range: R) -> RangeIter<'_, T, TKey, TEntries> + where + R: RangeBounds, + { + let (front, back) = self.index_range(range); + + GenericRangeIter::new(front, back, EntriesRef::new(self)) + } + + /// Return an iterator that emits mutable references to the stored items in + /// insertion order in the passed range. + /// + /// Hint: If the keys that are used in the range are not valid, the iterator + /// will be empty! + /// + /// # Examples + /// + /// ``` + /// # use generic_slab::*; + /// let mut slab = Slab::new(); + /// + /// let key0 = slab.insert(0); + /// let key1 = slab.insert(1); + /// let key2 = slab.insert(2); + /// let key3 = slab.insert(3); + /// let key4 = slab.insert(4); + /// let key5 = slab.insert(5); + /// let key6 = slab.insert(6); + /// + /// // simple range iterator + /// let mut iter = slab.range_mut(key2..key5); + /// assert_eq!(iter.next(), Some((2, &mut 2))); + /// assert_eq!(iter.next(), Some((3, &mut 3))); + /// assert_eq!(iter.next(), Some((4, &mut 4))); + /// assert_eq!(iter.next(), None); + /// + /// // a reverse range can be used to negate the set of returned items + /// let mut iter = slab.range_mut(key5..key2); + /// assert_eq!(iter.next(), Some((5, &mut 5))); + /// assert_eq!(iter.next(), Some((6, &mut 6))); + /// assert_eq!(iter.next(), Some((0, &mut 0))); + /// assert_eq!(iter.next(), Some((1, &mut 1))); + /// assert_eq!(iter.next(), None); + /// ``` + #[cfg(feature = "range")] + pub fn range_mut(&mut self, range: R) -> RangeIterMut<'_, T, TKey, TEntries> + where + R: RangeBounds, + { + let (front, back) = self.index_range(range); + + GenericRangeIter::new(front, back, EntriesMutRef::new(self)) + } + /// Insert a value in the slab, returning key assigned to the value. /// /// The returned key can later be used to retrieve or remove the value using indexed - /// lookup and `remove`. Additional capacity is allocated if needed. See + /// slab and `remove`. Additional capacity is allocated if needed. See /// [Capacity and reallocation](index.html#capacity-and-reallocation). /// /// # Panics @@ -735,7 +928,14 @@ where pub fn contains(&self, key: TKey) -> bool { let index = key.index(&self.meta.key_context); - matches!(self.entries.as_ref().get(index), Some(Entry::Occupied { key_data, .. }) if key.verify(&self.meta.key_context, key_data)) + match self.entries.as_ref().get(index) { + Some(Entry::Occupied { key_data, .. }) + if key.verify(&self.meta.key_context, key_data) => + { + true + } + _ => false, + } } /// Tries to remove the value associated with the given key, @@ -759,11 +959,13 @@ where let index = key.index(&self.meta.key_context); let entry = self.entries.as_mut().get_mut(index); - if matches!(entry, Some(Entry::Occupied { key_data, .. }) if key.verify(&self.meta.key_context, key_data)) - { - Some(self.remove_at(index)) - } else { - None + match entry { + Some(Entry::Occupied { key_data, .. }) + if key.verify(&self.meta.key_context, key_data) => + { + Some(self.remove_at(index)) + } + _ => None, } } @@ -821,7 +1023,9 @@ where { for index in 0..self.entries.as_mut().len() { let keep = match &mut self.entries.as_mut()[index] { - Entry::Occupied { value, key_data } => f( + Entry::Occupied { + value, key_data, .. + } => f( TKey::new_occupied(&self.meta.key_context, index, key_data), value, ), @@ -984,6 +1188,8 @@ where if let Some(Entry::Occupied { mut value, key_data, + #[cfg(feature = "range")] + range, }) = guard.slab.entries.pop() { // Found one, now find a vacant entry to move it to @@ -1008,7 +1214,12 @@ where // Changing the key failed, so push the entry back on at its old index. guard.decrement = false; - guard.slab.entries.push(Entry::Occupied { value, key_data }); + guard.slab.entries.push(Entry::Occupied { + value, + key_data, + #[cfg(feature = "range")] + range, + }); guard.slab.entries.shrink_to_fit(); // Guard drop handles cleanup @@ -1025,8 +1236,15 @@ where *entry = Entry::Occupied { value, key_data: TKey::convert_into_occupied(&guard.slab.meta.key_context, key_data), + #[cfg(feature = "range")] + range, }; + #[cfg(feature = "range")] + guard + .slab + .move_occupied_index(old_index, occupied_until, range); + occupied_until += 1; } } @@ -1092,14 +1310,41 @@ where } } + fn get_at(&self, key: &TKey) -> Option<&T> { + let index = key.index(&self.meta.key_context); + match self.entries.as_ref().get(index) { + Some(Entry::Occupied { + value, key_data, .. + }) if key.verify(&self.meta.key_context, key_data) => Some(value), + _ => None, + } + } + + fn get_mut_at(&mut self, key: &TKey) -> Option<&mut T> { + let index = key.index(&self.meta.key_context); + match self.entries.as_mut().get_mut(index) { + Some(Entry::Occupied { + value, key_data, .. + }) if key.verify(&self.meta.key_context, key_data) => Some(value), + _ => None, + } + } + fn insert_at(&mut self, index: usize, value: T) { self.meta.len += 1; + #[cfg(feature = "range")] + let range = self.insert_occupied_index(index); + if index == self.entries.as_ref().len() { - self.entries.push(Entry::Occupied { + let entry = Entry::Occupied { value, key_data: Default::default(), - }); + #[cfg(feature = "range")] + range, + }; + + self.entries.push(entry); self.meta.first_vacant = index + 1; } else { let entry = &mut self.entries.as_mut()[index]; @@ -1108,7 +1353,12 @@ where Entry::Vacant { next, key_data } => { let key_data = TKey::convert_into_occupied(&self.meta.key_context, key_data); - *entry = Entry::Occupied { value, key_data }; + *entry = Entry::Occupied { + value, + key_data, + #[cfg(feature = "range")] + range, + }; self.meta.first_vacant = next; } @@ -1120,12 +1370,20 @@ where fn remove_at(&mut self, index: usize) -> T { let entry = &mut self.entries.as_mut()[index]; match replace(entry, Entry::Unknown) { - Entry::Occupied { value, key_data } => { + Entry::Occupied { + value, + key_data, + #[cfg(feature = "range")] + range, + } => { *entry = Entry::Vacant { next: self.meta.first_vacant, key_data: TKey::convert_into_vacant(&self.meta.key_context, key_data), }; + #[cfg(feature = "range")] + self.remove_occupied_index(index, range); + self.meta.len -= 1; self.meta.first_vacant = index; @@ -1161,6 +1419,130 @@ where } } } + + #[cfg(feature = "range")] + pub(crate) fn insert_occupied_index(&mut self, index: usize) -> RangeIndices { + let mut prev = index; + let mut next = index; + + if self.meta.last_occupied != crate::INVALID_INDEX { + prev = self.meta.last_occupied; + + match &mut self.entries.as_mut()[prev] { + Entry::Occupied { range, .. } => { + next = range.next; + range.next = index; + } + _ => unreachable!(), + } + + match &mut self.entries.as_mut()[next] { + Entry::Occupied { range, .. } => { + range.prev = index; + } + _ => unreachable!(), + } + } + + self.meta.last_occupied = index; + if self.meta.first_occupied == crate::INVALID_INDEX { + self.meta.first_occupied = index; + } + + RangeIndices { prev, next } + } + + #[cfg(feature = "range")] + pub(crate) fn remove_occupied_index(&mut self, index: usize, range: RangeIndices) { + let RangeIndices { prev, next } = range; + + if prev != index { + match &mut self.entries.as_mut()[prev] { + Entry::Occupied { range, .. } => range.next = next, + _ => unreachable!(), + } + } + + if next != index { + match &mut self.entries.as_mut()[next] { + Entry::Occupied { range, .. } => range.prev = prev, + _ => unreachable!(), + } + } + + if self.meta.first_occupied == index { + self.meta.first_occupied = if next == index { + crate::INVALID_INDEX + } else { + next + }; + } + + if self.meta.last_occupied == index { + self.meta.last_occupied = if prev == index { + crate::INVALID_INDEX + } else { + prev + }; + } + } + + #[cfg(feature = "range")] + fn move_occupied_index(&mut self, old_index: usize, new_index: usize, range: RangeIndices) { + if range.prev != old_index { + match &mut self.entries.as_mut()[range.prev] { + Entry::Occupied { range, .. } => { + range.next = new_index; + } + _ => unimplemented!(), + } + } + + if range.next != old_index { + match &mut self.entries.as_mut()[range.next] { + Entry::Occupied { range, .. } => { + range.prev = new_index; + } + _ => unimplemented!(), + } + } + + if self.meta.first_occupied == old_index { + self.meta.first_occupied = new_index; + } + + if self.meta.last_occupied == old_index { + self.meta.last_occupied = new_index; + } + } + + #[cfg(feature = "range")] + fn index_range(&self, range: R) -> (Bound, Bound) + where + R: RangeBounds, + { + let front = match range.start_bound() { + Bound::Included(key) if self.get_at(key).is_some() => { + Bound::Included(key.index(&self.meta.key_context)) + } + Bound::Excluded(key) if self.get_at(key).is_some() => { + Bound::Excluded(key.index(&self.meta.key_context)) + } + _ => Bound::Included(self.meta.first_occupied), + }; + + let back = match range.end_bound() { + Bound::Included(key) if self.get_at(key).is_some() => { + Bound::Included(key.index(&self.meta.key_context)) + } + Bound::Excluded(key) if self.get_at(key).is_some() => { + Bound::Excluded(key.index(&self.meta.key_context)) + } + _ => Bound::Included(self.meta.last_occupied), + }; + + (front, back) + } } impl GenericSlab @@ -1295,7 +1677,7 @@ where fmt.debug_struct("GenericSlab") .field("len", &self.meta.len) .field("cap", &self.capacity()) - .finish_non_exhaustive() + .finish() } } } @@ -1430,6 +1812,10 @@ where len: self.len, key_context: self.key_context.clone(), first_vacant: self.first_vacant, + #[cfg(feature = "range")] + first_occupied: self.first_occupied, + #[cfg(feature = "range")] + last_occupied: self.last_occupied, } } } @@ -1464,6 +1850,6 @@ where fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("GenericVacantEntry") .field("index", &self.index) - .finish_non_exhaustive() + .finish() } } diff --git a/src/handle.rs b/src/handle.rs index 373fe28..a3482a5 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -96,7 +96,7 @@ impl Key for Handle { Self { id: cx.id, index, - count: data.copied().unwrap_or_default(), + count: data.map(Clone::clone).unwrap_or_default(), marker: PhantomData, } } @@ -124,6 +124,11 @@ impl Key for Handle { data } + + #[inline] + fn into_occupied_data(self) -> Self::OccupiedData { + self.count + } } /* Context */ diff --git a/src/iter.rs b/src/iter.rs index f566c6d..79c172a 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -227,7 +227,9 @@ where let (index, entry) = item; match entry { - Entry::Occupied { value, key_data } => { + Entry::Occupied { + value, key_data, .. + } => { let key = TKey::new_occupied(&self.meta.borrow().key_context, index, &key_data); Some((key, value)) @@ -249,7 +251,9 @@ where let (index, entry) = item; match entry { - Entry::Occupied { value, key_data } => { + Entry::Occupied { + value, key_data, .. + } => { let key = TKey::new_occupied(&self.meta.borrow().key_context, index, key_data); Some((key, value)) @@ -271,7 +275,9 @@ where let (index, entry) = item; match entry { - Entry::Occupied { value, key_data } => { + Entry::Occupied { + value, key_data, .. + } => { let key = TKey::new_occupied(&self.meta.borrow().key_context, index, key_data); Some((key, value)) @@ -308,7 +314,9 @@ where let (index, entry) = item; match replace(entry, Entry::Unknown) { - Entry::Occupied { value, key_data } => { + Entry::Occupied { + value, key_data, .. + } => { let meta = self.meta.borrow_mut(); let key_data = TKey::convert_into_vacant(&meta.key_context, key_data); diff --git a/src/key.rs b/src/key.rs index 32e0d7d..3663ae4 100644 --- a/src/key.rs +++ b/src/key.rs @@ -31,6 +31,9 @@ pub trait Key { /// /// This is mainly called when an item is inserted in an vacant entry. fn convert_into_occupied(cx: &Self::Context, data: Self::VacantData) -> Self::OccupiedData; + + /// Convert a key into the data that is stored for occupied entries. + fn into_occupied_data(self) -> Self::OccupiedData; } #[derive(Default, Debug, Clone, Copy)] @@ -85,4 +88,9 @@ impl Key for usize { data } + + #[inline] + fn into_occupied_data(self) -> Self::OccupiedData { + NoData + } } diff --git a/src/lib.rs b/src/lib.rs index 123db2c..2d52cbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,9 @@ mod handle; mod iter; mod key; +#[cfg(feature = "range")] +mod range_iter; + use alloc::vec::Vec; pub use crate::entries::Entry; @@ -199,3 +202,6 @@ pub type Iter<'a, T> = generic::Iter<'a, T, usize>; /// A mutable iterator over the values stored in the `Slab` pub type IterMut<'a, T> = generic::IterMut<'a, T, usize>; + +#[cfg(feature = "range")] +pub(crate) const INVALID_INDEX: usize = core::usize::MAX; diff --git a/src/range_iter.rs b/src/range_iter.rs new file mode 100644 index 0000000..fbee085 --- /dev/null +++ b/src/range_iter.rs @@ -0,0 +1,285 @@ +use core::fmt::{Debug, Formatter, Result as FmtResult}; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::ops::Bound; + +use crate::entries::{Entries, Entry}; +use crate::generic::GenericSlab; +use crate::key::Key; +use crate::INVALID_INDEX; + +#[derive(Debug, Clone)] +pub struct GenericRangeIter { + front: Bound, + back: Bound, + inner: TInner, +} + +impl GenericRangeIter { + pub(crate) fn new(front: Bound, back: Bound, inner: TInner) -> Self { + let front = match front { + Bound::Excluded(index) | Bound::Included(index) if index == INVALID_INDEX => { + Bound::Unbounded + } + front => front, + }; + + let back = match back { + Bound::Excluded(index) | Bound::Included(index) if index == INVALID_INDEX => { + Bound::Unbounded + } + back => back, + }; + + let (front, back) = match (front, back) { + (Bound::Unbounded, _) | (_, Bound::Unbounded) => (Bound::Unbounded, Bound::Unbounded), + (front, back) => (front, back), + }; + + Self { front, back, inner } + } +} + +impl Iterator for GenericRangeIter +where + TInner: IterImpl, +{ + type Item = TInner::Item; + + fn next(&mut self) -> Option { + loop { + let (front, skip) = match self.front { + Bound::Unbounded => return None, + Bound::Included(front) => (front, false), + Bound::Excluded(front) => (front, true), + }; + + // This is safe because we do not use `inner` until `item` is dropped + let (next, item) = self.inner.get_next(front); + + match self.back { + Bound::Unbounded => return None, + Bound::Excluded(back) if front == back => { + self.front = Bound::Unbounded; + self.back = Bound::Unbounded; + + return None; + } + Bound::Included(back) if front == back => { + self.front = Bound::Unbounded; + self.back = Bound::Unbounded; + } + _ => (), + } + + self.front = Bound::Included(next); + + if !skip { + return Some(item); + } + } + } +} + +impl DoubleEndedIterator for GenericRangeIter +where + TInner: IterImpl, +{ + fn next_back(&mut self) -> Option { + loop { + let (back, skip) = match self.back { + Bound::Unbounded => return None, + Bound::Included(back) => (back, false), + Bound::Excluded(back) => (back, true), + }; + + // This is safe because we do not use `inner` until `item` is dropped + let (next, item) = self.inner.get_prev(back); + + match self.front { + Bound::Unbounded => return None, + Bound::Excluded(front) if front == back => { + self.front = Bound::Unbounded; + self.back = Bound::Unbounded; + + return None; + } + Bound::Included(front) if front == back => { + self.front = Bound::Unbounded; + self.back = Bound::Unbounded; + } + _ => (), + } + + self.back = Bound::Included(next); + + if !skip { + return Some(item); + } + } + } +} + +impl FusedIterator for GenericRangeIter where TInner: IterImpl {} + +pub trait IterImpl: Sized { + type Item; + + /// Try to get the item stored at `index` and the index of the next item. + /// + /// # Returns + /// - `Some((prev, item))` - If the item at `index` is valid. + /// - `None` - If the item at `index` is not valid. + fn get_next(&mut self, index: usize) -> (usize, Self::Item); + + /// Try to get the item stored at `index` and the index of the previous item. + /// + /// # Returns + /// - `Some((prev, item))` - If the item at `index` is valid. + /// - `None` - If the item at `index` is not valid. + fn get_prev(&mut self, index: usize) -> (usize, Self::Item); +} + +/* EntriesRef */ + +pub struct EntriesRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + slab: &'a GenericSlab, + key: PhantomData, + value: PhantomData, +} + +impl<'a, T, TKey, TEntries> EntriesRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + pub fn new(slab: &'a GenericSlab) -> Self { + Self { + slab, + key: PhantomData, + value: PhantomData, + } + } +} + +impl<'a, T, TKey, TEntries> IterImpl for EntriesRef<'a, T, TKey, TEntries> +where + TKey: Key, + TEntries: Entries, +{ + type Item = (TKey, &'a T); + + fn get_next(&mut self, index: usize) -> (usize, Self::Item) { + match &self.slab.entries.as_ref()[index] { + Entry::Occupied { + value, + key_data, + range, + } => { + let key = TKey::new_occupied(&self.slab.meta.key_context, index, key_data); + + (range.next, (key, value)) + } + _ => unimplemented!(), + } + } + + fn get_prev(&mut self, index: usize) -> (usize, Self::Item) { + match &self.slab.entries.as_ref()[index] { + Entry::Occupied { + value, + key_data, + range, + } => { + let key = TKey::new_occupied(&self.slab.meta.key_context, index, key_data); + + (range.prev, (key, value)) + } + _ => unimplemented!(), + } + } +} + +impl<'a, T, TKey, TEntries> Debug for EntriesRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "EntriesRef") + } +} + +/* EntriesMutRef */ + +pub struct EntriesMutRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + slab: &'a mut GenericSlab, + key: PhantomData, + value: PhantomData, +} + +impl<'a, T, TKey, TEntries> EntriesMutRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + pub fn new(slab: &'a mut GenericSlab) -> Self { + Self { + slab, + key: PhantomData, + value: PhantomData, + } + } +} + +impl<'a, T, TKey, TEntries> IterImpl for EntriesMutRef<'a, T, TKey, TEntries> +where + TKey: Key, + TEntries: Entries, +{ + type Item = (TKey, &'a mut T); + + fn get_next(&mut self, index: usize) -> (usize, Self::Item) { + match &mut self.slab.entries.as_mut()[index] { + Entry::Occupied { + value, + key_data, + range, + } => { + let key = TKey::new_occupied(&self.slab.meta.key_context, index, key_data); + let value = unsafe { &mut *(value as *mut _) }; + + (range.next, (key, value)) + } + _ => unimplemented!(), + } + } + + fn get_prev(&mut self, index: usize) -> (usize, Self::Item) { + match &mut self.slab.entries.as_mut()[index] { + Entry::Occupied { + value, + key_data, + range, + } => { + let key = TKey::new_occupied(&self.slab.meta.key_context, index, key_data); + let value = unsafe { &mut *(value as *mut _) }; + + (range.prev, (key, value)) + } + _ => unimplemented!(), + } + } +} + +impl<'a, T, TKey, TEntries> Debug for EntriesMutRef<'a, T, TKey, TEntries> +where + TKey: Key, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "EntriesMutRef") + } +} diff --git a/tests/range.rs b/tests/range.rs new file mode 100644 index 0000000..c6b96b6 --- /dev/null +++ b/tests/range.rs @@ -0,0 +1,141 @@ +#![cfg(feature = "range")] +#![warn(rust_2018_idioms)] + +use std::ops::Bound; + +use generic_slab::Slab; + +#[derive(Eq, PartialEq, Debug)] +struct Data(pub usize); + +macro_rules! range_test { + ($name:ident, $start:expr, $end:expr, $expected:expr) => { + #[test] + fn $name() { + let lookup = test_lookup_large(); + + let range: (Bound, Bound) = ($start.into(), $end.into()); + let expected: Vec = $expected; + let actual = lookup.range(range).map(|(_, d)| d.0).collect::>(); + assert_eq!(expected, actual); + } + }; +} + +range_test!( + range_unbound_unbound, + Bound::Unbounded, + Bound::Unbounded, + vec![0, 1, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15] +); + +range_test!( + range_included_unbound, + Bound::Included(7), + Bound::Unbounded, + vec![7, 8, 9, 10, 11, 12, 13, 14, 15] +); + +range_test!( + range_excluded_unbound, + Bound::Excluded(7), + Bound::Unbounded, + vec![8, 9, 10, 11, 12, 13, 14, 15] +); + +range_test!( + range_unbound_included, + Bound::Unbounded, + Bound::Included(7), + vec![0, 1, 3, 4, 7] +); + +range_test!( + range_unbound_excluded, + Bound::Unbounded, + Bound::Excluded(7), + vec![0, 1, 3, 4] +); + +range_test!( + range_included_included, + Bound::Included(7), + Bound::Included(10), + vec![7, 8, 9, 10, 11, 12, 13] +); + +range_test!( + range_excluded_excluded, + Bound::Excluded(7), + Bound::Excluded(10), + vec![8, 9, 10, 11, 12] +); + +range_test!( + range_included_excluded, + Bound::Included(7), + Bound::Excluded(10), + vec![7, 8, 9, 10, 11, 12] +); + +range_test!( + range_excluded_included, + Bound::Excluded(7), + Bound::Included(10), + vec![8, 9, 10, 11, 12, 13] +); + +range_test!( + range_included_excluded_edge_case, + Bound::Included(7), + Bound::Excluded(7), + vec![] +); + +range_test!( + range_included_included_reverse, + Bound::Included(10), + Bound::Included(7), + vec![13, 14, 15, 0, 1, 3, 4, 7] +); + +/// Create a large lookup with the following data layout +/// - [ 0] Occupied data=0 next=1 prev=5 first_occupied +/// - [ 1] Occupied data=1 next=3 prev=0 +/// - [ 2] Occupied data=10 next=6 prev=5 +/// - [ 3] Occupied data=3 next=4 prev=1 +/// - [ 4] Occupied data=4 next=7 prev=3 +/// - [ 5] Occupied data=9 next=2 prev=8 +/// - [ 6] Occupied data=11 next=9 prev=2 +/// - [ 7] Occupied data=7 next=8 prev=4 +/// - [ 8] Occupied data=8 next=5 prev=7 +/// - [ 9] Occupied data=12 next=10 prev=6 +/// - [10] Occupied data=13 next=11 prev=9 +/// - [11] Occupied data=14 next=12 prev=10 +/// - [12] Occupied data=15 next=0 prev=11 last_occupied +fn test_lookup_large() -> Slab { + let mut lookup = Slab::::new(); + lookup.insert(Data(0)); + lookup.insert(Data(1)); + let handle2 = lookup.insert(Data(2)); + lookup.insert(Data(3)); + lookup.insert(Data(4)); + let handle5 = lookup.insert(Data(5)); + let handle6 = lookup.insert(Data(6)); + lookup.insert(Data(7)); + lookup.insert(Data(8)); + + lookup.remove(handle5); + lookup.remove(handle2); + lookup.remove(handle6); + + lookup.insert(Data(9)); + lookup.insert(Data(10)); + lookup.insert(Data(11)); + lookup.insert(Data(12)); + lookup.insert(Data(13)); + lookup.insert(Data(14)); + lookup.insert(Data(15)); + + lookup +}