From 4ac2e31c08cf771b262446c31b58d9d4c2b7b378 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 26 Nov 2025 10:34:30 -0800 Subject: [PATCH] Move crate::map::core to crate::inner --- src/{map/core.rs => inner.rs} | 56 ++-- src/{map/core => inner}/entry.rs | 447 ++--------------------------- src/{map/core => inner}/extract.rs | 6 +- src/lib.rs | 1 + src/map.rs | 32 ++- src/map/entry.rs | 435 ++++++++++++++++++++++++++++ src/map/iter.rs | 7 +- src/map/{core => }/raw_entry_v1.rs | 22 +- src/set/iter.rs | 5 +- 9 files changed, 535 insertions(+), 476 deletions(-) rename src/{map/core.rs => inner.rs} (97%) rename src/{map/core => inner}/entry.rs (50%) rename src/{map/core => inner}/extract.rs (97%) create mode 100644 src/map/entry.rs rename src/map/{core => }/raw_entry_v1.rs (97%) diff --git a/src/map/core.rs b/src/inner.rs similarity index 97% rename from src/map/core.rs rename to src/inner.rs index 9d4d833..2208ace 100644 --- a/src/map/core.rs +++ b/src/inner.rs @@ -1,16 +1,14 @@ //! This is the core implementation that doesn't depend on the hasher at all. //! -//! The methods of `RingMapCore` don't use any Hash properties of K. +//! The methods of `Core` don't use any Hash properties of K. //! //! It's cleaner to separate them out, then the compiler checks that we are not //! using Hash at all in these methods. //! //! However, we should probably not let this show in the public API or docs. -mod entry; -mod extract; - -pub mod raw_entry_v1; +pub(crate) mod entry; +pub(crate) mod extract; use hashbrown::hash_table; @@ -25,9 +23,6 @@ use crate::{Bucket, Equivalent, HashValue, TryReserveError}; type Indices = hash_table::HashTable; type Entries = VecDeque>; -pub use entry::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; -pub(crate) use extract::ExtractCore; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct OffsetIndex { raw: usize, @@ -48,8 +43,8 @@ impl OffsetIndex { } /// Core of the map that does not depend on S -#[derive(Debug)] -pub(crate) struct RingMapCore { +#[cfg_attr(feature = "test_debug", derive(Debug))] +pub(crate) struct Core { /// indices mapping from the entry hash to its index, with an offset. /// i.e. `entries[i]` is stored as `i.wrapping_add(offset)` in `indices`. indices: Indices, @@ -149,7 +144,7 @@ fn insert_bulk_no_grow(indices: &mut Indices, offset: usize, entries: Pair } } -impl Clone for RingMapCore +impl Clone for Core where K: Clone, V: Clone, @@ -172,13 +167,13 @@ where } } -impl RingMapCore { +impl Core { /// The maximum capacity before the `entries` allocation would exceed `isize::MAX`. const MAX_ENTRIES_CAPACITY: usize = (isize::MAX as usize) / size_of::>(); #[inline] pub(crate) const fn new() -> Self { - RingMapCore { + Core { indices: Indices::new(), entries: VecDeque::new(), offset: 0, @@ -187,7 +182,7 @@ impl RingMapCore { #[inline] pub(crate) fn with_capacity(n: usize) -> Self { - RingMapCore { + Core { indices: Indices::with_capacity(n), entries: VecDeque::with_capacity(n), offset: 0, @@ -411,6 +406,18 @@ impl RingMapCore { Some(oi.get(self.offset)) } + /// Return the index in `entries` where an equivalent key can be found + pub(crate) fn get_index_of_raw(&self, hash: HashValue, mut is_match: F) -> Option + where + F: FnMut(&K) -> bool, + { + let entries = &self.entries; + let offset = self.offset; + let eq = move |&i: &OffsetIndex| is_match(&entries[i.get(offset)].key); + let oi = self.indices.find(hash.get(), eq)?; + Some(oi.get(self.offset)) + } + pub(crate) fn push_back(&mut self, hash: HashValue, key: K, value: V) -> (usize, Option) where K: Eq, @@ -715,7 +722,12 @@ impl RingMapCore { &mut self.entries[0] } - fn push_back_unique(&mut self, hash: HashValue, key: K, value: V) -> &mut Bucket { + pub(crate) fn push_back_unique( + &mut self, + hash: HashValue, + key: K, + value: V, + ) -> &mut Bucket { let i = self.indices.len(); debug_assert_eq!(i, self.entries.len()); let oi = OffsetIndex::new(i, self.offset); @@ -744,7 +756,13 @@ impl RingMapCore { /// Insert a key-value pair in `entries` at a particular index, /// *without* checking whether it already exists. - fn shift_insert_unique(&mut self, index: usize, hash: HashValue, key: K, value: V) { + pub(super) fn shift_insert_unique( + &mut self, + index: usize, + hash: HashValue, + key: K, + value: V, + ) -> &mut Bucket { let end = self.indices.len(); assert!(index <= end); // Increment others first so we don't have duplicate indices. @@ -765,6 +783,7 @@ impl RingMapCore { self.reserve_entries(1); } self.entries.insert(index, Bucket { hash, key, value }); + &mut self.entries[index] } /// Remove an entry by shifting all entries that follow it @@ -1036,8 +1055,5 @@ impl RingMapCore { #[test] fn assert_send_sync() { fn assert_send_sync() {} - assert_send_sync::>(); - assert_send_sync::>(); - assert_send_sync::>(); - assert_send_sync::>(); + assert_send_sync::>(); } diff --git a/src/map/core/entry.rs b/src/inner/entry.rs similarity index 50% rename from src/map/core/entry.rs rename to src/inner/entry.rs index a8a0f83..6c56125 100644 --- a/src/map/core/entry.rs +++ b/src/inner/entry.rs @@ -1,19 +1,11 @@ -use super::{equivalent, get_hash, Bucket, OffsetIndex, RingMapCore}; +use super::{equivalent, get_hash, Bucket, Core, OffsetIndex}; +use crate::map::{Entry, IndexedEntry}; use crate::HashValue; use core::cmp::Ordering; -use core::{fmt, mem}; - -/// Entry for an existing key-value pair in an [`RingMap`][crate::RingMap] -/// or a vacant location to insert one. -pub enum Entry<'a, K, V> { - /// Existing slot with equivalent key. - Occupied(OccupiedEntry<'a, K, V>), - /// Vacant slot (no equivalent key in the map). - Vacant(VacantEntry<'a, K, V>), -} +use core::mem; impl<'a, K, V> Entry<'a, K, V> { - pub(crate) fn new(map: &'a mut RingMapCore, hash: HashValue, key: K) -> Self + pub(crate) fn new(map: &'a mut Core, hash: HashValue, key: K) -> Self where K: Eq, { @@ -29,221 +21,12 @@ impl<'a, K, V> Entry<'a, K, V> { Err(_) => Entry::Vacant(VacantEntry { map, hash, key }), } } - - /// Return the index where the key-value pair exists or may be appended. - /// - /// Note that some methods may instead prepend new items at index 0. - pub fn index(&self) -> usize { - match self { - Entry::Occupied(entry) => entry.index, - Entry::Vacant(entry) => entry.index(), - } - } - - #[deprecated = "use `push_back_entry` or `push_front_entry` instead"] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - self.push_back_entry(value) - } - - /// Sets the value of the entry (after appending if vacant), and returns an `OccupiedEntry`. - /// - /// Computes in **O(1)** time (amortized average). - pub fn push_back_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - match self { - Entry::Occupied(mut entry) => { - entry.insert(value); - entry - } - Entry::Vacant(entry) => entry.push_back_entry(value), - } - } - - /// Sets the value of the entry (after prepending if vacant), and returns an `OccupiedEntry`. - /// - /// Computes in **O(1)** time (amortized average). - pub fn push_front_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - match self { - Entry::Occupied(mut entry) => { - entry.insert(value); - entry - } - Entry::Vacant(entry) => entry.push_front_entry(value), - } - } - - #[deprecated = "use `or_push_back` or `or_push_front` instead"] - pub fn or_insert(self, default: V) -> &'a mut V { - self.or_push_back(default) - } - - /// Appends the given default value in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_back(self, default: V) -> &'a mut V { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.push_back(default), - } - } - - /// Prepends the given default value in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_front(self, default: V) -> &'a mut V { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.push_front(default), - } - } - - #[deprecated = "use `or_push_back_default` or `or_push_front_default` instead"] - pub fn or_default(self) -> &'a mut V - where - V: Default, - { - self.or_push_back_default() - } - - /// Appends a default-constructed value in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_back_default(self) -> &'a mut V - where - V: Default, - { - self.or_push_back_with(V::default) - } - - /// Prepends a default-constructed value in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_front_default(self) -> &'a mut V - where - V: Default, - { - self.or_push_front_with(V::default) - } - - #[deprecated = "use `or_push_back_with` or `or_push_front_with` instead"] - pub fn or_insert_with(self, call: F) -> &'a mut V - where - F: FnOnce() -> V, - { - self.or_push_back_with(call) - } - - /// Appends the result of the `call` function in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_back_with(self, call: F) -> &'a mut V - where - F: FnOnce() -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.push_back(call()), - } - } - - /// Prepends the result of the `call` function in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_front_with(self, call: F) -> &'a mut V - where - F: FnOnce() -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.push_front(call()), - } - } - - #[deprecated = "use `or_push_back_with_key` or `or_push_front_with_key` instead"] - pub fn or_insert_with_key(self, call: F) -> &'a mut V - where - F: FnOnce(&K) -> V, - { - self.or_push_back_with_key(call) - } - - /// Appends the result of the `call` function with a reference to the entry's key if it is - /// vacant, and returns a mutable reference to the new value. Otherwise a mutable reference to - /// an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_back_with_key(self, call: F) -> &'a mut V - where - F: FnOnce(&K) -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - let value = call(&entry.key); - entry.push_back(value) - } - } - } - - /// Prepends the result of the `call` function with a reference to the entry's key if it is - /// vacant, and returns a mutable reference to the new value. Otherwise a mutable reference to - /// an already existent value is returned. - /// - /// Computes in **O(1)** time (amortized average). - pub fn or_push_front_with_key(self, call: F) -> &'a mut V - where - F: FnOnce(&K) -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - let value = call(&entry.key); - entry.push_front(value) - } - } - } - - /// Gets a reference to the entry's key, either within the map if occupied, - /// or else the new key that was used to find the entry. - pub fn key(&self) -> &K { - match *self { - Entry::Occupied(ref entry) => entry.key(), - Entry::Vacant(ref entry) => entry.key(), - } - } - - /// Modifies the entry if it is occupied. - pub fn and_modify(mut self, f: F) -> Self - where - F: FnOnce(&mut V), - { - if let Entry::Occupied(entry) = &mut self { - f(entry.get_mut()); - } - self - } -} - -impl fmt::Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut tuple = f.debug_tuple("Entry"); - match self { - Entry::Vacant(v) => tuple.field(v), - Entry::Occupied(o) => tuple.field(o), - }; - tuple.finish() - } } /// A view into an occupied entry in an [`RingMap`][crate::RingMap]. /// It is part of the [`Entry`] enum. pub struct OccupiedEntry<'a, K, V> { - map: &'a mut RingMapCore, + map: &'a mut Core, // We have a mutable reference to the map, which keeps these two // indices valid and pointing to the correct entry. index: usize, @@ -252,18 +35,18 @@ pub struct OccupiedEntry<'a, K, V> { impl<'a, K, V> OccupiedEntry<'a, K, V> { /// Constructor for `RawEntryMut::from_hash` - pub(super) fn from_hash( - map: &'a mut RingMapCore, - hash: u64, + pub(crate) fn from_hash( + map: &'a mut Core, + hash: HashValue, mut is_match: F, - ) -> Result> + ) -> Result> where F: FnMut(&K) -> bool, { let entries = &map.entries; let offset = map.offset; let eq = move |&i: &OffsetIndex| is_match(&entries[i.get(offset)].key); - match map.indices.find_entry(hash, eq) { + match map.indices.find_entry(hash.get(), eq) { Ok(entry) => Ok(OccupiedEntry { bucket: entry.bucket_index(), index: entry.get().get(offset), @@ -291,18 +74,22 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { self.index } + pub(crate) fn into_core(self) -> &'a mut Core { + self.map + } + /// Gets a reference to the entry's key in the map. /// /// Note that this is not the key that was used to find the entry. There may be an observable /// difference if the key type has any distinguishing features outside of `Hash` and `Eq`, like /// extra fields or the memory address of an allocation. pub fn key(&self) -> &K { - &self.map.entries[self.index].key + &self.get_bucket().key } /// Gets a reference to the entry's value in the map. pub fn get(&self) -> &V { - &self.map.entries[self.index].value + &self.get_bucket().value } /// Gets a mutable reference to the entry's value in the map. @@ -310,13 +97,13 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// If you need a reference which may outlive the destruction of the /// [`Entry`] value, see [`into_mut`][Self::into_mut]. pub fn get_mut(&mut self) -> &mut V { - &mut self.map.entries[self.index].value + &mut self.get_bucket_mut().value } /// Converts into a mutable reference to the entry's value in the map, /// with a lifetime bound to the map itself. pub fn into_mut(self) -> &'a mut V { - &mut self.map.entries[self.index].value + &mut self.into_bucket().value } /// Sets the value of the entry to `value`, and returns the entry's old value. @@ -451,18 +238,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { } } -impl fmt::Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry") - .field("key", self.key()) - .field("value", self.get()) - .finish() - } -} - impl<'a, K, V> From> for OccupiedEntry<'a, K, V> { fn from(other: IndexedEntry<'a, K, V>) -> Self { - let IndexedEntry { map, index } = other; + let index = other.index(); + let map = other.into_core(); let hash = map.entries[index].hash; let needle = OffsetIndex::new(index, map.offset); let bucket = map @@ -476,7 +255,7 @@ impl<'a, K, V> From> for OccupiedEntry<'a, K, V> { /// A view into a vacant entry in an [`RingMap`][crate::RingMap]. /// It is part of the [`Entry`] enum. pub struct VacantEntry<'a, K, V> { - map: &'a mut RingMapCore, + map: &'a mut Core, hash: HashValue, key: K, } @@ -624,8 +403,8 @@ impl<'a, K, V> VacantEntry<'a, K, V> { #[track_caller] pub fn shift_insert(self, index: usize, value: V) -> &'a mut V { self.map - .shift_insert_unique(index, self.hash, self.key, value); - &mut self.map.entries[index].value + .shift_insert_unique(index, self.hash, self.key, value) + .value_mut() } /// Replaces the key at the given index with this entry's key, returning the @@ -659,183 +438,3 @@ impl<'a, K, V> VacantEntry<'a, K, V> { (old_key, OccupiedEntry { map, index, bucket }) } } - -impl fmt::Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() - } -} - -/// A view into an occupied entry in an [`RingMap`][crate::RingMap] obtained by index. -/// -/// This `struct` is created from the [`get_index_entry`][crate::RingMap::get_index_entry] method. -pub struct IndexedEntry<'a, K, V> { - map: &'a mut RingMapCore, - // We have a mutable reference to the map, which keeps the index - // valid and pointing to the correct entry. - index: usize, -} - -impl<'a, K, V> IndexedEntry<'a, K, V> { - pub(crate) fn new(map: &'a mut RingMapCore, index: usize) -> Option { - if index < map.len() { - Some(Self { map, index }) - } else { - None - } - } - - /// Return the index of the key-value pair - #[inline] - pub fn index(&self) -> usize { - self.index - } - - /// Gets a reference to the entry's key in the map. - pub fn key(&self) -> &K { - &self.map.entries[self.index].key - } - - pub(crate) fn key_mut(&mut self) -> &mut K { - &mut self.map.entries[self.index].key - } - - /// Gets a reference to the entry's value in the map. - pub fn get(&self) -> &V { - &self.map.entries[self.index].value - } - - /// Gets a mutable reference to the entry's value in the map. - /// - /// If you need a reference which may outlive the destruction of the - /// `IndexedEntry` value, see [`into_mut`][Self::into_mut]. - pub fn get_mut(&mut self) -> &mut V { - &mut self.map.entries[self.index].value - } - - /// Sets the value of the entry to `value`, and returns the entry's old value. - pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) - } - - /// Converts into a mutable reference to the entry's value in the map, - /// with a lifetime bound to the map itself. - pub fn into_mut(self) -> &'a mut V { - &mut self.map.entries[self.index].value - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// Like [`VecDeque::remove`][super::VecDeque::remove], the pair is removed by shifting all of the - /// elements either before or after it, preserving their relative order. - /// **This perturbs the index of all of the following elements!** - /// - /// Computes in **O(n)** time (average). - pub fn remove_entry(self) -> (K, V) { - self.map.shift_remove_index(self.index).unwrap() - } - - /// Remove the key, value pair stored in the map for this entry, and return the value. - /// - /// Like [`VecDeque::remove`][super::VecDeque::remove], the pair is removed by shifting all of the - /// elements either before or after it, preserving their relative order. - /// **This perturbs the index of all of the following elements!** - /// - /// Computes in **O(n)** time (average). - pub fn remove(self) -> V { - self.remove_entry().1 - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// Like [`VecDeque::swap_remove_back`][super::VecDeque::swap_remove_back], the pair is removed - /// by swapping it with the last element of the map and popping it off. - /// **This perturbs the position of what used to be the last element!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove_back_entry(self) -> (K, V) { - self.map.swap_remove_back_index(self.index).unwrap() - } - - /// Remove the key, value pair stored in the map for this entry, and return the value. - /// - /// Like [`VecDeque::swap_remove_back`][super::VecDeque::swap_remove_back], the pair is removed - /// by swapping it with the last element of the map and popping it off. - /// **This perturbs the position of what used to be the last element!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove_back(self) -> V { - self.swap_remove_back_entry().1 - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// Like [`VecDeque::swap_remove_front`][super::VecDeque::swap_remove_front], the pair is removed - /// by swapping it with the front element of the map and popping it off. - /// **This perturbs the position of what used to be the front element!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove_front_entry(self) -> (K, V) { - self.map.swap_remove_front_index(self.index).unwrap() - } - - /// Remove the key, value pair stored in the map for this entry, and return the value. - /// - /// Like [`VecDeque::swap_remove_front`][super::VecDeque::swap_remove_front], the pair is removed - /// by swapping it with the front element of the map and popping it off. - /// **This perturbs the position of what used to be the front element!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove_front(self) -> V { - self.swap_remove_front_entry().1 - } - - /// Moves the position of the entry to a new index - /// by shifting all other entries in-between. - /// - /// This is equivalent to [`RingMap::move_index`][`crate::RingMap::move_index`] - /// coming `from` the current [`.index()`][Self::index]. - /// - /// * If `self.index() < to`, the other pairs will shift down while the targeted pair moves up. - /// * If `self.index() > to`, the other pairs will shift up while the targeted pair moves down. - /// - /// ***Panics*** if `to` is out of bounds. - /// - /// Computes in **O(n)** time (average). - #[track_caller] - pub fn move_index(self, to: usize) { - self.map.move_index(self.index, to); - } - - /// Swaps the position of entry with another. - /// - /// This is equivalent to [`RingMap::swap_indices`][`crate::RingMap::swap_indices`] - /// with the current [`.index()`][Self::index] as one of the two being swapped. - /// - /// ***Panics*** if the `other` index is out of bounds. - /// - /// Computes in **O(1)** time (average). - #[track_caller] - pub fn swap_indices(self, other: usize) { - self.map.swap_indices(self.index, other); - } -} - -impl fmt::Debug for IndexedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("IndexedEntry") - .field("index", &self.index) - .field("key", self.key()) - .field("value", self.get()) - .finish() - } -} - -impl<'a, K, V> From> for IndexedEntry<'a, K, V> { - fn from(other: OccupiedEntry<'a, K, V>) -> Self { - Self { - map: other.map, - index: other.index, - } - } -} diff --git a/src/map/core/extract.rs b/src/inner/extract.rs similarity index 97% rename from src/map/core/extract.rs rename to src/inner/extract.rs index 5cffd41..156b516 100644 --- a/src/map/core/extract.rs +++ b/src/inner/extract.rs @@ -1,13 +1,13 @@ #![allow(unsafe_code)] -use super::{Bucket, RingMapCore}; +use super::{Bucket, Core}; use crate::util::simplify_range; use alloc::vec::Vec; use core::mem; use core::ops::RangeBounds; -impl RingMapCore { +impl Core { #[track_caller] pub(crate) fn extract(&mut self, range: R) -> ExtractCore<'_, K, V> where @@ -36,7 +36,7 @@ impl RingMapCore { } pub(crate) struct ExtractCore<'a, K, V> { - map: &'a mut RingMapCore, + map: &'a mut Core, entries: Vec>, new_len: usize, current: usize, diff --git a/src/lib.rs b/src/lib.rs index 09c75eb..3960350 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,6 +104,7 @@ extern crate alloc; extern crate std; mod arbitrary; +mod inner; #[macro_use] mod macros; #[cfg(feature = "borsh")] diff --git a/src/map.rs b/src/map.rs index 8d7b4bc..0962eb6 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,11 +1,13 @@ //! [`RingMap`] is a hash table where the iteration order of the key-value //! pairs is independent of the hash values of the keys. -mod core; +mod entry; mod iter; mod mutable; mod slice; +pub mod raw_entry_v1; + #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] pub mod serde_seq; @@ -13,14 +15,16 @@ pub mod serde_seq; #[cfg(test)] mod tests; -pub use self::core::raw_entry_v1::{self, RawEntryApiV1}; -pub use self::core::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; +pub use self::entry::{Entry, IndexedEntry}; +pub use crate::inner::entry::{OccupiedEntry, VacantEntry}; + pub use self::iter::{ Drain, ExtractIf, IntoIter, IntoKeys, IntoValues, Iter, IterMut, IterMut2, Keys, Splice, Values, ValuesMut, }; pub use self::mutable::MutableEntryKey; pub use self::mutable::MutableKeys; +pub use self::raw_entry_v1::RawEntryApiV1; pub use self::slice::Slice; #[cfg(feature = "rayon")] @@ -28,19 +32,19 @@ pub use crate::rayon::map as rayon; pub(crate) use self::iter::Buckets; -use ::core::cmp::Ordering; -use ::core::fmt; -use ::core::hash::{BuildHasher, Hash, Hasher}; -use ::core::mem; -use ::core::ops::{Index, IndexMut, RangeBounds}; use alloc::boxed::Box; use alloc::collections::VecDeque; use alloc::vec::Vec; +use core::cmp::Ordering; +use core::fmt; +use core::hash::{BuildHasher, Hash, Hasher}; +use core::mem; +use core::ops::{Index, IndexMut, RangeBounds}; #[cfg(feature = "std")] use std::hash::RandomState; -pub(crate) use self::core::{ExtractCore, RingMapCore}; +use crate::inner::Core; use crate::util::third; use crate::{Bucket, Equivalent, GetDisjointMutError, HashValue, TryReserveError}; @@ -89,12 +93,12 @@ use crate::{Bucket, Equivalent, GetDisjointMutError, HashValue, TryReserveError} /// ``` #[cfg(feature = "std")] pub struct RingMap { - pub(crate) core: RingMapCore, + pub(crate) core: Core, hash_builder: S, } #[cfg(not(feature = "std"))] pub struct RingMap { - pub(crate) core: RingMapCore, + pub(crate) core: Core, hash_builder: S, } @@ -129,7 +133,7 @@ where #[cfg(feature = "test_debug")] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Let the inner `RingMapCore` print all of its details + // Let the inner `Core` print all of its details f.debug_struct("RingMap").field("core", &self.core).finish() } } @@ -164,7 +168,7 @@ impl RingMap { Self::with_hasher(hash_builder) } else { RingMap { - core: RingMapCore::with_capacity(n), + core: Core::with_capacity(n), hash_builder, } } @@ -176,7 +180,7 @@ impl RingMap { /// can be called in `static` contexts. pub const fn with_hasher(hash_builder: S) -> Self { RingMap { - core: RingMapCore::new(), + core: Core::new(), hash_builder, } } diff --git a/src/map/entry.rs b/src/map/entry.rs new file mode 100644 index 0000000..8134d52 --- /dev/null +++ b/src/map/entry.rs @@ -0,0 +1,435 @@ +use super::{Bucket, Core}; +use crate::inner::entry::{OccupiedEntry, VacantEntry}; +use core::{fmt, mem}; + +/// Entry for an existing key-value pair in an [`RingMap`][crate::RingMap] +/// or a vacant location to insert one. +pub enum Entry<'a, K, V> { + /// Existing slot with equivalent key. + Occupied(OccupiedEntry<'a, K, V>), + /// Vacant slot (no equivalent key in the map). + Vacant(VacantEntry<'a, K, V>), +} + +impl<'a, K, V> Entry<'a, K, V> { + /// Return the index where the key-value pair exists or may be appended. + /// + /// Note that some methods may instead prepend new items at index 0. + pub fn index(&self) -> usize { + match self { + Entry::Occupied(entry) => entry.index(), + Entry::Vacant(entry) => entry.index(), + } + } + + #[deprecated = "use `push_back_entry` or `push_front_entry` instead"] + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + self.push_back_entry(value) + } + + /// Sets the value of the entry (after appending if vacant), and returns an `OccupiedEntry`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn push_back_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + match self { + Entry::Occupied(mut entry) => { + entry.insert(value); + entry + } + Entry::Vacant(entry) => entry.push_back_entry(value), + } + } + + /// Sets the value of the entry (after prepending if vacant), and returns an `OccupiedEntry`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn push_front_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + match self { + Entry::Occupied(mut entry) => { + entry.insert(value); + entry + } + Entry::Vacant(entry) => entry.push_front_entry(value), + } + } + + #[deprecated = "use `or_push_back` or `or_push_front` instead"] + pub fn or_insert(self, default: V) -> &'a mut V { + self.or_push_back(default) + } + + /// Appends the given default value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_back(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.push_back(default), + } + } + + /// Prepends the given default value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_front(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.push_front(default), + } + } + + #[deprecated = "use `or_push_back_default` or `or_push_front_default` instead"] + pub fn or_default(self) -> &'a mut V + where + V: Default, + { + self.or_push_back_default() + } + + /// Appends a default-constructed value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_back_default(self) -> &'a mut V + where + V: Default, + { + self.or_push_back_with(V::default) + } + + /// Prepends a default-constructed value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_front_default(self) -> &'a mut V + where + V: Default, + { + self.or_push_front_with(V::default) + } + + #[deprecated = "use `or_push_back_with` or `or_push_front_with` instead"] + pub fn or_insert_with(self, call: F) -> &'a mut V + where + F: FnOnce() -> V, + { + self.or_push_back_with(call) + } + + /// Appends the result of the `call` function in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_back_with(self, call: F) -> &'a mut V + where + F: FnOnce() -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.push_back(call()), + } + } + + /// Prepends the result of the `call` function in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_front_with(self, call: F) -> &'a mut V + where + F: FnOnce() -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.push_front(call()), + } + } + + #[deprecated = "use `or_push_back_with_key` or `or_push_front_with_key` instead"] + pub fn or_insert_with_key(self, call: F) -> &'a mut V + where + F: FnOnce(&K) -> V, + { + self.or_push_back_with_key(call) + } + + /// Appends the result of the `call` function with a reference to the entry's key if it is + /// vacant, and returns a mutable reference to the new value. Otherwise a mutable reference to + /// an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_back_with_key(self, call: F) -> &'a mut V + where + F: FnOnce(&K) -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let value = call(entry.key()); + entry.push_back(value) + } + } + } + + /// Prepends the result of the `call` function with a reference to the entry's key if it is + /// vacant, and returns a mutable reference to the new value. Otherwise a mutable reference to + /// an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_push_front_with_key(self, call: F) -> &'a mut V + where + F: FnOnce(&K) -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let value = call(entry.key()); + entry.push_front(value) + } + } + } + + /// Gets a reference to the entry's key, either within the map if occupied, + /// or else the new key that was used to find the entry. + pub fn key(&self) -> &K { + match *self { + Entry::Occupied(ref entry) => entry.key(), + Entry::Vacant(ref entry) => entry.key(), + } + } + + /// Modifies the entry if it is occupied. + pub fn and_modify(mut self, f: F) -> Self + where + F: FnOnce(&mut V), + { + if let Entry::Occupied(entry) = &mut self { + f(entry.get_mut()); + } + self + } +} + +impl fmt::Debug for Entry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut tuple = f.debug_tuple("Entry"); + match self { + Entry::Vacant(v) => tuple.field(v), + Entry::Occupied(o) => tuple.field(o), + }; + tuple.finish() + } +} + +impl fmt::Debug for OccupiedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +impl fmt::Debug for VacantEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() + } +} + +/// A view into an occupied entry in an [`RingMap`][crate::RingMap] obtained by index. +/// +/// This `struct` is created from the [`get_index_entry`][crate::RingMap::get_index_entry] method. +pub struct IndexedEntry<'a, K, V> { + map: &'a mut Core, + // We have a mutable reference to the map, which keeps the index + // valid and pointing to the correct entry. + index: usize, +} + +impl<'a, K, V> IndexedEntry<'a, K, V> { + pub(crate) fn new(map: &'a mut Core, index: usize) -> Option { + if index < map.len() { + Some(Self { map, index }) + } else { + None + } + } + + /// Return the index of the key-value pair + #[inline] + pub fn index(&self) -> usize { + self.index + } + + pub(crate) fn into_core(self) -> &'a mut Core { + self.map + } + + fn get_bucket(&self) -> &Bucket { + &self.map.as_entries()[self.index] + } + + fn get_bucket_mut(&mut self) -> &mut Bucket { + &mut self.map.as_entries_mut()[self.index] + } + + fn into_bucket(self) -> &'a mut Bucket { + &mut self.map.as_entries_mut()[self.index] + } + + /// Gets a reference to the entry's key in the map. + pub fn key(&self) -> &K { + &self.get_bucket().key + } + + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.get_bucket_mut().key + } + + /// Gets a reference to the entry's value in the map. + pub fn get(&self) -> &V { + &self.get_bucket().value + } + + /// Gets a mutable reference to the entry's value in the map. + /// + /// If you need a reference which may outlive the destruction of the + /// `IndexedEntry` value, see [`into_mut`][Self::into_mut]. + pub fn get_mut(&mut self) -> &mut V { + &mut self.get_bucket_mut().value + } + + /// Sets the value of the entry to `value`, and returns the entry's old value. + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Converts into a mutable reference to the entry's value in the map, + /// with a lifetime bound to the map itself. + pub fn into_mut(self) -> &'a mut V { + &mut self.into_bucket().value + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`VecDeque::remove`][super::VecDeque::remove], the pair is removed by shifting all of the + /// elements either before or after it, preserving their relative order. + /// **This perturbs the index of all of the following elements!** + /// + /// Computes in **O(n)** time (average). + pub fn remove_entry(self) -> (K, V) { + self.map.shift_remove_index(self.index).unwrap() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`VecDeque::remove`][super::VecDeque::remove], the pair is removed by shifting all of the + /// elements either before or after it, preserving their relative order. + /// **This perturbs the index of all of the following elements!** + /// + /// Computes in **O(n)** time (average). + pub fn remove(self) -> V { + self.remove_entry().1 + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`VecDeque::swap_remove_back`][super::VecDeque::swap_remove_back], the pair is removed + /// by swapping it with the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_back_entry(self) -> (K, V) { + self.map.swap_remove_back_index(self.index).unwrap() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`VecDeque::swap_remove_back`][super::VecDeque::swap_remove_back], the pair is removed + /// by swapping it with the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_back(self) -> V { + self.swap_remove_back_entry().1 + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`VecDeque::swap_remove_front`][super::VecDeque::swap_remove_front], the pair is removed + /// by swapping it with the front element of the map and popping it off. + /// **This perturbs the position of what used to be the front element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_front_entry(self) -> (K, V) { + self.map.swap_remove_front_index(self.index).unwrap() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`VecDeque::swap_remove_front`][super::VecDeque::swap_remove_front], the pair is removed + /// by swapping it with the front element of the map and popping it off. + /// **This perturbs the position of what used to be the front element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_front(self) -> V { + self.swap_remove_front_entry().1 + } + + /// Moves the position of the entry to a new index + /// by shifting all other entries in-between. + /// + /// This is equivalent to [`RingMap::move_index`][`crate::RingMap::move_index`] + /// coming `from` the current [`.index()`][Self::index]. + /// + /// * If `self.index() < to`, the other pairs will shift down while the targeted pair moves up. + /// * If `self.index() > to`, the other pairs will shift up while the targeted pair moves down. + /// + /// ***Panics*** if `to` is out of bounds. + /// + /// Computes in **O(n)** time (average). + #[track_caller] + pub fn move_index(self, to: usize) { + self.map.move_index(self.index, to); + } + + /// Swaps the position of entry with another. + /// + /// This is equivalent to [`RingMap::swap_indices`][`crate::RingMap::swap_indices`] + /// with the current [`.index()`][Self::index] as one of the two being swapped. + /// + /// ***Panics*** if the `other` index is out of bounds. + /// + /// Computes in **O(1)** time (average). + #[track_caller] + pub fn swap_indices(self, other: usize) { + self.map.swap_indices(self.index, other); + } +} + +impl fmt::Debug for IndexedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IndexedEntry") + .field("index", &self.index) + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +impl<'a, K, V> From> for IndexedEntry<'a, K, V> { + fn from(other: OccupiedEntry<'a, K, V>) -> Self { + Self { + index: other.index(), + map: other.into_core(), + } + } +} + +#[test] +fn assert_send_sync() { + fn assert_send_sync() {} + assert_send_sync::>(); + assert_send_sync::>(); +} diff --git a/src/map/iter.rs b/src/map/iter.rs index c2ab032..286d748 100644 --- a/src/map/iter.rs +++ b/src/map/iter.rs @@ -1,4 +1,5 @@ -use super::{Bucket, ExtractCore, RingMap, RingMapCore}; +use super::{Bucket, Core, RingMap}; +use crate::inner::extract::ExtractCore; use alloc::collections::vec_deque::{self, VecDeque}; use core::hash::{BuildHasher, Hash}; @@ -921,7 +922,7 @@ where S: BuildHasher, { map: &'a mut RingMap, - tail: RingMapCore, + tail: Core, drain: vec_deque::IntoIter>, replace_with: I, } @@ -1051,7 +1052,7 @@ pub struct ExtractIf<'a, K, V, F> { impl ExtractIf<'_, K, V, F> { #[track_caller] - pub(super) fn new(core: &mut RingMapCore, range: R, pred: F) -> ExtractIf<'_, K, V, F> + pub(super) fn new(core: &mut Core, range: R, pred: F) -> ExtractIf<'_, K, V, F> where R: RangeBounds, F: FnMut(&K, &mut V) -> bool, diff --git a/src/map/core/raw_entry_v1.rs b/src/map/raw_entry_v1.rs similarity index 97% rename from src/map/core/raw_entry_v1.rs rename to src/map/raw_entry_v1.rs index a4cfcce..1f6604a 100644 --- a/src/map/core/raw_entry_v1.rs +++ b/src/map/raw_entry_v1.rs @@ -9,7 +9,7 @@ //! `hash_raw_entry` feature (or some replacement), matching *inherent* methods will be added to //! `RingMap` without such an opt-in trait. -use super::{OccupiedEntry, OffsetIndex, RingMapCore}; +use super::{Core, OccupiedEntry}; use crate::{Equivalent, HashValue, RingMap}; use core::fmt; use core::hash::{BuildHasher, Hash}; @@ -224,16 +224,12 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { } /// Access the index of an entry by hash. - pub fn index_from_hash(self, hash: u64, mut is_match: F) -> Option + pub fn index_from_hash(self, hash: u64, is_match: F) -> Option where F: FnMut(&K) -> bool, { let hash = HashValue(hash as usize); - let entries = &self.map.core.entries; - let offset = self.map.core.offset; - let eq = move |&i: &OffsetIndex| is_match(&entries[i.get(offset)].key); - let oi = self.map.core.indices.find(hash.get(), eq)?; - Some(oi.get(offset)) + self.map.core.get_index_of_raw(hash, is_match) } } @@ -275,6 +271,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { where F: FnMut(&K) -> bool, { + let hash = HashValue(hash as usize); match OccupiedEntry::from_hash(&mut self.map.core, hash, is_match) { Ok(inner) => RawEntryMut::Occupied(RawOccupiedEntryMut { inner, @@ -557,7 +554,7 @@ impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { /// A view into a vacant raw entry in an [`RingMap`]. /// It is part of the [`RawEntryMut`] enum. pub struct RawVacantEntryMut<'a, K, V, S> { - map: &'a mut RingMapCore, + map: &'a mut Core, hash_builder: &'a S, } @@ -622,11 +619,16 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { value: V, ) -> (&'a mut K, &'a mut V) { let hash = HashValue(hash as usize); - self.map.shift_insert_unique(index, hash, key, value); - self.map.entries[index].muts() + self.map.shift_insert_unique(index, hash, key, value).muts() } } trait Sealed {} impl Sealed for RingMap {} + +#[test] +fn assert_send_sync() { + fn assert_send_sync() {} + assert_send_sync::>(); +} diff --git a/src/set/iter.rs b/src/set/iter.rs index 7c2fef4..9e40193 100644 --- a/src/set/iter.rs +++ b/src/set/iter.rs @@ -1,4 +1,5 @@ -use crate::map::{ExtractCore, RingMapCore}; +use crate::inner::extract::ExtractCore; +use crate::inner::Core; use super::{Bucket, RingSet}; use crate::map::Buckets; @@ -637,7 +638,7 @@ pub struct ExtractIf<'a, T, F> { impl ExtractIf<'_, T, F> { #[track_caller] - pub(super) fn new(core: &mut RingMapCore, range: R, pred: F) -> ExtractIf<'_, T, F> + pub(super) fn new(core: &mut Core, range: R, pred: F) -> ExtractIf<'_, T, F> where R: RangeBounds, F: FnMut(&T) -> bool,