diff --git a/src/map/core.rs b/src/inner.rs similarity index 96% rename from src/map/core.rs rename to src/inner.rs index 1dcf2b76..2923ef7d 100644 --- a/src/map/core.rs +++ b/src/inner.rs @@ -1,6 +1,6 @@ //! This is the core implementation that doesn't depend on the hasher at all. //! -//! The methods of `IndexMapCore` 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. @@ -10,8 +10,6 @@ mod entry; mod extract; -pub mod raw_entry_v1; - use alloc::vec::{self, Vec}; use core::mem; use core::ops::RangeBounds; @@ -23,12 +21,12 @@ use crate::{Bucket, Equivalent, HashValue, TryReserveError}; type Indices = hash_table::HashTable; type Entries = Vec>; -pub use entry::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; +pub use entry::{OccupiedEntry, VacantEntry}; pub(crate) use extract::ExtractCore; /// Core of the map that does not depend on S -#[derive(Debug)] -pub(crate) struct IndexMapCore { +#[cfg_attr(feature = "test_debug", derive(Debug))] +pub(crate) struct Core { /// indices mapping from the entry hash to its index. indices: Indices, /// entries is a dense vec maintaining entry order. @@ -76,7 +74,7 @@ fn insert_bulk_no_grow(indices: &mut Indices, entries: &[Bucket]) { } } -impl Clone for IndexMapCore +impl Clone for Core where K: Clone, V: Clone, @@ -98,13 +96,13 @@ where } } -impl IndexMapCore { +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 { - IndexMapCore { + Core { indices: Indices::new(), entries: Vec::new(), } @@ -112,7 +110,7 @@ impl IndexMapCore { #[inline] pub(crate) fn with_capacity(n: usize) -> Self { - IndexMapCore { + Core { indices: Indices::with_capacity(n), entries: Vec::with_capacity(n), } @@ -305,6 +303,15 @@ impl IndexMapCore { self.indices.find(hash.get(), eq).copied() } + /// 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 eq = move |&i: &usize| is_match(&self.entries[i].key); + self.indices.find(hash.get(), eq).copied() + } + /// Append a key-value pair to `entries`, /// *without* checking whether it already exists. fn push_entry(&mut self, hash: HashValue, key: K, value: V) { @@ -509,7 +516,13 @@ impl IndexMapCore { /// 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(crate) 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. @@ -527,6 +540,7 @@ impl IndexMapCore { 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 @@ -680,8 +694,5 @@ impl IndexMapCore { #[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 56% rename from src/map/core/entry.rs rename to src/inner/entry.rs index 87a7d955..20b73bd9 100644 --- a/src/map/core/entry.rs +++ b/src/inner/entry.rs @@ -1,19 +1,11 @@ -use super::{equivalent, get_hash, Bucket, IndexMapCore}; +use super::{equivalent, get_hash, Bucket, Core}; +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 [`IndexMap`][crate::IndexMap] -/// 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 IndexMapCore, hash: HashValue, key: K) -> Self + pub(crate) fn new(map: &'a mut Core, hash: HashValue, key: K) -> Self where K: Eq, { @@ -27,121 +19,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 will be inserted. - pub fn index(&self) -> usize { - match self { - Entry::Occupied(entry) => entry.index, - Entry::Vacant(entry) => entry.index(), - } - } - - /// Sets the value of the entry (after inserting if vacant), and returns an `OccupiedEntry`. - /// - /// Computes in **O(1)** time (amortized average). - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - match self { - Entry::Occupied(mut entry) => { - entry.insert(value); - entry - } - Entry::Vacant(entry) => entry.insert_entry(value), - } - } - - /// Inserts 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_insert(self, default: V) -> &'a mut V { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(default), - } - } - - /// Inserts 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_insert_with(self, call: F) -> &'a mut V - where - F: FnOnce() -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(call()), - } - } - - /// Inserts 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_insert_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.insert(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 - } - - /// Inserts 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_default(self) -> &'a mut V - where - V: Default, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(V::default()), - } - } -} - -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 [`IndexMap`][crate::IndexMap]. /// It is part of the [`Entry`] enum. pub struct OccupiedEntry<'a, K, V> { - map: &'a mut IndexMapCore, + 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, @@ -150,17 +33,17 @@ 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 IndexMapCore, - 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 eq = move |&i: &usize| is_match(&entries[i].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(), @@ -170,6 +53,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { } } + pub(crate) fn into_core(self) -> &'a mut Core { + self.map + } + pub(crate) fn get_bucket(&self) -> &Bucket { &self.map.entries[self.index] } @@ -194,12 +81,12 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// 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. @@ -207,13 +94,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. @@ -346,18 +233,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 bucket = map .indices @@ -370,7 +249,7 @@ impl<'a, K, V> From> for OccupiedEntry<'a, K, V> { /// A view into a vacant entry in an [`IndexMap`][crate::IndexMap]. /// It is part of the [`Entry`] enum. pub struct VacantEntry<'a, K, V> { - map: &'a mut IndexMapCore, + map: &'a mut Core, hash: HashValue, key: K, } @@ -484,8 +363,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 @@ -517,159 +396,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 [`IndexMap`][crate::IndexMap] obtained by index. -/// -/// This `struct` is created from the [`get_index_entry`][crate::IndexMap::get_index_entry] method. -pub struct IndexedEntry<'a, K, V> { - map: &'a mut IndexMapCore, - // 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 IndexMapCore, 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 [`Vec::swap_remove`][alloc::vec::Vec::swap_remove], 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_entry(self) -> (K, V) { - self.map.swap_remove_index(self.index).unwrap() - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// Like [`Vec::remove`][alloc::vec::Vec::remove], the pair is removed by shifting all of the - /// elements that follow it, preserving their relative order. - /// **This perturbs the index of all of those elements!** - /// - /// Computes in **O(n)** time (average). - pub fn shift_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 [`Vec::swap_remove`][alloc::vec::Vec::swap_remove], 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(self) -> V { - self.swap_remove_entry().1 - } - - /// Remove the key, value pair stored in the map for this entry, and return the value. - /// - /// Like [`Vec::remove`][alloc::vec::Vec::remove], the pair is removed by shifting all of the - /// elements that follow it, preserving their relative order. - /// **This perturbs the index of all of those elements!** - /// - /// Computes in **O(n)** time (average). - pub fn shift_remove(self) -> V { - self.shift_remove_entry().1 - } - - /// Moves the position of the entry to a new index - /// by shifting all other entries in-between. - /// - /// This is equivalent to [`IndexMap::move_index`][`crate::IndexMap::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 [`IndexMap::swap_indices`][`crate::IndexMap::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 { - let OccupiedEntry { map, index, .. } = other; - Self { map, 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 b2d29e1b..b85c22eb 100644 --- a/src/map/core/extract.rs +++ b/src/inner/extract.rs @@ -1,11 +1,11 @@ #![allow(unsafe_code)] -use super::{Bucket, IndexMapCore}; +use super::{Bucket, Core}; use crate::util::simplify_range; use core::ops::RangeBounds; -impl IndexMapCore { +impl Core { #[track_caller] pub(crate) fn extract(&mut self, range: R) -> ExtractCore<'_, K, V> where @@ -29,7 +29,7 @@ impl IndexMapCore { } pub(crate) struct ExtractCore<'a, K, V> { - map: &'a mut IndexMapCore, + map: &'a mut Core, new_len: usize, current: usize, end: usize, diff --git a/src/lib.rs b/src/lib.rs index 8b3a2551..6dc91339 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,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 7e9b8f94..66cd13f5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,11 +1,13 @@ //! [`IndexMap`] 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,31 +15,33 @@ 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::{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")] pub use crate::rayon::map as rayon; -use ::core::cmp::Ordering; -use ::core::fmt; -use ::core::hash::{BuildHasher, Hash}; -use ::core::mem; -use ::core::ops::{Index, IndexMut, RangeBounds}; use alloc::boxed::Box; use alloc::vec::Vec; +use core::cmp::Ordering; +use core::fmt; +use core::hash::{BuildHasher, Hash}; +use core::mem; +use core::ops::{Index, IndexMut, RangeBounds}; #[cfg(feature = "std")] use std::hash::RandomState; -pub(crate) use self::core::{ExtractCore, IndexMapCore}; +use crate::inner::Core; use crate::util::{third, try_simplify_range}; use crate::{Bucket, Equivalent, GetDisjointMutError, HashValue, TryReserveError}; @@ -86,12 +90,12 @@ use crate::{Bucket, Equivalent, GetDisjointMutError, HashValue, TryReserveError} /// ``` #[cfg(feature = "std")] pub struct IndexMap { - pub(crate) core: IndexMapCore, + pub(crate) core: Core, hash_builder: S, } #[cfg(not(feature = "std"))] pub struct IndexMap { - pub(crate) core: IndexMapCore, + pub(crate) core: Core, hash_builder: S, } @@ -126,7 +130,7 @@ where #[cfg(feature = "test_debug")] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Let the inner `IndexMapCore` print all of its details + // Let the inner `Core` print all of its details f.debug_struct("IndexMap") .field("core", &self.core) .finish() @@ -163,7 +167,7 @@ impl IndexMap { Self::with_hasher(hash_builder) } else { IndexMap { - core: IndexMapCore::with_capacity(n), + core: Core::with_capacity(n), hash_builder, } } @@ -175,7 +179,7 @@ impl IndexMap { /// can be called in `static` contexts. pub const fn with_hasher(hash_builder: S) -> Self { IndexMap { - core: IndexMapCore::new(), + core: Core::new(), hash_builder, } } diff --git a/src/map/entry.rs b/src/map/entry.rs new file mode 100644 index 00000000..15acc965 --- /dev/null +++ b/src/map/entry.rs @@ -0,0 +1,313 @@ +use crate::inner::{Core, OccupiedEntry, VacantEntry}; +use crate::Bucket; +use core::{fmt, mem}; + +/// Entry for an existing key-value pair in an [`IndexMap`][crate::IndexMap] +/// 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 will be inserted. + pub fn index(&self) -> usize { + match self { + Entry::Occupied(entry) => entry.index(), + Entry::Vacant(entry) => entry.index(), + } + } + + /// Sets the value of the entry (after inserting if vacant), and returns an `OccupiedEntry`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + match self { + Entry::Occupied(mut entry) => { + entry.insert(value); + entry + } + Entry::Vacant(entry) => entry.insert_entry(value), + } + } + + /// Inserts 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_insert(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default), + } + } + + /// Inserts 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_insert_with(self, call: F) -> &'a mut V + where + F: FnOnce() -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(call()), + } + } + + /// Inserts 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_insert_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.insert(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 + } + + /// Inserts 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_default(self) -> &'a mut V + where + V: Default, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(V::default()), + } + } +} + +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 [`IndexMap`][crate::IndexMap] obtained by index. +/// +/// This `struct` is created from the [`get_index_entry`][crate::IndexMap::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(super) 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 [`Vec::swap_remove`][alloc::vec::Vec::swap_remove], 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_entry(self) -> (K, V) { + self.map.swap_remove_index(self.index).unwrap() + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::remove`][alloc::vec::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_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 [`Vec::swap_remove`][alloc::vec::Vec::swap_remove], 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(self) -> V { + self.swap_remove_entry().1 + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::remove`][alloc::vec::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove(self) -> V { + self.shift_remove_entry().1 + } + + /// Moves the position of the entry to a new index + /// by shifting all other entries in-between. + /// + /// This is equivalent to [`IndexMap::move_index`][`crate::IndexMap::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 [`IndexMap::swap_indices`][`crate::IndexMap::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 8f9350a2..abc60010 100644 --- a/src/map/iter.rs +++ b/src/map/iter.rs @@ -1,4 +1,5 @@ -use super::{Bucket, ExtractCore, IndexMap, IndexMapCore, Slice}; +use super::{Bucket, IndexMap, Slice}; +use crate::inner::{Core, ExtractCore}; use alloc::vec::{self, Vec}; use core::fmt; @@ -655,7 +656,7 @@ where S: BuildHasher, { map: &'a mut IndexMap, - tail: IndexMapCore, + tail: Core, drain: vec::IntoIter>, replace_with: I, } @@ -785,7 +786,7 @@ pub struct ExtractIf<'a, K, V, F> { impl ExtractIf<'_, K, V, F> { #[track_caller] - pub(super) fn new(core: &mut IndexMapCore, 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 98% rename from src/map/core/raw_entry_v1.rs rename to src/map/raw_entry_v1.rs index ed8bf72c..4b5dbc95 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 //! `IndexMap` without such an opt-in trait. -use super::{IndexMapCore, OccupiedEntry}; +use super::{Core, OccupiedEntry}; use crate::{Equivalent, HashValue, IndexMap}; use core::fmt; use core::hash::{BuildHasher, Hash}; @@ -224,14 +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 eq = move |&i: &usize| is_match(&entries[i].key); - self.map.core.indices.find(hash.get(), eq).copied() + self.map.core.get_index_of_raw(hash, is_match) } } @@ -273,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 +556,7 @@ impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { /// A view into a vacant raw entry in an [`IndexMap`]. /// It is part of the [`RawEntryMut`] enum. pub struct RawVacantEntryMut<'a, K, V, S> { - map: &'a mut IndexMapCore, + map: &'a mut Core, hash_builder: &'a S, } @@ -622,11 +621,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 IndexMap {} + +#[test] +fn assert_send_sync() { + fn assert_send_sync() {} + assert_send_sync::>(); +} diff --git a/src/map/slice.rs b/src/map/slice.rs index f37411e2..9b4ece87 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -27,7 +27,7 @@ pub struct Slice { // and reference lifetimes are bound together in function signatures. #[allow(unsafe_code)] impl Slice { - pub(super) const fn from_slice(entries: &[Bucket]) -> &Self { + pub(crate) const fn from_slice(entries: &[Bucket]) -> &Self { unsafe { &*(entries as *const [Bucket] as *const Self) } } diff --git a/src/set/iter.rs b/src/set/iter.rs index 9ea81023..08c2baef 100644 --- a/src/set/iter.rs +++ b/src/set/iter.rs @@ -1,6 +1,5 @@ -use crate::map::{ExtractCore, IndexMapCore}; - use super::{Bucket, IndexSet, Slice}; +use crate::inner::{Core, ExtractCore}; use alloc::vec::{self, Vec}; use core::fmt; @@ -640,7 +639,7 @@ pub struct ExtractIf<'a, T, F> { impl ExtractIf<'_, T, F> { #[track_caller] - pub(super) fn new(core: &mut IndexMapCore, 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,