From 4fba5922b48d1ad0329274bd89d52fd709563144 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 27 Feb 2021 21:59:53 -0800 Subject: [PATCH 01/16] Add dynamically-sized slices of maps and sets --- src/map.rs | 2 + src/map/slice.rs | 319 +++++++++++++++++++++++++++++++++++++++++++++++ src/set.rs | 10 +- src/set/slice.rs | 193 ++++++++++++++++++++++++++++ 4 files changed, 521 insertions(+), 3 deletions(-) create mode 100644 src/map/slice.rs create mode 100644 src/set/slice.rs diff --git a/src/map.rs b/src/map.rs index 604538df..38cf326b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2,7 +2,9 @@ //! pairs is independent of the hash values of the keys. mod core; +mod slice; +pub use self::slice::Slice; pub use crate::mutable_keys::MutableKeys; #[cfg(feature = "rayon")] diff --git a/src/map/slice.rs b/src/map/slice.rs new file mode 100644 index 00000000..4ccfb287 --- /dev/null +++ b/src/map/slice.rs @@ -0,0 +1,319 @@ +use super::{Bucket, Entries, IndexMap, Iter, IterMut, Keys, Values, ValuesMut}; + +use core::cmp::Ordering; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::ops::{self, Index, IndexMut}; + +/// A dynamically-sized slice of key-value pairs in an `IndexMap`. +/// +/// This supports indexed operations much like a `[(K, V)]` slice, +/// but not any hashed operations on the map keys. +/// +/// Unlike `IndexMap`, `Slice` does consider the order for `PartialEq` +/// and `Eq`, and it also implements `PartialOrd`, `Ord`, and `Hash`. +#[repr(transparent)] +pub struct Slice { + entries: [Bucket], +} + +#[allow(unsafe_code)] +impl Slice { + fn from_slice(entries: &[Bucket]) -> &Self { + // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, + // and the lifetimes are bound together by this function's signature. + unsafe { &*(entries as *const [Bucket] as *const Self) } + } + + fn from_mut_slice(entries: &mut [Bucket]) -> &mut Self { + // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, + // and the lifetimes are bound together by this function's signature. + unsafe { &mut *(entries as *mut [Bucket] as *mut Self) } + } +} + +impl IndexMap { + /// Returns a slice of all the entries in the map. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } + + /// Returns a mutable slice of all the entries in the map. + pub fn as_mut_slice(&mut self) -> &mut Slice { + Slice::from_mut_slice(self.as_entries_mut()) + } +} + +impl<'a, K, V> Iter<'a, K, V> { + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl<'a, K, V> IterMut<'a, K, V> { + /// Returns a slice of the remaining entries in the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. + pub fn into_slice(self) -> &'a mut Slice { + Slice::from_mut_slice(self.iter.into_slice()) + } +} + +impl Slice { + /// Return the number of key-value pairs in the map slice. + #[inline] + pub fn len(&self) -> usize { + self.entries.len() + } + + /// Returns true if the map slice contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Get a key-value pair by index. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + self.entries.get(index).map(Bucket::refs) + } + + /// Get a key-value pair by index, with mutable access to the value. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { + // NB: we're not returning `&mut K` like `IndexMap::get_index_mut`, + // because that was a mistake that should have required `MutableKeys`. + self.entries.get_mut(index).map(Bucket::ref_mut) + } + + /// Get the first key-value pair. + pub fn first(&self) -> Option<(&K, &V)> { + self.entries.first().map(Bucket::refs) + } + + /// Get the first key-value pair, with mutable access to the value. + pub fn first_mut(&mut self) -> Option<(&K, &mut V)> { + self.entries.first_mut().map(Bucket::ref_mut) + } + + /// Get the last key-value pair. + pub fn last(&self) -> Option<(&K, &V)> { + self.entries.last().map(Bucket::refs) + } + + /// Get the last key-value pair, with mutable access to the value. + pub fn last_mut(&mut self) -> Option<(&K, &mut V)> { + self.entries.last_mut().map(Bucket::ref_mut) + } + + /// Divides one slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at(&self, index: usize) -> (&Self, &Self) { + let (first, second) = self.entries.split_at(index); + (Self::from_slice(first), Self::from_slice(second)) + } + + /// Divides one mutable slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at_mut(&mut self, index: usize) -> (&mut Self, &mut Self) { + let (first, second) = self.entries.split_at_mut(index); + (Self::from_mut_slice(first), Self::from_mut_slice(second)) + } + + /// Returns the first key-value pair and the rest of the slice, + /// or `None` if it is empty. + pub fn split_first(&self) -> Option<((&K, &V), &Self)> { + if let Some((first, rest)) = self.entries.split_first() { + Some((first.refs(), Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the first key-value pair and the rest of the slice, + /// with mutable access to the value, or `None` if it is empty. + pub fn split_first_mut(&mut self) -> Option<((&K, &mut V), &mut Self)> { + if let Some((first, rest)) = self.entries.split_first_mut() { + Some((first.ref_mut(), Self::from_mut_slice(rest))) + } else { + None + } + } + + /// Returns the last key-value pair and the rest of the slice, + /// or `None` if it is empty. + pub fn split_last(&self) -> Option<((&K, &V), &Self)> { + if let Some((last, rest)) = self.entries.split_last() { + Some((last.refs(), Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the last key-value pair and the rest of the slice, + /// with mutable access to the value, or `None` if it is empty. + pub fn split_last_mut(&mut self) -> Option<((&K, &mut V), &mut Self)> { + if let Some((last, rest)) = self.entries.split_last_mut() { + Some((last.ref_mut(), Self::from_mut_slice(rest))) + } else { + None + } + } + + /// Return an iterator over the key-value pairs of the map slice. + pub fn iter(&self) -> Iter<'_, K, V> { + Iter { + iter: self.entries.iter(), + } + } + + /// Return an iterator over the key-value pairs of the map slice. + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + IterMut { + iter: self.entries.iter_mut(), + } + } + + /// Return an iterator over the keys of the map slice. + pub fn keys(&self) -> Keys<'_, K, V> { + Keys { + iter: self.entries.iter(), + } + } + + /// Return an iterator over the values of the map slice. + pub fn values(&self) -> Values<'_, K, V> { + Values { + iter: self.entries.iter(), + } + } + + /// Return an iterator over mutable references to the the values of the map slice. + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + ValuesMut { + iter: self.entries.iter_mut(), + } + } +} + +impl<'a, K, V> IntoIterator for &'a Slice { + type IntoIter = Iter<'a, K, V>; + type Item = (&'a K, &'a V); + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, K, V> IntoIterator for &'a mut Slice { + type IntoIter = IterMut<'a, K, V>; + type Item = (&'a K, &'a mut V); + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl Default for &'_ Slice { + fn default() -> Self { + Slice::from_slice(&[]) + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +impl PartialEq for Slice { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } +} + +impl Eq for Slice {} + +impl PartialOrd for Slice { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +impl Ord for Slice { + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + self.len().hash(state); + for (key, value) in self { + key.hash(state); + value.hash(state); + } + } +} + +impl Index for Slice { + type Output = V; + + fn index(&self, index: usize) -> &V { + &self.entries[index].value + } +} + +impl IndexMut for Slice { + fn index_mut(&mut self, index: usize) -> &mut V { + &mut self.entries[index].value + } +} + +// We can't have `impl> Index` because that conflicts +// both upstream with `Index` and downstream with `Index<&Q>`. +// Instead, we repeat the implementations for all the core range types. +macro_rules! impl_index { + ($($range:ty),*) => {$( + impl Index<$range> for IndexMap { + type Output = Slice; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.as_entries()[range]) + } + } + + impl IndexMut<$range> for IndexMap { + fn index_mut(&mut self, range: $range) -> &mut Self::Output { + Slice::from_mut_slice(&mut self.as_entries_mut()[range]) + } + } + + impl Index<$range> for Slice { + type Output = Slice; + + fn index(&self, range: $range) -> &Self { + Self::from_slice(&self.entries[range]) + } + } + + impl IndexMut<$range> for Slice { + fn index_mut(&mut self, range: $range) -> &mut Self { + Self::from_mut_slice(&mut self.entries[range]) + } + } + )*} +} +impl_index!( + ops::Range, + ops::RangeFrom, + ops::RangeFull, + ops::RangeInclusive, + ops::RangeTo, + ops::RangeToInclusive +); diff --git a/src/set.rs b/src/set.rs index fa157b0f..4484693a 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1,5 +1,9 @@ //! A hash set implemented using `IndexMap` +mod slice; + +pub use self::slice::Slice; + #[cfg(feature = "rayon")] pub use crate::rayon::set as rayon; @@ -12,7 +16,7 @@ use core::fmt; use core::hash::{BuildHasher, Hash}; use core::iter::{Chain, FusedIterator}; use core::ops::{BitAnd, BitOr, BitXor, Index, RangeBounds, Sub}; -use core::slice; +use core::slice::Iter as SliceIter; use super::{Entries, Equivalent, IndexMap}; @@ -192,7 +196,7 @@ impl IndexSet { /// Return an iterator over the values of the set, in their order pub fn iter(&self) -> Iter<'_, T> { Iter { - iter: self.map.keys().iter, + iter: self.map.as_entries().iter(), } } @@ -791,7 +795,7 @@ impl fmt::Debug for IntoIter { /// [`IndexSet`]: struct.IndexSet.html /// [`iter`]: struct.IndexSet.html#method.iter pub struct Iter<'a, T> { - iter: slice::Iter<'a, Bucket>, + iter: SliceIter<'a, Bucket>, } impl<'a, T> Iterator for Iter<'a, T> { diff --git a/src/set/slice.rs b/src/set/slice.rs new file mode 100644 index 00000000..8962799f --- /dev/null +++ b/src/set/slice.rs @@ -0,0 +1,193 @@ +use super::{Bucket, Entries, IndexSet, Iter}; + +use core::cmp::Ordering; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::ops::{self, Index}; + +/// A dynamically-sized slice of values in an `IndexSet`. +/// +/// This supports indexed operations much like a `[T]` slice, +/// but not any hashed operations on the values. +/// +/// Unlike `IndexSet`, `Slice` does consider the order for `PartialEq` +/// and `Eq`, and it also implements `PartialOrd`, `Ord`, and `Hash`. +#[repr(transparent)] +pub struct Slice { + entries: [Bucket], +} + +#[allow(unsafe_code)] +impl Slice { + fn from_slice(entries: &[Bucket]) -> &Self { + // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, + // and the lifetimes are bound together by this function's signature. + unsafe { &*(entries as *const [Bucket] as *const Self) } + } +} + +impl IndexSet { + /// Returns a slice of all the values in the set. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } +} + +impl<'a, T> Iter<'a, T> { + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl Slice { + /// Return the number of elements in the set slice. + pub fn len(&self) -> usize { + self.entries.len() + } + + /// Returns true if the set slice contains no elements. + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Get a value by index. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_index(&self, index: usize) -> Option<&T> { + self.entries.get(index).map(Bucket::key_ref) + } + + /// Get the first value. + pub fn first(&self) -> Option<&T> { + self.entries.first().map(Bucket::key_ref) + } + + /// Get the last value. + pub fn last(&self) -> Option<&T> { + self.entries.last().map(Bucket::key_ref) + } + + /// Divides one slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at(&self, index: usize) -> (&Self, &Self) { + let (first, second) = self.entries.split_at(index); + (Self::from_slice(first), Self::from_slice(second)) + } + + /// Returns the first value and the rest of the slice, + /// or `None` if it is empty. + pub fn split_first(&self) -> Option<(&T, &Self)> { + if let Some((first, rest)) = self.entries.split_first() { + Some((&first.key, Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the last value and the rest of the slice, + /// or `None` if it is empty. + pub fn split_last(&self) -> Option<(&T, &Self)> { + if let Some((last, rest)) = self.entries.split_last() { + Some((&last.key, Self::from_slice(rest))) + } else { + None + } + } + + /// Return an iterator over the values of the set slice. + pub fn iter(&self) -> Iter<'_, T> { + Iter { + iter: self.entries.iter(), + } + } +} + +impl<'a, T> IntoIterator for &'a Slice { + type IntoIter = Iter<'a, T>; + type Item = &'a T; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Default for &'_ Slice { + fn default() -> Self { + Slice::from_slice(&[]) + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +impl PartialEq for Slice { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } +} + +impl Eq for Slice {} + +impl PartialOrd for Slice { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +impl Ord for Slice { + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + self.len().hash(state); + for value in self { + value.hash(state); + } + } +} + +impl Index for Slice { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.entries[index].key + } +} + +// We can't have `impl> Index` because that conflicts with `Index`. +// Instead, we repeat the implementations for all the core range types. +macro_rules! impl_index { + ($($range:ty),*) => {$( + impl Index<$range> for IndexSet { + type Output = Slice; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.as_entries()[range]) + } + } + + impl Index<$range> for Slice { + type Output = Self; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.entries[range]) + } + } + )*} +} +impl_index!( + ops::Range, + ops::RangeFrom, + ops::RangeFull, + ops::RangeInclusive, + ops::RangeTo, + ops::RangeToInclusive +); From e2c6a2f9869080224bdfc51f40554b0b98659424 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 27 Feb 2021 22:59:42 -0800 Subject: [PATCH 02/16] Create parallel iterators from slices --- src/map/slice.rs | 2 +- src/rayon/map.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ src/rayon/set.rs | 16 ++++++++++ src/set/slice.rs | 2 +- 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index 4ccfb287..741b06de 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -14,7 +14,7 @@ use core::ops::{self, Index, IndexMut}; /// and `Eq`, and it also implements `PartialOrd`, `Ord`, and `Hash`. #[repr(transparent)] pub struct Slice { - entries: [Bucket], + pub(crate) entries: [Bucket], } #[allow(unsafe_code)] diff --git a/src/rayon/map.rs b/src/rayon/map.rs index 8819f13e..021b63ba 100644 --- a/src/rayon/map.rs +++ b/src/rayon/map.rs @@ -15,6 +15,7 @@ use core::fmt; use core::hash::{BuildHasher, Hash}; use core::ops::RangeBounds; +use crate::map::Slice; use crate::Bucket; use crate::Entries; use crate::IndexMap; @@ -79,6 +80,22 @@ where } } +/// Requires crate feature `"rayon"`. +impl<'a, K, V> IntoParallelIterator for &'a Slice +where + K: Sync, + V: Sync, +{ + type Item = (&'a K, &'a V); + type Iter = ParIter<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: &self.entries, + } + } +} + /// A parallel iterator over the entries of a `IndexMap`. /// /// This `struct` is created by the [`par_iter`] method on [`IndexMap`] @@ -129,6 +146,22 @@ where } } +/// Requires crate feature `"rayon"`. +impl<'a, K, V> IntoParallelIterator for &'a mut Slice +where + K: Sync + Send, + V: Send, +{ + type Item = (&'a K, &'a mut V); + type Iter = ParIterMut<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIterMut { + entries: &mut self.entries, + } + } +} + /// A parallel mutable iterator over the entries of a `IndexMap`. /// /// This `struct` is created by the [`par_iter_mut`] method on [`IndexMap`] @@ -225,6 +258,37 @@ where } } +/// Parallel iterator methods and other parallel methods. +/// +/// The following methods **require crate feature `"rayon"`**. +/// +/// See also the `IntoParallelIterator` implementations. +impl Slice +where + K: Sync, + V: Sync, +{ + /// Return a parallel iterator over the keys of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_keys(&self) -> ParKeys<'_, K, V> { + ParKeys { + entries: &self.entries, + } + } + + /// Return a parallel iterator over the values of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_values(&self) -> ParValues<'_, K, V> { + ParValues { + entries: &self.entries, + } + } +} + impl IndexMap where K: Hash + Eq + Sync, @@ -331,6 +395,23 @@ where } } +/// Requires crate feature `"rayon"`. +impl Slice +where + K: Send, + V: Send, +{ + /// Return a parallel iterator over mutable references to the the values of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_values_mut(&mut self) -> ParValuesMut<'_, K, V> { + ParValuesMut { + entries: &mut self.entries, + } + } +} + impl IndexMap where K: Hash + Eq + Send, diff --git a/src/rayon/set.rs b/src/rayon/set.rs index 6749dc0d..74d5e395 100644 --- a/src/rayon/set.rs +++ b/src/rayon/set.rs @@ -15,6 +15,7 @@ use core::fmt; use core::hash::{BuildHasher, Hash}; use core::ops::RangeBounds; +use crate::set::Slice; use crate::Entries; use crate::IndexSet; @@ -78,6 +79,21 @@ where } } +/// Requires crate feature `"rayon"`. +impl<'a, T> IntoParallelIterator for &'a Slice +where + T: Sync, +{ + type Item = &'a T; + type Iter = ParIter<'a, T>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: &self.entries, + } + } +} + /// A parallel iterator over the items of a `IndexSet`. /// /// This `struct` is created by the [`par_iter`] method on [`IndexSet`] diff --git a/src/set/slice.rs b/src/set/slice.rs index 8962799f..2ab190d3 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -14,7 +14,7 @@ use core::ops::{self, Index}; /// and `Eq`, and it also implements `PartialOrd`, `Ord`, and `Hash`. #[repr(transparent)] pub struct Slice { - entries: [Bucket], + pub(crate) entries: [Bucket], } #[allow(unsafe_code)] From 45852b7a27f8eb545303c77ebbb5d8f8301eac2a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 26 May 2021 12:25:09 -0700 Subject: [PATCH 03/16] `impl Index<(Bound, Bound)>` like Rust 1.53 --- src/map/slice.rs | 41 ++++++++++++++++++++++++++++++++++++++++- src/set/slice.rs | 25 ++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index 741b06de..8677b279 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -1,9 +1,10 @@ use super::{Bucket, Entries, IndexMap, Iter, IterMut, Keys, Values, ValuesMut}; +use crate::util::simplify_range; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::ops::{self, Index, IndexMut}; +use core::ops::{self, Bound, Index, IndexMut}; /// A dynamically-sized slice of key-value pairs in an `IndexMap`. /// @@ -317,3 +318,41 @@ impl_index!( ops::RangeTo, ops::RangeToInclusive ); + +// NB: with MSRV 1.53, we can forward `Bound` pairs to direct slice indexing like other ranges + +impl Index<(Bound, Bound)> for IndexMap { + type Output = Slice; + + fn index(&self, range: (Bound, Bound)) -> &Self::Output { + let entries = self.as_entries(); + let range = simplify_range(range, entries.len()); + Slice::from_slice(&entries[range]) + } +} + +impl IndexMut<(Bound, Bound)> for IndexMap { + fn index_mut(&mut self, range: (Bound, Bound)) -> &mut Self::Output { + let entries = self.as_entries_mut(); + let range = simplify_range(range, entries.len()); + Slice::from_mut_slice(&mut entries[range]) + } +} + +impl Index<(Bound, Bound)> for Slice { + type Output = Slice; + + fn index(&self, range: (Bound, Bound)) -> &Self { + let entries = &self.entries; + let range = simplify_range(range, entries.len()); + Slice::from_slice(&entries[range]) + } +} + +impl IndexMut<(Bound, Bound)> for Slice { + fn index_mut(&mut self, range: (Bound, Bound)) -> &mut Self { + let entries = &mut self.entries; + let range = simplify_range(range, entries.len()); + Slice::from_mut_slice(&mut entries[range]) + } +} diff --git a/src/set/slice.rs b/src/set/slice.rs index 2ab190d3..8218e9e0 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -1,9 +1,10 @@ use super::{Bucket, Entries, IndexSet, Iter}; +use crate::util::simplify_range; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::ops::{self, Index}; +use core::ops::{self, Bound, Index}; /// A dynamically-sized slice of values in an `IndexSet`. /// @@ -191,3 +192,25 @@ impl_index!( ops::RangeTo, ops::RangeToInclusive ); + +// NB: with MSRV 1.53, we can forward `Bound` pairs to direct slice indexing like other ranges + +impl Index<(Bound, Bound)> for IndexSet { + type Output = Slice; + + fn index(&self, range: (Bound, Bound)) -> &Self::Output { + let entries = self.as_entries(); + let range = simplify_range(range, entries.len()); + Slice::from_slice(&entries[range]) + } +} + +impl Index<(Bound, Bound)> for Slice { + type Output = Self; + + fn index(&self, range: (Bound, Bound)) -> &Self::Output { + let entries = &self.entries; + let range = simplify_range(range, entries.len()); + Slice::from_slice(&entries[range]) + } +} From 7f9af49e7da31ec06becda0c7c339cbe4920a8e4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 26 May 2021 13:12:07 -0700 Subject: [PATCH 04/16] Add `get_range` and `get_range_mut` --- src/map/slice.rs | 38 ++++++++++++++++++++++++++++++++++++-- src/set/slice.rs | 21 +++++++++++++++++++-- src/util.rs | 22 ++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index 8677b279..7f091315 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -1,10 +1,10 @@ use super::{Bucket, Entries, IndexMap, Iter, IterMut, Keys, Values, ValuesMut}; -use crate::util::simplify_range; +use crate::util::{simplify_range, try_simplify_range}; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::ops::{self, Bound, Index, IndexMut}; +use core::ops::{self, Bound, Index, IndexMut, RangeBounds}; /// A dynamically-sized slice of key-value pairs in an `IndexMap`. /// @@ -43,6 +43,24 @@ impl IndexMap { pub fn as_mut_slice(&mut self) -> &mut Slice { Slice::from_mut_slice(self.as_entries_mut()) } + + /// Returns a slice of entries in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } + + /// Returns a mutable slice of entries in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Slice> { + let entries = self.as_entries_mut(); + let range = try_simplify_range(range, entries.len())?; + entries.get_mut(range).map(Slice::from_mut_slice) + } } impl<'a, K, V> Iter<'a, K, V> { @@ -90,6 +108,22 @@ impl Slice { self.entries.get_mut(index).map(Bucket::ref_mut) } + /// Returns a slice of key-value pairs in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range>(&self, range: R) -> Option<&Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get(range).map(Slice::from_slice) + } + + /// Returns a mutable slice of key-value pairs in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get_mut(range).map(Slice::from_mut_slice) + } + /// Get the first key-value pair. pub fn first(&self) -> Option<(&K, &V)> { self.entries.first().map(Bucket::refs) diff --git a/src/set/slice.rs b/src/set/slice.rs index 8218e9e0..99ed5ce1 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -1,10 +1,10 @@ use super::{Bucket, Entries, IndexSet, Iter}; -use crate::util::simplify_range; +use crate::util::{simplify_range, try_simplify_range}; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::ops::{self, Bound, Index}; +use core::ops::{self, Bound, Index, RangeBounds}; /// A dynamically-sized slice of values in an `IndexSet`. /// @@ -32,6 +32,15 @@ impl IndexSet { pub fn as_slice(&self) -> &Slice { Slice::from_slice(self.as_entries()) } + + /// Returns a slice of values in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } } impl<'a, T> Iter<'a, T> { @@ -59,6 +68,14 @@ impl Slice { self.entries.get(index).map(Bucket::key_ref) } + /// Returns a slice of values in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + pub fn get_range>(&self, range: R) -> Option<&Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get(range).map(Self::from_slice) + } + /// Get the first value. pub fn first(&self) -> Option<&T> { self.entries.first().map(Bucket::key_ref) diff --git a/src/util.rs b/src/util.rs index a24dfafd..377ff516 100644 --- a/src/util.rs +++ b/src/util.rs @@ -29,3 +29,25 @@ where } start..end } + +pub(crate) fn try_simplify_range(range: R, len: usize) -> Option> +where + R: RangeBounds, +{ + let start = match range.start_bound() { + Bound::Unbounded => 0, + Bound::Included(&i) if i <= len => i, + Bound::Excluded(&i) if i < len => i + 1, + _ => return None, + }; + let end = match range.end_bound() { + Bound::Unbounded => len, + Bound::Excluded(&i) if i <= len => i, + Bound::Included(&i) if i < len => i + 1, + _ => return None, + }; + if start > end { + return None; + } + Some(start..end) +} From 20c050683fbc94172c6f875d422367e5185778e4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 27 May 2021 09:19:05 -0700 Subject: [PATCH 05/16] impl Default for &mut map::Slice, like normal slices --- src/map/slice.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/map/slice.rs b/src/map/slice.rs index 7f091315..6d5fa867 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -260,6 +260,12 @@ impl Default for &'_ Slice { } } +impl Default for &'_ mut Slice { + fn default() -> Self { + Slice::from_mut_slice(&mut []) + } +} + impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self).finish() From 949c9a9159aedc7ac232e3ec1c928b4ad24a8f99 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 27 May 2021 11:21:59 -0700 Subject: [PATCH 06/16] Add tests of slice indexing --- src/map/slice.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++ src/set/slice.rs | 50 ++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/src/map/slice.rs b/src/map/slice.rs index 6d5fa867..80d3f89e 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -396,3 +396,119 @@ impl IndexMut<(Bound, Bound)> for Slice { Slice::from_mut_slice(&mut entries[range]) } } + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec::Vec; + + #[test] + fn slice_index() { + fn check( + vec_slice: &[(i32, i32)], + map_slice: &Slice, + sub_slice: &Slice, + ) { + assert_eq!(map_slice as *const _, sub_slice as *const _); + itertools::assert_equal( + vec_slice.iter().copied(), + map_slice.iter().map(|(&k, &v)| (k, v)), + ); + itertools::assert_equal(vec_slice.iter().map(|(k, _)| k), map_slice.keys()); + itertools::assert_equal(vec_slice.iter().map(|(_, v)| v), map_slice.values()); + } + + let vec: Vec<(i32, i32)> = (0..10).map(|i| (i, i * i)).collect(); + let map: IndexMap = vec.iter().cloned().collect(); + let slice = map.as_slice(); + + // RangeFull + check(&vec[..], &map[..], &slice[..]); + + for i in 0usize..10 { + // Index + assert_eq!(vec[i].1, map[i]); + assert_eq!(vec[i].1, slice[i]); + assert_eq!(map[&(i as i32)], map[i]); + assert_eq!(map[&(i as i32)], slice[i]); + + // RangeFrom + check(&vec[i..], &map[i..], &slice[i..]); + + // RangeTo + check(&vec[..i], &map[..i], &slice[..i]); + + // RangeToInclusive + check(&vec[..=i], &map[..=i], &slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check(&vec[i + 1..], &map[bounds], &slice[bounds]); + + for j in i..=10 { + // Range + check(&vec[i..j], &map[i..j], &slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check(&vec[i..=j], &map[i..=j], &slice[i..=j]); + } + } + } + + #[test] + fn slice_index_mut() { + fn check_mut( + vec_slice: &[(i32, i32)], + map_slice: &mut Slice, + sub_slice: &mut Slice, + ) { + assert_eq!(map_slice, sub_slice); + itertools::assert_equal( + vec_slice.iter().copied(), + map_slice.iter_mut().map(|(&k, &mut v)| (k, v)), + ); + itertools::assert_equal( + vec_slice.iter().map(|&(_, v)| v), + map_slice.values_mut().map(|&mut v| v), + ); + } + + let vec: Vec<(i32, i32)> = (0..10).map(|i| (i, i * i)).collect(); + let mut map: IndexMap = vec.iter().cloned().collect(); + let mut map2 = map.clone(); + let slice = map2.as_mut_slice(); + + // RangeFull + check_mut(&vec[..], &mut map[..], &mut slice[..]); + + for i in 0usize..10 { + // IndexMut + assert_eq!(&mut map[i], &mut slice[i]); + + // RangeFrom + check_mut(&vec[i..], &mut map[i..], &mut slice[i..]); + + // RangeTo + check_mut(&vec[..i], &mut map[..i], &mut slice[..i]); + + // RangeToInclusive + check_mut(&vec[..=i], &mut map[..=i], &mut slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check_mut(&vec[i + 1..], &mut map[bounds], &mut slice[bounds]); + + for j in i..=10 { + // Range + check_mut(&vec[i..j], &mut map[i..j], &mut slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check_mut(&vec[i..=j], &mut map[i..=j], &mut slice[i..=j]); + } + } + } +} diff --git a/src/set/slice.rs b/src/set/slice.rs index 99ed5ce1..97b9682c 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -231,3 +231,53 @@ impl Index<(Bound, Bound)> for Slice { Slice::from_slice(&entries[range]) } } + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec::Vec; + + #[test] + fn slice_index() { + fn check(vec_slice: &[i32], set_slice: &Slice, sub_slice: &Slice) { + assert_eq!(set_slice as *const _, sub_slice as *const _); + itertools::assert_equal(vec_slice, set_slice); + } + + let vec: Vec = (0..10).map(|i| i * i).collect(); + let set: IndexSet = vec.iter().cloned().collect(); + let slice = set.as_slice(); + + // RangeFull + check(&vec[..], &set[..], &slice[..]); + + for i in 0usize..10 { + // Index + assert_eq!(vec[i], set[i]); + assert_eq!(vec[i], slice[i]); + + // RangeFrom + check(&vec[i..], &set[i..], &slice[i..]); + + // RangeTo + check(&vec[..i], &set[..i], &slice[..i]); + + // RangeToInclusive + check(&vec[..=i], &set[..=i], &slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check(&vec[i + 1..], &set[bounds], &slice[bounds]); + + for j in i..=10 { + // Range + check(&vec[i..j], &set[i..j], &slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check(&vec[i..=j], &set[i..=j], &slice[i..=j]); + } + } + } +} From e9024a39c97a4cbfb939dcd0af38bfff59b33e3b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 27 May 2021 11:38:59 -0700 Subject: [PATCH 07/16] Move some slice methods for better doc order --- src/map.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++- src/map/slice.rs | 50 ++------------------------------------------ src/set.rs | 26 +++++++++++++++++++++++ src/set/slice.rs | 25 +--------------------- 4 files changed, 82 insertions(+), 73 deletions(-) diff --git a/src/map.rs b/src/map.rs index 38cf326b..86d344d0 100644 --- a/src/map.rs +++ b/src/map.rs @@ -23,7 +23,7 @@ use std::collections::hash_map::RandomState; use self::core::IndexMapCore; use crate::equivalent::Equivalent; -use crate::util::third; +use crate::util::{third, try_simplify_range}; use crate::{Bucket, Entries, HashValue}; pub use self::core::{Entry, OccupiedEntry, VacantEntry}; @@ -760,6 +760,20 @@ where } impl IndexMap { + /// Returns a slice of all the key-value pairs in the map. + /// + /// Computes in **O(1)** time. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } + + /// Returns a mutable slice of all the key-value pairs in the map. + /// + /// Computes in **O(1)** time. + pub fn as_mut_slice(&mut self) -> &mut Slice { + Slice::from_mut_slice(self.as_entries_mut()) + } + /// Get a key-value pair by index /// /// Valid indices are *0 <= index < self.len()* @@ -778,6 +792,28 @@ impl IndexMap { self.as_entries_mut().get_mut(index).map(Bucket::ref_mut) } + /// Returns a slice of key-value pairs in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } + + /// Returns a mutable slice of key-value pairs in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Slice> { + let entries = self.as_entries_mut(); + let range = try_simplify_range(range, entries.len())?; + entries.get_mut(range).map(Slice::from_mut_slice) + } + /// Get the first key-value pair /// /// Computes in **O(1)** time. @@ -1047,6 +1083,13 @@ pub struct Iter<'a, K, V> { iter: SliceIter<'a, Bucket>, } +impl<'a, K, V> Iter<'a, K, V> { + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); @@ -1091,6 +1134,15 @@ pub struct IterMut<'a, K, V> { iter: SliceIterMut<'a, Bucket>, } +impl<'a, K, V> IterMut<'a, K, V> { + /// Returns a slice of the remaining entries in the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. + pub fn into_slice(self) -> &'a mut Slice { + Slice::from_mut_slice(self.iter.into_slice()) + } +} + impl<'a, K, V> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); diff --git a/src/map/slice.rs b/src/map/slice.rs index 80d3f89e..8f642386 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -20,65 +20,19 @@ pub struct Slice { #[allow(unsafe_code)] impl Slice { - fn from_slice(entries: &[Bucket]) -> &Self { + pub(super) fn from_slice(entries: &[Bucket]) -> &Self { // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, // and the lifetimes are bound together by this function's signature. unsafe { &*(entries as *const [Bucket] as *const Self) } } - fn from_mut_slice(entries: &mut [Bucket]) -> &mut Self { + pub(super) fn from_mut_slice(entries: &mut [Bucket]) -> &mut Self { // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, // and the lifetimes are bound together by this function's signature. unsafe { &mut *(entries as *mut [Bucket] as *mut Self) } } } -impl IndexMap { - /// Returns a slice of all the entries in the map. - pub fn as_slice(&self) -> &Slice { - Slice::from_slice(self.as_entries()) - } - - /// Returns a mutable slice of all the entries in the map. - pub fn as_mut_slice(&mut self) -> &mut Slice { - Slice::from_mut_slice(self.as_entries_mut()) - } - - /// Returns a slice of entries in the given range of indices. - /// - /// Valid indices are *0 <= index < self.len()* - pub fn get_range>(&self, range: R) -> Option<&Slice> { - let entries = self.as_entries(); - let range = try_simplify_range(range, entries.len())?; - entries.get(range).map(Slice::from_slice) - } - - /// Returns a mutable slice of entries in the given range of indices. - /// - /// Valid indices are *0 <= index < self.len()* - pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Slice> { - let entries = self.as_entries_mut(); - let range = try_simplify_range(range, entries.len())?; - entries.get_mut(range).map(Slice::from_mut_slice) - } -} - -impl<'a, K, V> Iter<'a, K, V> { - /// Returns a slice of the remaining entries in the iterator. - pub fn as_slice(&self) -> &'a Slice { - Slice::from_slice(self.iter.as_slice()) - } -} - -impl<'a, K, V> IterMut<'a, K, V> { - /// Returns a slice of the remaining entries in the iterator. - /// - /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. - pub fn into_slice(self) -> &'a mut Slice { - Slice::from_mut_slice(self.iter.into_slice()) - } -} - impl Slice { /// Return the number of key-value pairs in the map slice. #[inline] diff --git a/src/set.rs b/src/set.rs index 4484693a..c1468974 100644 --- a/src/set.rs +++ b/src/set.rs @@ -10,6 +10,7 @@ pub use crate::rayon::set as rayon; #[cfg(feature = "std")] use std::collections::hash_map::RandomState; +use crate::util::try_simplify_range; use crate::vec::{self, Vec}; use core::cmp::Ordering; use core::fmt; @@ -654,6 +655,13 @@ where } impl IndexSet { + /// Returns a slice of all the values in the set. + /// + /// Computes in **O(1)** time. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } + /// Get a value by index /// /// Valid indices are *0 <= index < self.len()* @@ -663,6 +671,17 @@ impl IndexSet { self.as_entries().get(index).map(Bucket::key_ref) } + /// Returns a slice of values in the given range of indices. + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } + /// Get the first value /// /// Computes in **O(1)** time. @@ -798,6 +817,13 @@ pub struct Iter<'a, T> { iter: SliceIter<'a, Bucket>, } +impl<'a, T> Iter<'a, T> { + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; diff --git a/src/set/slice.rs b/src/set/slice.rs index 97b9682c..ba0d7ec4 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -20,36 +20,13 @@ pub struct Slice { #[allow(unsafe_code)] impl Slice { - fn from_slice(entries: &[Bucket]) -> &Self { + pub(super) fn from_slice(entries: &[Bucket]) -> &Self { // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, // and the lifetimes are bound together by this function's signature. unsafe { &*(entries as *const [Bucket] as *const Self) } } } -impl IndexSet { - /// Returns a slice of all the values in the set. - pub fn as_slice(&self) -> &Slice { - Slice::from_slice(self.as_entries()) - } - - /// Returns a slice of values in the given range of indices. - /// - /// Valid indices are *0 <= index < self.len()* - pub fn get_range>(&self, range: R) -> Option<&Slice> { - let entries = self.as_entries(); - let range = try_simplify_range(range, entries.len())?; - entries.get(range).map(Slice::from_slice) - } -} - -impl<'a, T> Iter<'a, T> { - /// Returns a slice of the remaining entries in the iterator. - pub fn as_slice(&self) -> &'a Slice { - Slice::from_slice(self.iter.as_slice()) - } -} - impl Slice { /// Return the number of elements in the set slice. pub fn len(&self) -> usize { From 7304afcbd87a2eeecf6606e579a04745dfa65c79 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 27 May 2021 12:19:54 -0700 Subject: [PATCH 08/16] impl Serialize for {map,set}::Slice --- src/serde_seq.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/serde_seq.rs b/src/serde_seq.rs index 7c89a6e5..b8bd8203 100644 --- a/src/serde_seq.rs +++ b/src/serde_seq.rs @@ -28,6 +28,42 @@ use core::hash::{BuildHasher, Hash}; use core::marker::PhantomData; use crate::IndexMap; +use crate::map::Slice as MapSlice; +use crate::set::Slice as SetSlice; + +/// Serializes a `map::Slice` as an ordered sequence. +/// +/// This behaves like [`crate::serde_seq`] for `IndexMap`, serializing a sequence +/// of `(key, value)` pairs, rather than as a map that might not preserver order. +/// +/// Requires crate feature `"serde"` or `"serde-1"` +impl Serialize for MapSlice +where + K: Serialize, + V: Serialize, +{ + fn serialize(&self, serializer: T) -> Result + where + T: Serializer, + { + serializer.collect_seq(self) + } +} + +/// Serializes a `set::Slice` as an ordered sequence. +/// +/// Requires crate feature `"serde"` or `"serde-1"` +impl Serialize for SetSlice +where + T: Serialize, +{ + fn serialize(&self, serializer: Se) -> Result + where + Se: Serializer, + { + serializer.collect_seq(self) + } +} /// Serializes an `IndexMap` as an ordered sequence. /// From 0241a1863c2095aea5aab43434ca32e66636f167 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 15:29:32 -0700 Subject: [PATCH 09/16] Remove outdated comment on get_index_mut --- src/map/slice.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index 8f642386..0be11332 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -57,8 +57,6 @@ impl Slice { /// /// Valid indices are *0 <= index < self.len()* pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { - // NB: we're not returning `&mut K` like `IndexMap::get_index_mut`, - // because that was a mistake that should have required `MutableKeys`. self.entries.get_mut(index).map(Bucket::ref_mut) } From 7242c540ed2db10f050ed382f6de6b0adbf49a75 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 15:30:04 -0700 Subject: [PATCH 10/16] Use built-in indexing for Bound pairs --- src/map/slice.rs | 43 +++---------------------------------------- src/set/slice.rs | 27 +++------------------------ 2 files changed, 6 insertions(+), 64 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index 0be11332..077aebbe 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -1,5 +1,5 @@ use super::{Bucket, Entries, IndexMap, Iter, IterMut, Keys, Values, ValuesMut}; -use crate::util::{simplify_range, try_simplify_range}; +use crate::util::try_simplify_range; use core::cmp::Ordering; use core::fmt; @@ -308,47 +308,10 @@ impl_index!( ops::RangeFull, ops::RangeInclusive, ops::RangeTo, - ops::RangeToInclusive + ops::RangeToInclusive, + (Bound, Bound) ); -// NB: with MSRV 1.53, we can forward `Bound` pairs to direct slice indexing like other ranges - -impl Index<(Bound, Bound)> for IndexMap { - type Output = Slice; - - fn index(&self, range: (Bound, Bound)) -> &Self::Output { - let entries = self.as_entries(); - let range = simplify_range(range, entries.len()); - Slice::from_slice(&entries[range]) - } -} - -impl IndexMut<(Bound, Bound)> for IndexMap { - fn index_mut(&mut self, range: (Bound, Bound)) -> &mut Self::Output { - let entries = self.as_entries_mut(); - let range = simplify_range(range, entries.len()); - Slice::from_mut_slice(&mut entries[range]) - } -} - -impl Index<(Bound, Bound)> for Slice { - type Output = Slice; - - fn index(&self, range: (Bound, Bound)) -> &Self { - let entries = &self.entries; - let range = simplify_range(range, entries.len()); - Slice::from_slice(&entries[range]) - } -} - -impl IndexMut<(Bound, Bound)> for Slice { - fn index_mut(&mut self, range: (Bound, Bound)) -> &mut Self { - let entries = &mut self.entries; - let range = simplify_range(range, entries.len()); - Slice::from_mut_slice(&mut entries[range]) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/set/slice.rs b/src/set/slice.rs index ba0d7ec4..1e5a5bb2 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -1,5 +1,5 @@ use super::{Bucket, Entries, IndexSet, Iter}; -use crate::util::{simplify_range, try_simplify_range}; +use crate::util::try_simplify_range; use core::cmp::Ordering; use core::fmt; @@ -184,31 +184,10 @@ impl_index!( ops::RangeFull, ops::RangeInclusive, ops::RangeTo, - ops::RangeToInclusive + ops::RangeToInclusive, + (Bound, Bound) ); -// NB: with MSRV 1.53, we can forward `Bound` pairs to direct slice indexing like other ranges - -impl Index<(Bound, Bound)> for IndexSet { - type Output = Slice; - - fn index(&self, range: (Bound, Bound)) -> &Self::Output { - let entries = self.as_entries(); - let range = simplify_range(range, entries.len()); - Slice::from_slice(&entries[range]) - } -} - -impl Index<(Bound, Bound)> for Slice { - type Output = Self; - - fn index(&self, range: (Bound, Bound)) -> &Self::Output { - let entries = &self.entries; - let range = simplify_range(range, entries.len()); - Slice::from_slice(&entries[range]) - } -} - #[cfg(test)] mod tests { use super::*; From 9f2b14d678eff8e8d317a758ede7df7705770749 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 16:50:44 -0700 Subject: [PATCH 11/16] Hide unnecessary iterator visibility --- src/map.rs | 4 ++-- src/set.rs | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/map.rs b/src/map.rs index 86d344d0..7c72e9bf 100644 --- a/src/map.rs +++ b/src/map.rs @@ -884,7 +884,7 @@ impl IndexMap { /// [`keys`]: struct.IndexMap.html#method.keys /// [`IndexMap`]: struct.IndexMap.html pub struct Keys<'a, K, V> { - pub(crate) iter: SliceIter<'a, Bucket>, + iter: SliceIter<'a, Bucket>, } impl<'a, K, V> Iterator for Keys<'a, K, V> { @@ -1176,7 +1176,7 @@ impl fmt::Debug for IterMut<'_, K, V> { /// [`into_iter`]: struct.IndexMap.html#method.into_iter /// [`IndexMap`]: struct.IndexMap.html pub struct IntoIter { - pub(crate) iter: vec::IntoIter>, + iter: vec::IntoIter>, } impl Iterator for IntoIter { diff --git a/src/set.rs b/src/set.rs index c1468974..a115c262 100644 --- a/src/set.rs +++ b/src/set.rs @@ -607,8 +607,10 @@ where where F: FnMut(&T, &T) -> Ordering, { + let mut entries = self.into_entries(); + entries.sort_by(move |a, b| cmp(&a.key, &b.key)); IntoIter { - iter: self.map.sorted_by(move |a, _, b, _| cmp(a, b)).iter, + iter: entries.into_iter(), } } @@ -638,11 +640,10 @@ where where F: FnMut(&T, &T) -> Ordering, { + let mut entries = self.into_entries(); + entries.sort_unstable_by(move |a, b| cmp(&a.key, &b.key)); IntoIter { - iter: self - .map - .sorted_unstable_by(move |a, _, b, _| cmp(a, b)) - .iter, + iter: entries.into_iter(), } } @@ -907,7 +908,7 @@ impl IntoIterator for IndexSet { fn into_iter(self) -> Self::IntoIter { IntoIter { - iter: self.map.into_iter().iter, + iter: self.into_entries().into_iter(), } } } From 8c49292fc7f840b341fda9ca7650c8d2944d8b98 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 17:08:40 -0700 Subject: [PATCH 12/16] Add boxed slices, including owned iterators --- src/map.rs | 8 ++++++++ src/map/slice.rs | 50 +++++++++++++++++++++++++++++++++++++++++++----- src/rayon/map.rs | 17 ++++++++++++++++ src/rayon/set.rs | 16 ++++++++++++++++ src/set.rs | 8 ++++++++ src/set/slice.rs | 31 +++++++++++++++++++++++++++--- 6 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/map.rs b/src/map.rs index 7c72e9bf..93622bc6 100644 --- a/src/map.rs +++ b/src/map.rs @@ -17,6 +17,7 @@ use ::core::hash::{BuildHasher, Hash, Hasher}; use ::core::iter::FusedIterator; use ::core::ops::{Index, IndexMut, RangeBounds}; use ::core::slice::{Iter as SliceIter, IterMut as SliceIterMut}; +use alloc::boxed::Box; #[cfg(feature = "std")] use std::collections::hash_map::RandomState; @@ -774,6 +775,13 @@ impl IndexMap { Slice::from_mut_slice(self.as_entries_mut()) } + /// Converts into a boxed slice of all the key-value pairs in the map. + /// + /// Note that this will drop the inner hash table and any excess capacity. + pub fn into_boxed_slice(self) -> Box> { + Slice::from_boxed(self.into_entries().into_boxed_slice()) + } + /// Get a key-value pair by index /// /// Valid indices are *0 <= index < self.len()* diff --git a/src/map/slice.rs b/src/map/slice.rs index 077aebbe..9fe1be6b 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -1,6 +1,11 @@ -use super::{Bucket, Entries, IndexMap, Iter, IterMut, Keys, Values, ValuesMut}; +use super::{ + Bucket, Entries, IndexMap, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, + ValuesMut, +}; use crate::util::try_simplify_range; +use alloc::boxed::Box; +use alloc::vec::Vec; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; @@ -18,22 +23,32 @@ pub struct Slice { pub(crate) entries: [Bucket], } +// SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, +// and reference lifetimes are bound together in function signatures. #[allow(unsafe_code)] impl Slice { pub(super) fn from_slice(entries: &[Bucket]) -> &Self { - // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, - // and the lifetimes are bound together by this function's signature. unsafe { &*(entries as *const [Bucket] as *const Self) } } pub(super) fn from_mut_slice(entries: &mut [Bucket]) -> &mut Self { - // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, - // and the lifetimes are bound together by this function's signature. unsafe { &mut *(entries as *mut [Bucket] as *mut Self) } } + + pub(super) fn from_boxed(entries: Box<[Bucket]>) -> Box { + unsafe { Box::from_raw(Box::into_raw(entries) as *mut Self) } + } + + fn into_boxed(self: Box) -> Box<[Bucket]> { + unsafe { Box::from_raw(Box::into_raw(self) as *mut [Bucket]) } + } } impl Slice { + pub(crate) fn into_entries(self: Box) -> Vec> { + self.into_boxed().into_vec() + } + /// Return the number of key-value pairs in the map slice. #[inline] pub fn len(&self) -> usize { @@ -173,6 +188,13 @@ impl Slice { } } + /// Return an owning iterator over the keys of the map slice. + pub fn into_keys(self: Box) -> IntoKeys { + IntoKeys { + iter: self.into_entries().into_iter(), + } + } + /// Return an iterator over the values of the map slice. pub fn values(&self) -> Values<'_, K, V> { Values { @@ -186,6 +208,13 @@ impl Slice { iter: self.entries.iter_mut(), } } + + /// Return an owning iterator over the values of the map slice. + pub fn into_values(self: Box) -> IntoValues { + IntoValues { + iter: self.into_entries().into_iter(), + } + } } impl<'a, K, V> IntoIterator for &'a Slice { @@ -206,6 +235,17 @@ impl<'a, K, V> IntoIterator for &'a mut Slice { } } +impl IntoIterator for Box> { + type IntoIter = IntoIter; + type Item = (K, V); + + fn into_iter(self) -> Self::IntoIter { + IntoIter { + iter: self.into_entries().into_iter(), + } + } +} + impl Default for &'_ Slice { fn default() -> Self { Slice::from_slice(&[]) diff --git a/src/rayon/map.rs b/src/rayon/map.rs index 021b63ba..7559d549 100644 --- a/src/rayon/map.rs +++ b/src/rayon/map.rs @@ -10,6 +10,7 @@ use rayon::iter::plumbing::{Consumer, ProducerCallback, UnindexedConsumer}; use rayon::prelude::*; use crate::vec::Vec; +use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; @@ -36,6 +37,22 @@ where } } +/// Requires crate feature `"rayon"`. +impl IntoParallelIterator for Box> +where + K: Send, + V: Send, +{ + type Item = (K, V); + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } +} + /// A parallel owning iterator over the entries of a `IndexMap`. /// /// This `struct` is created by the [`into_par_iter`] method on [`IndexMap`] diff --git a/src/rayon/set.rs b/src/rayon/set.rs index 74d5e395..0dc553fc 100644 --- a/src/rayon/set.rs +++ b/src/rayon/set.rs @@ -10,6 +10,7 @@ use rayon::iter::plumbing::{Consumer, ProducerCallback, UnindexedConsumer}; use rayon::prelude::*; use crate::vec::Vec; +use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; @@ -36,6 +37,21 @@ where } } +/// Requires crate feature `"rayon"`. +impl IntoParallelIterator for Box> +where + T: Send, +{ + type Item = T; + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } +} + /// A parallel owning iterator over the items of a `IndexSet`. /// /// This `struct` is created by the [`into_par_iter`] method on [`IndexSet`] diff --git a/src/set.rs b/src/set.rs index a115c262..bdcbaffb 100644 --- a/src/set.rs +++ b/src/set.rs @@ -12,6 +12,7 @@ use std::collections::hash_map::RandomState; use crate::util::try_simplify_range; use crate::vec::{self, Vec}; +use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; @@ -663,6 +664,13 @@ impl IndexSet { Slice::from_slice(self.as_entries()) } + /// Converts into a boxed slice of all the values in the set. + /// + /// Note that this will drop the inner hash table and any excess capacity. + pub fn into_boxed_slice(self) -> Box> { + Slice::from_boxed(self.into_entries().into_boxed_slice()) + } + /// Get a value by index /// /// Valid indices are *0 <= index < self.len()* diff --git a/src/set/slice.rs b/src/set/slice.rs index 1e5a5bb2..0039b4a7 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -1,6 +1,8 @@ -use super::{Bucket, Entries, IndexSet, Iter}; +use super::{Bucket, Entries, IndexSet, IntoIter, Iter}; use crate::util::try_simplify_range; +use alloc::boxed::Box; +use alloc::vec::Vec; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; @@ -18,16 +20,28 @@ pub struct Slice { pub(crate) entries: [Bucket], } +// SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, +// and reference lifetimes are bound together in function signatures. #[allow(unsafe_code)] impl Slice { pub(super) fn from_slice(entries: &[Bucket]) -> &Self { - // SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, - // and the lifetimes are bound together by this function's signature. unsafe { &*(entries as *const [Bucket] as *const Self) } } + + pub(super) fn from_boxed(entries: Box<[Bucket]>) -> Box { + unsafe { Box::from_raw(Box::into_raw(entries) as *mut Self) } + } + + fn into_boxed(self: Box) -> Box<[Bucket]> { + unsafe { Box::from_raw(Box::into_raw(self) as *mut [Bucket]) } + } } impl Slice { + pub(crate) fn into_entries(self: Box) -> Vec> { + self.into_boxed().into_vec() + } + /// Return the number of elements in the set slice. pub fn len(&self) -> usize { self.entries.len() @@ -108,6 +122,17 @@ impl<'a, T> IntoIterator for &'a Slice { } } +impl IntoIterator for Box> { + type IntoIter = IntoIter; + type Item = T; + + fn into_iter(self) -> Self::IntoIter { + IntoIter { + iter: self.into_entries().into_iter(), + } + } +} + impl Default for &'_ Slice { fn default() -> Self { Slice::from_slice(&[]) From 2d3b165e1eff0a2bd3957e9c4864285c95d2182c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 17:56:44 -0700 Subject: [PATCH 13/16] impl {Default,Clone,From<&Slice>} for Box --- src/map/slice.rs | 18 ++++++++++++++++++ src/set/slice.rs | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/map/slice.rs b/src/map/slice.rs index 9fe1be6b..ae9030ec 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -258,6 +258,24 @@ impl Default for &'_ mut Slice { } } +impl Default for Box> { + fn default() -> Self { + Slice::from_boxed(Box::default()) + } +} + +impl Clone for Box> { + fn clone(&self) -> Self { + Slice::from_boxed(self.entries.to_vec().into_boxed_slice()) + } +} + +impl From<&Slice> for Box> { + fn from(slice: &Slice) -> Self { + Slice::from_boxed(slice.entries.to_vec().into_boxed_slice()) + } +} + impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self).finish() diff --git a/src/set/slice.rs b/src/set/slice.rs index 0039b4a7..203ce126 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -139,6 +139,24 @@ impl Default for &'_ Slice { } } +impl Default for Box> { + fn default() -> Self { + Slice::from_boxed(Box::default()) + } +} + +impl Clone for Box> { + fn clone(&self) -> Self { + Slice::from_boxed(self.entries.to_vec().into_boxed_slice()) + } +} + +impl From<&Slice> for Box> { + fn from(slice: &Slice) -> Self { + Slice::from_boxed(slice.entries.to_vec().into_boxed_slice()) + } +} + impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self).finish() From 49836c639475fd15ddb2916cbc63b2d6028f2f8e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 4 Apr 2022 11:59:11 -0700 Subject: [PATCH 14/16] use more a direct Box from &Slice --- src/map/slice.rs | 2 +- src/set/slice.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/slice.rs b/src/map/slice.rs index ae9030ec..07c6705a 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -272,7 +272,7 @@ impl Clone for Box> { impl From<&Slice> for Box> { fn from(slice: &Slice) -> Self { - Slice::from_boxed(slice.entries.to_vec().into_boxed_slice()) + Slice::from_boxed(Box::from(&slice.entries)) } } diff --git a/src/set/slice.rs b/src/set/slice.rs index 203ce126..dd317ebd 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -153,7 +153,7 @@ impl Clone for Box> { impl From<&Slice> for Box> { fn from(slice: &Slice) -> Self { - Slice::from_boxed(slice.entries.to_vec().into_boxed_slice()) + Slice::from_boxed(Box::from(&slice.entries)) } } From eac27f11dd1186c7a7e0a7dcd3a5896c3a9c6b22 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 May 2022 18:02:14 -0700 Subject: [PATCH 15/16] Add a release note for Slice --- RELEASES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index f27a0bc8..c1efa646 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -19,6 +19,11 @@ - The new `IndexMap::shrink_to` and `IndexSet::shrink_to` methods shrink the capacity with a lower bound. + - The new `map::Slice` and `set::Slice` offer a linear view of maps + and sets, behaving a lot like normal `[(K, V)]` and `[T]` slices. Notably, + comparison traits like `Eq` only consider items in order, rather than hash + lookups, and slices even implement `Hash`. + - 1.8.1 - The new `IndexSet::replace_full` will return the index of the item along From 8a571c6d68cb38c283d563ff6972613e0eea4111 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 May 2022 18:09:53 -0700 Subject: [PATCH 16/16] Bump MSRV to 1.56.1, matching hashbrown as of 0.12.1 --- .github/workflows/ci.yml | 4 ++-- Cargo.toml | 2 +- README.md | 2 +- RELEASES.md | 2 +- src/lib.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fddcca0a..806da7ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: include: - - rust: 1.56.0 # MSRV + - rust: 1.56.1 # MSRV features: - rust: stable features: serde @@ -57,7 +57,7 @@ jobs: strategy: matrix: include: - - rust: 1.56.0 + - rust: 1.56.1 target: thumbv6m-none-eabi - rust: stable target: thumbv6m-none-eabi diff --git a/Cargo.toml b/Cargo.toml index 85867a1b..75ebd6d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0 OR MIT" description = "A hash table with consistent order and fast iteration." keywords = ["hashmap", "no_std"] categories = ["data-structures", "no-std"] -rust-version = "1.56" +rust-version = "1.56.1" [lib] bench = false diff --git a/README.md b/README.md index 5dc3f181..de7ab337 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![build status](https://github.com/bluss/indexmap/workflows/Continuous%20integration/badge.svg?branch=master)](https://github.com/bluss/indexmap/actions) [![crates.io](https://img.shields.io/crates/v/indexmap.svg)](https://crates.io/crates/indexmap) [![docs](https://docs.rs/indexmap/badge.svg)](https://docs.rs/indexmap) -[![rustc](https://img.shields.io/badge/rust-1.56%2B-orange.svg)](https://img.shields.io/badge/rust-1.56%2B-orange.svg) +[![rustc](https://img.shields.io/badge/rust-1.56.1%2B-orange.svg)](https://img.shields.io/badge/rust-1.56.1%2B-orange.svg) A pure-Rust hash table which preserves (in a limited sense) insertion order. diff --git a/RELEASES.md b/RELEASES.md index c1efa646..a0d9a77d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ - 2.0.0 (pending) - - **MSRV**: Rust 1.56 or later is now required. + - **MSRV**: Rust 1.56.1 or later is now required. - The `hashbrown` dependency has been updated to version 0.12. diff --git a/src/lib.rs b/src/lib.rs index 3d796ea7..3b2c571c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ //! //! ### Rust Version //! -//! This version of indexmap requires Rust 1.56 or later. +//! This version of indexmap requires Rust 1.56.1 or later. //! //! The indexmap 2.x release series will use a carefully considered version //! upgrade policy, where in a later 2.x version, we will raise the minimum