From f58f7abfede3b799e04787754a92429f03a1681d Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 23 Apr 2018 19:40:36 +0200 Subject: [PATCH] port IndexMap and IndexSet --- Cargo.toml | 3 +- src/__core.rs | 2 + src/indexmap.rs | 889 ++++++++++++++++++++++++++++++++++++++++++++++++ src/indexset.rs | 624 +++++++++++++++++++++++++++++++++ src/lib.rs | 11 +- src/vec.rs | 57 +++- 6 files changed, 1568 insertions(+), 18 deletions(-) create mode 100644 src/indexmap.rs create mode 100644 src/indexset.rs diff --git a/Cargo.toml b/Cargo.toml index e8d0aae5..40c362d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ version = "0.3.0" scoped_threadpool = "0.1.8" [dependencies] -generic-array = "0.11.0" \ No newline at end of file +generic-array = "0.11.0" +hash32 = "0.1.0" diff --git a/src/__core.rs b/src/__core.rs index dfb469a2..671255f4 100644 --- a/src/__core.rs +++ b/src/__core.rs @@ -1,6 +1,8 @@ /// Temporary fork of some stuff in `core` that's doesn't have a `const fn` API pub mod mem { + pub use core::mem::{replace, zeroed}; + use core::ops::{Deref, DerefMut}; #[allow(unions_with_drop_fields)] diff --git a/src/indexmap.rs b/src/indexmap.rs new file mode 100644 index 00000000..1ee5b018 --- /dev/null +++ b/src/indexmap.rs @@ -0,0 +1,889 @@ +use core::borrow::Borrow; +use core::iter::FromIterator; +use core::num::NonZeroU32; +use core::{fmt, ops, slice}; + +use generic_array::typenum::PowerOfTwo; +use generic_array::{ArrayLength, GenericArray}; + +use hash32::{BuildHasher, BuildHasherDefault, FnvHasher, Hash, Hasher}; + +use Vec; +use __core::mem; + +/// An `IndexMap` using the default FNV hasher +pub type FnvIndexMap = IndexMap>; + +#[derive(Clone, Copy, Eq, PartialEq)] +struct HashValue(u16); + +impl HashValue { + fn desired_pos(&self, mask: usize) -> usize { + usize::from(self.0) & mask + } + + fn probe_distance(&self, mask: usize, current: usize) -> usize { + current.wrapping_sub(self.desired_pos(mask) as usize) & mask + } +} + +#[doc(hidden)] +pub struct Bucket { + hash: HashValue, + key: K, + value: V, +} + +#[doc(hidden)] +#[derive(Clone, Copy, PartialEq)] +pub struct Pos { + // compact representation of `{ hash_value: u16, index: u16 }` + // To get the most from `NonZero` we store the *value minus 1*. This way `None::Option` + // is equivalent to the very unlikely value of `{ hash_value: 0xffff, index: 0xffff }` instead + // the more likely of `{ hash_value: 0x00, index: 0x00 }` + nz: NonZeroU32, +} + +impl Pos { + fn new(index: usize, hash: HashValue) -> Self { + Pos { + nz: unsafe { + NonZeroU32::new_unchecked( + ((u32::from(hash.0) << 16) + index as u32).wrapping_add(1), + ) + }, + } + } + + fn hash(&self) -> HashValue { + HashValue((self.nz.get().wrapping_sub(1) >> 16) as u16) + } + + fn index(&self) -> usize { + self.nz.get().wrapping_sub(1) as u16 as usize + } +} + +pub enum Inserted { + Done, + Swapped { prev_value: V }, + RobinHood { probe: usize, old_pos: Pos }, +} + +macro_rules! probe_loop { + ($probe_var: ident < $len: expr, $body: expr) => { + loop { + if $probe_var < $len { + $body + $probe_var += 1; + } else { + $probe_var = 0; + } + } + } +} + +struct CoreMap +where + K: Eq + Hash, + N: ArrayLength> + ArrayLength>, +{ + entries: Vec, N>, + indices: GenericArray, N>, +} + +impl CoreMap +where + K: Eq + Hash, + N: ArrayLength> + ArrayLength>, +{ + // TODO turn into a `const fn`; needs `mem::zeroed` to be a `const fn` + fn new() -> Self { + CoreMap { + entries: Vec::new(), + indices: unsafe { mem::zeroed() }, + } + } + + fn capacity() -> usize { + N::to_usize() + } + + fn mask() -> usize { + Self::capacity() - 1 + } + + fn find(&self, hash: HashValue, query: &Q) -> Option<(usize, usize)> + where + K: Borrow, + Q: ?Sized + Eq, + { + let mut probe = hash.desired_pos(Self::mask()); + let mut dist = 0; + + probe_loop!(probe < self.indices.len(), { + if let Some(pos) = self.indices[probe] { + let entry_hash = pos.hash(); + // NOTE(i) we use unchecked indexing below + let i = pos.index(); + debug_assert!(i < self.entries.len()); + + if dist > entry_hash.probe_distance(Self::mask(), probe) { + // give up when probe distance is too long + return None; + } else if entry_hash == hash && unsafe { + self.entries.get_unchecked(i).key.borrow() == query + } { + return Some((probe, i)); + } + } else { + return None; + } + + dist += 1; + }); + } + + // First phase: Look for the preferred location for key. + // + // We will know if `key` is already in the map, before we need to insert it. + // When we insert they key, it might be that we need to continue displacing + // entries (robin hood hashing), in which case Inserted::RobinHood is returned + fn insert_phase_1(&mut self, hash: HashValue, key: K, value: V) -> Inserted { + let mut probe = hash.desired_pos(Self::mask()); + let mut dist = 0; + + let inserted; + probe_loop!(probe < self.indices.len(), { + let pos = &mut self.indices[probe]; + + if let Some(pos) = *pos { + let entry_hash = pos.hash(); + // NOTE(i) we use unchecked indexing below + let i = pos.index(); + debug_assert!(i < self.entries.len()); + + let their_dist = entry_hash.probe_distance(Self::mask(), probe); + + if their_dist < dist { + // robin hood: steal the spot if it's better for us + let index = self.entries.len(); + inserted = Inserted::RobinHood { + probe: probe, + old_pos: Pos::new(index, hash), + }; + break; + } else if entry_hash == hash && unsafe { self.entries.get_unchecked(i).key == key } + { + return Inserted::Swapped { + prev_value: mem::replace( + unsafe { &mut self.entries.get_unchecked_mut(i).value }, + value, + ), + }; + } + } else { + // empty bucket, insert here + let index = self.entries.len(); + *pos = Some(Pos::new(index, hash)); + inserted = Inserted::Done; + break; + } + dist += 1; + }); + + // NOTE(unsafe) we already checked (in `insert`) that we aren't exceeding the capacity + unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) } + inserted + } + + // phase 2 is post-insert where we forward-shift `Pos` in the indices. + fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) { + probe_loop!(probe < self.indices.len(), { + let pos = unsafe { self.indices.get_unchecked_mut(probe) }; + + if let Some(pos) = pos.as_mut() { + old_pos = mem::replace(pos, old_pos); + } else { + *pos = Some(old_pos); + break; + } + }); + } + + fn remove_found(&mut self, probe: usize, found: usize) -> (K, V) { + // index `probe` and entry `found` is to be removed + // use swap_remove, but then we need to update the index that points + // to the other entry that has to move + self.indices[probe] = None; + let entry = unsafe { self.entries.swap_remove_unchecked(found) }; + + // correct index that points to the entry that had to swap places + if let Some(entry) = self.entries.get(found) { + // was not last element + // examine new element in `found` and find it in indices + let mut probe = entry.hash.desired_pos(Self::mask()); + + probe_loop!(probe < self.indices.len(), { + if let Some(pos) = self.indices[probe] { + if pos.index() >= self.entries.len() { + // found it + self.indices[probe] = Some(Pos::new(found, entry.hash)); + break; + } + } + }); + } + + self.backward_shift_after_removal(probe); + + (entry.key, entry.value) + } + + fn backward_shift_after_removal(&mut self, probe_at_remove: usize) { + // backward shift deletion in self.indices + // after probe, shift all non-ideally placed indices backward + let mut last_probe = probe_at_remove; + let mut probe = probe_at_remove + 1; + + probe_loop!(probe < self.indices.len(), { + if let Some(pos) = self.indices[probe] { + let entry_hash = pos.hash(); + + if entry_hash.probe_distance(Self::mask(), probe) > 0 { + unsafe { *self.indices.get_unchecked_mut(last_probe) = self.indices[probe] } + self.indices[probe] = None; + } else { + break; + } + } else { + break; + } + last_probe = probe; + }); + } +} + +/// Fixed capacity [`IndexMap`](https://docs.rs/indexmap/1/indexmap/map/struct.IndexMap.html) +/// +/// Note that the capacity of the `IndexMap` must be a power of 2. +/// +/// # Examples +/// +/// ``` +/// use heapless::FnvIndexMap; +/// use heapless::consts::*; +/// +/// // A hash map with a capacity of 16 key-value pairs +/// let mut book_reviews = FnvIndexMap::<_, _, U16>::new(); +/// +/// // review some books. +/// book_reviews.insert("Adventures of Huckleberry Finn", "My favorite book.").unwrap(); +/// book_reviews.insert("Grimms' Fairy Tales", "Masterpiece.").unwrap(); +/// book_reviews.insert("Pride and Prejudice", "Very enjoyable.").unwrap(); +/// book_reviews.insert("The Adventures of Sherlock Holmes", "Eye lyked it alot.").unwrap(); +/// +/// // check for a specific one. +/// if !book_reviews.contains_key("Les Misérables") { +/// println!("We've got {} reviews, but Les Misérables ain't one.", +/// book_reviews.len()); +/// } +/// +/// // oops, this review has a lot of spelling mistakes, let's delete it. +/// book_reviews.remove("The Adventures of Sherlock Holmes"); +/// +/// // look up the values associated with some keys. +/// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; +/// for book in &to_find { +/// match book_reviews.get(book) { +/// Some(review) => println!("{}: {}", book, review), +/// None => println!("{} is unreviewed.", book) +/// } +/// } +/// +/// // iterate over everything. +/// for (book, review) in &book_reviews { +/// println!("{}: \"{}\"", book, review); +/// } +/// ``` +pub struct IndexMap +where + K: Eq + Hash, + N: ArrayLength> + ArrayLength>, +{ + core: CoreMap, + build_hasher: S, +} + +impl IndexMap> +where + K: Eq + Hash, + S: Default + Hasher, + N: ArrayLength> + ArrayLength> + PowerOfTwo, +{ + // TODO turn into a `const fn`; needs `mem::zeroed` to be a `const fn` + /// Creates an empty `IndexMap`. + /// + /// **NOTE** This constructor will become a `const fn` in the future + pub fn new() -> Self { + IndexMap { + build_hasher: BuildHasherDefault::default(), + core: CoreMap::new(), + } + } +} + +impl IndexMap +where + K: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + /* Public API */ + /// Returns the number of elements the map can hold + pub fn capacity(&self) -> usize { + N::to_usize() + } + + /// Return an iterator over the keys of the map, in their order + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert("a", 1).unwrap(); + /// map.insert("b", 2).unwrap(); + /// map.insert("c", 3).unwrap(); + /// + /// for key in map.keys() { + /// println!("{}", key); + /// } + /// ``` + pub fn keys(&self) -> impl Iterator { + self.core.entries.iter().map(|bucket| &bucket.key) + } + + /// Return an iterator over the values of the map, in their order + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert("a", 1).unwrap(); + /// map.insert("b", 2).unwrap(); + /// map.insert("c", 3).unwrap(); + /// + /// for val in map.values() { + /// println!("{}", val); + /// } + /// ``` + pub fn values(&self) -> impl Iterator { + self.core.entries.iter().map(|bucket| &bucket.value) + } + + /// Return an iterator over mutable references to the the values of the map, in their order + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert("a", 1).unwrap(); + /// map.insert("b", 2).unwrap(); + /// map.insert("c", 3).unwrap(); + /// + /// for val in map.values_mut() { + /// *val += 10; + /// } + /// + /// for val in map.values() { + /// println!("{}", val); + /// } + /// ``` + pub fn values_mut(&mut self) -> impl Iterator { + self.core.entries.iter_mut().map(|bucket| &mut bucket.value) + } + + /// Return an iterator over the key-value pairs of the map, in their order + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert("a", 1).unwrap(); + /// map.insert("b", 2).unwrap(); + /// map.insert("c", 3).unwrap(); + /// + /// for (key, val) in map.iter() { + /// println!("key: {} val: {}", key, val); + /// } + /// ``` + pub fn iter(&self) -> Iter { + Iter { + iter: self.core.entries.iter(), + } + } + + /// Return an iterator over the key-value pairs of the map, in their order + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert("a", 1).unwrap(); + /// map.insert("b", 2).unwrap(); + /// map.insert("c", 3).unwrap(); + /// + /// for (_, val) in map.iter_mut() { + /// *val = 2; + /// } + /// + /// for (key, val) in &map { + /// println!("key: {} val: {}", key, val); + /// } + /// ``` + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + iter: self.core.entries.iter_mut(), + } + } + + // TODO + // pub fn entry(&mut self, key: K) -> Entry { .. } + + /// Return the number of key-value pairs in the map. + /// + /// Computes in **O(1)** time. + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut a = FnvIndexMap::<_, _, U16>::new(); + /// assert_eq!(a.len(), 0); + /// a.insert(1, "a").unwrap(); + /// assert_eq!(a.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.core.entries.len() + } + + /// Returns true if the map contains no elements. + /// + /// Computes in **O(1)** time. + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut a = FnvIndexMap::<_, _, U16>::new(); + /// assert!(a.is_empty()); + /// a.insert(1, "a"); + /// assert!(!a.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Remove all key-value pairs in the map, while preserving its capacity. + /// + /// Computes in **O(n)** time. + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut a = FnvIndexMap::<_, _, U16>::new(); + /// a.insert(1, "a"); + /// a.clear(); + /// assert!(a.is_empty()); + /// ``` + pub fn clear(&mut self) { + self.core.entries.clear(); + for pos in self.core.indices.iter_mut() { + *pos = None; + } + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + /// + /// Computes in **O(1)** time (average). + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U16>::new(); + /// map.insert(1, "a").unwrap(); + /// assert_eq!(map.get(&1), Some(&"a")); + /// assert_eq!(map.get(&2), None); + /// ``` + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + Q: ?Sized + Hash + Eq, + { + self.find(key) + .map(|(_, found)| unsafe { &self.core.entries.get_unchecked(found).value }) + } + + /// Returns true if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + /// + /// Computes in **O(1)** time (average). + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U8>::new(); + /// map.insert(1, "a").unwrap(); + /// assert_eq!(map.contains_key(&1), true); + /// assert_eq!(map.contains_key(&2), false); + /// ``` + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow, + Q: ?Sized + Eq + Hash, + { + self.find(key).is_some() + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + /// + /// Computes in **O(1)** time (average). + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U8>::new(); + /// map.insert(1, "a").unwrap(); + /// if let Some(x) = map.get_mut(&1) { + /// *x = "b"; + /// } + /// assert_eq!(map[&1], "b"); + /// ``` + pub fn get_mut<'v, Q>(&'v mut self, key: &Q) -> Option<&'v mut V> + where + K: Borrow, + Q: ?Sized + Hash + Eq, + { + if let Some((_, found)) = self.find(key) { + Some(unsafe { &mut self.core.entries.get_unchecked_mut(found).value }) + } else { + None + } + } + + /// Inserts a key-value pair into the map. + /// + /// If an equivalent key already exists in the map: the key remains and retains in its place in + /// the order, its corresponding value is updated with `value` and the older value is returned + /// inside `Some(_)`. + /// + /// If no equivalent key existed in the map: the new key-value pair is inserted, last in order, + /// and `None` is returned. + /// + /// Computes in **O(1)** time (average). + /// + /// See also entry if you you want to insert or modify or if you need to get the index of the + /// corresponding key-value pair. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexMap; + /// use heapless::consts::*; + /// + /// let mut map = FnvIndexMap::<_, _, U8>::new(); + /// assert_eq!(map.insert(37, "a"), Ok(None)); + /// assert_eq!(map.is_empty(), false); + /// + /// map.insert(37, "b"); + /// assert_eq!(map.insert(37, "c"), Ok(Some("b"))); + /// assert_eq!(map[&37], "c"); + /// ``` + pub fn insert(&mut self, key: K, value: V) -> Result, (K, V)> { + if self.core.entries.is_full() { + Err((key, value)) + } else { + Ok(match self.insert_phase_1(key, value) { + Inserted::Swapped { prev_value } => Some(prev_value), + Inserted::Done => None, + Inserted::RobinHood { probe, old_pos } => { + self.core.insert_phase_2(probe, old_pos); + None + } + }) + } + } + + /// Same as [`swap_remove`](struct.IndexMap.html#method.swap_remove) + /// + /// Computes in **O(1)** time (average). + pub fn remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: ?Sized + Hash + Eq, + { + self.swap_remove(key) + } + + /// Remove the key-value pair equivalent to `key` and return its value. + /// + /// Like `Vec::swap_remove`, the pair is removed by swapping it with the last element of the map + /// and popping it off. **This perturbs the postion of what used to be the last element!** + /// + /// Return `None` if `key` is not in map. + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: ?Sized + Hash + Eq, + { + self.find(key) + .map(|(probe, found)| self.core.remove_found(probe, found).1) + } + + /* Private API */ + /// Return probe (indices) and position (entries) + fn find(&self, key: &Q) -> Option<(usize, usize)> + where + K: Borrow, + Q: ?Sized + Hash + Eq, + { + if self.len() == 0 { + return None; + } + let h = hash_with(key, &self.build_hasher); + self.core.find(h, key) + } + + fn insert_phase_1(&mut self, key: K, value: V) -> Inserted { + let hash = hash_with(&key, &self.build_hasher); + self.core.insert_phase_1(hash, key, value) + } +} + +impl<'a, K, Q, V, N, S> ops::Index<&'a Q> for IndexMap +where + K: Eq + Hash + Borrow, + Q: ?Sized + Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + type Output = V; + + fn index(&self, key: &Q) -> &V { + self.get(key).expect("key not found") + } +} + +impl<'a, K, Q, V, N, S> ops::IndexMut<&'a Q> for IndexMap +where + K: Eq + Hash + Borrow, + Q: ?Sized + Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn index_mut(&mut self, key: &Q) -> &mut V { + self.get_mut(key).expect("key not found") + } +} + +impl fmt::Debug for IndexMap +where + K: Eq + Hash + fmt::Debug, + V: fmt::Debug, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl Default for IndexMap +where + K: Eq + Hash, + S: BuildHasher + Default, + N: ArrayLength> + ArrayLength>, +{ + fn default() -> Self { + IndexMap { + build_hasher: <_>::default(), + core: CoreMap::new(), + } + } +} + +impl Extend<(K, V)> for IndexMap +where + K: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn extend(&mut self, iterable: I) + where + I: IntoIterator, + { + for (k, v) in iterable { + self.insert(k, v).ok().unwrap(); + } + } +} + +impl<'a, K, V, N, S> Extend<(&'a K, &'a V)> for IndexMap +where + K: Eq + Hash + Copy, + V: Copy, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn extend(&mut self, iterable: I) + where + I: IntoIterator, + { + self.extend(iterable.into_iter().map(|(&key, &value)| (key, value))) + } +} + +impl FromIterator<(K, V)> for IndexMap +where + K: Eq + Hash, + S: BuildHasher + Default, + N: ArrayLength> + ArrayLength>, +{ + fn from_iter(iterable: I) -> Self + where + I: IntoIterator, + { + let mut map = IndexMap::default(); + map.extend(iterable); + map + } +} + +impl<'a, K, V, N, S> IntoIterator for &'a IndexMap +where + K: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, K, V, N, S> IntoIterator for &'a mut IndexMap +where + K: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +pub struct Iter<'a, K, V> +where + K: 'a, + V: 'a, +{ + iter: slice::Iter<'a, Bucket>, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> +where + K: 'a, + V: 'a, +{ + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + self.iter.next().map(|bucket| (&bucket.key, &bucket.value)) + } +} + +pub struct IterMut<'a, K, V> +where + K: 'a, + V: 'a, +{ + iter: slice::IterMut<'a, Bucket>, +} + +impl<'a, K, V> Iterator for IterMut<'a, K, V> +where + K: 'a, + V: 'a, +{ + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|bucket| (&bucket.key, &mut bucket.value)) + } +} + +fn hash_with(key: &K, build_hasher: &S) -> HashValue +where + K: ?Sized + Hash, + S: BuildHasher, +{ + let mut h = build_hasher.build_hasher(); + key.hash(&mut h); + HashValue(h.finish() as u16) +} + +#[cfg(test)] +mod tests { + use core::mem; + + use generic_array::typenum::Unsigned; + + use consts::*; + use FnvIndexMap; + + #[test] + fn size() { + type Cap = U4; + + let cap = Cap::to_usize(); + assert_eq!( + mem::size_of::>(), + cap * mem::size_of::() + // indices + cap * (mem::size_of::() + // key + mem::size_of::() + // value + mem::size_of::() // hash + ) + // buckets + mem::size_of::() // entries.length + ) + } +} diff --git a/src/indexset.rs b/src/indexset.rs new file mode 100644 index 00000000..b68e3037 --- /dev/null +++ b/src/indexset.rs @@ -0,0 +1,624 @@ +use core::borrow::Borrow; +use core::fmt; +use core::iter::FromIterator; + +use generic_array::typenum::PowerOfTwo; +use generic_array::ArrayLength; +use hash32::{BuildHasher, BuildHasherDefault, FnvHasher, Hash, Hasher}; + +use indexmap::{self, Bucket, IndexMap, Pos}; + +/// An `IndexSet` using the default FNV hasher +pub type FnvIndexSet = IndexSet>; + +/// Fixed capacity [`IndexSet`](https://docs.rs/indexmap/1/indexmap/set/struct.IndexSet.html) +/// +/// Note that the capacity of the `IndexSet` must be a power of 2. +/// +/// # Examples +/// +/// ``` +/// use heapless::FnvIndexSet; +/// use heapless::consts::*; +/// +/// // A hash set with a capacity of 16 elements +/// let mut books = FnvIndexSet::<_, U16>::new(); +/// +/// // Add some books. +/// books.insert("A Dance With Dragons").unwrap(); +/// books.insert("To Kill a Mockingbird").unwrap(); +/// books.insert("The Odyssey").unwrap(); +/// books.insert("The Great Gatsby").unwrap(); +/// +/// // Check for a specific one. +/// if !books.contains("The Winds of Winter") { +/// println!("We have {} books, but The Winds of Winter ain't one.", +/// books.len()); +/// } +/// +/// // Remove a book. +/// books.remove("The Odyssey"); +/// +/// // Iterate over everything. +/// for book in &books { +/// println!("{}", book); +/// } +/// ``` +pub struct IndexSet +where + T: Eq + Hash, + N: ArrayLength> + ArrayLength>, +{ + map: IndexMap, +} + +impl IndexSet> +where + T: Eq + Hash, + S: Default + Hasher, + N: ArrayLength> + ArrayLength> + PowerOfTwo, +{ + /// Creates an empty `IndexSet` + pub fn new() -> Self { + IndexSet { + map: IndexMap::new(), + } + } +} + +impl IndexSet +where + T: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + /// Returns the number of elements the set can hold + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let set = FnvIndexSet::::new(); + /// assert_eq!(set.capacity(), 16); + /// ``` + pub fn capacity(&self) -> usize { + self.map.capacity() + } + + /// Return an iterator over the values of the set, in their order + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut set = FnvIndexSet::<_, U16>::new(); + /// set.insert("a").unwrap(); + /// set.insert("b").unwrap(); + /// + /// // Will print in an arbitrary order. + /// for x in set.iter() { + /// println!("{}", x); + /// } + /// ``` + pub fn iter(&self) -> Iter { + Iter { + iter: self.map.iter(), + } + } + + /// Visits the values representing the difference, i.e. the values that are in `self` but not in + /// `other`. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut a: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut b: FnvIndexSet<_, U16> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Can be seen as `a - b`. + /// for x in a.difference(&b) { + /// println!("{}", x); // Print 1 + /// } + /// + /// let diff: FnvIndexSet<_, U16> = a.difference(&b).collect(); + /// assert_eq!(diff, [1].iter().collect::>()); + /// + /// // Note that difference is not symmetric, + /// // and `b - a` means something else: + /// let diff: FnvIndexSet<_, U16> = b.difference(&a).collect(); + /// assert_eq!(diff, [4].iter().collect::>()); + /// ``` + pub fn difference<'a, N2, S2>( + &'a self, + other: &'a IndexSet, + ) -> Difference<'a, T, N2, S2> + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + Difference { + iter: self.iter(), + other, + } + } + + /// Visits the values representing the symmetric difference, i.e. the values that are in `self` + /// or in `other` but not in both. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut a: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut b: FnvIndexSet<_, U16> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 1, 4 in that order order. + /// for x in a.symmetric_difference(&b) { + /// println!("{}", x); + /// } + /// + /// let diff1: FnvIndexSet<_, U16> = a.symmetric_difference(&b).collect(); + /// let diff2: FnvIndexSet<_, U16> = b.symmetric_difference(&a).collect(); + /// + /// assert_eq!(diff1, diff2); + /// assert_eq!(diff1, [1, 4].iter().collect::>()); + /// ``` + pub fn symmetric_difference<'a, N2, S2>( + &'a self, + other: &'a IndexSet, + ) -> impl Iterator + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + self.difference(other).chain(other.difference(self)) + } + + /// Visits the values representing the intersection, i.e. the values that are both in `self` and + /// `other`. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut a: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut b: FnvIndexSet<_, U16> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 2, 3 in that order. + /// for x in a.intersection(&b) { + /// println!("{}", x); + /// } + /// + /// let intersection: FnvIndexSet<_, U16> = a.intersection(&b).collect(); + /// assert_eq!(intersection, [2, 3].iter().collect::>()); + /// ``` + pub fn intersection<'a, N2, S2>( + &'a self, + other: &'a IndexSet, + ) -> Intersection<'a, T, N2, S2> + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + Intersection { + iter: self.iter(), + other, + } + } + + /// Visits the values representing the union, i.e. all the values in `self` or `other`, without + /// duplicates. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut a: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut b: FnvIndexSet<_, U16> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 1, 2, 3, 4 in that order. + /// for x in a.union(&b) { + /// println!("{}", x); + /// } + /// + /// let union: FnvIndexSet<_, U16> = a.union(&b).collect(); + /// assert_eq!(union, [1, 2, 3, 4].iter().collect::>()); + /// ``` + pub fn union<'a, N2, S2>( + &'a self, + other: &'a IndexSet, + ) -> impl Iterator + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + self.iter().chain(other.difference(self)) + } + + /// Returns the number of elements in the set. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut v: FnvIndexSet<_, U16> = FnvIndexSet::new(); + /// assert_eq!(v.len(), 0); + /// v.insert(1).unwrap(); + /// assert_eq!(v.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if the set contains no elements. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut v: FnvIndexSet<_, U16> = FnvIndexSet::new(); + /// assert!(v.is_empty()); + /// v.insert(1).unwrap(); + /// assert!(!v.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// Clears the set, removing all values. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut v: FnvIndexSet<_, U16> = FnvIndexSet::new(); + /// v.insert(1).unwrap(); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + pub fn clear(&mut self) { + self.map.clear() + } + + /// Returns `true` if the set contains a value. + /// + /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the + /// borrowed form must match those for the value type. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let set: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.contains(&1), true); + /// assert_eq!(set.contains(&4), false); + /// ``` + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Eq + Hash, + { + self.map.contains_key(value) + } + + /// Returns `true` if `self` has no elements in common with `other`. This is equivalent to + /// checking for an empty intersection. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let a: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut b = FnvIndexSet::<_, U16>::new(); + /// + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(4).unwrap(); + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(1).unwrap(); + /// assert_eq!(a.is_disjoint(&b), false); + /// ``` + pub fn is_disjoint(&self, other: &IndexSet) -> bool + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + self.iter().all(|v| !other.contains(v)) + } + + /// Returns `true` if the set is a subset of another, i.e. `other` contains at least all the + /// values in `self`. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let sup: FnvIndexSet<_, U16> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = FnvIndexSet::<_, U16>::new(); + /// + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(2).unwrap(); + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(4).unwrap(); + /// assert_eq!(set.is_subset(&sup), false); + /// ``` + pub fn is_subset(&self, other: &IndexSet) -> bool + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + self.iter().all(|v| other.contains(v)) + } + + // Returns `true` if the set is a superset of another, i.e. `self` contains at least all the + // values in `other`. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let sub: FnvIndexSet<_, U16> = [1, 2].iter().cloned().collect(); + /// let mut set = FnvIndexSet::<_, U16>::new(); + /// + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(0).unwrap(); + /// set.insert(1).unwrap(); + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(2).unwrap(); + /// assert_eq!(set.is_superset(&sub), true); + /// ``` + pub fn is_superset(&self, other: &IndexSet) -> bool + where + N2: ArrayLength> + ArrayLength>, + S2: BuildHasher, + { + other.is_subset(self) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, `true` is returned. + /// + /// If the set did have this value present, `false` is returned. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut set = FnvIndexSet::<_, U16>::new(); + /// + /// assert_eq!(set.insert(2).unwrap(), true); + /// assert_eq!(set.insert(2).unwrap(), false); + /// assert_eq!(set.len(), 1); + /// ``` + pub fn insert(&mut self, value: T) -> Result { + self.map + .insert(value, ()) + .map(|old| old.is_none()) + .map_err(|(k, _)| k) + } + + /// Removes a value from the set. Returns `true` if the value was present in the set. + /// + /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the + /// borrowed form must match those for the value type. + /// + /// # Examples + /// + /// ``` + /// use heapless::FnvIndexSet; + /// use heapless::consts::*; + /// + /// let mut set = FnvIndexSet::<_, U16>::new(); + /// + /// set.insert(2).unwrap(); + /// assert_eq!(set.remove(&2), true); + /// assert_eq!(set.remove(&2), false); + /// ``` + pub fn remove(&mut self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Eq + Hash, + { + self.map.remove(value).is_some() + } +} + +impl fmt::Debug for IndexSet +where + T: Eq + Hash + fmt::Debug, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +impl Default for IndexSet +where + T: Eq + Hash, + S: BuildHasher + Default, + N: ArrayLength> + ArrayLength>, +{ + fn default() -> Self { + IndexSet { + map: <_>::default(), + } + } +} + +impl PartialEq> for IndexSet +where + T: Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, + N1: ArrayLength> + ArrayLength>, + N2: ArrayLength> + ArrayLength>, +{ + fn eq(&self, other: &IndexSet) -> bool { + self.len() == other.len() && self.is_subset(other) + } +} + +impl Extend for IndexSet +where + T: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn extend(&mut self, iterable: I) + where + I: IntoIterator, + { + self.map.extend(iterable.into_iter().map(|k| (k, ()))) + } +} + +impl<'a, T, N, S> Extend<&'a T> for IndexSet +where + T: 'a + Eq + Hash + Copy, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + fn extend(&mut self, iterable: I) + where + I: IntoIterator, + { + self.extend(iterable.into_iter().cloned()) + } +} + +impl FromIterator for IndexSet +where + T: Eq + Hash, + S: BuildHasher + Default, + N: ArrayLength> + ArrayLength>, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let mut set = IndexSet::default(); + set.extend(iter); + set + } +} + +impl<'a, T, N, S> IntoIterator for &'a IndexSet +where + T: Eq + Hash, + S: BuildHasher, + N: ArrayLength> + ArrayLength>, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub struct Iter<'a, T> +where + T: 'a, +{ + iter: indexmap::Iter<'a, T, ()>, +} + +impl<'a, T> Iterator for Iter<'a, T> +where + T: 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + self.iter.next().map(|(k, _)| k) + } +} + +pub struct Difference<'a, T, N, S> +where + S: 'a + BuildHasher, + T: 'a + Eq + Hash, + N: 'a + ArrayLength> + ArrayLength>, +{ + iter: Iter<'a, T>, + other: &'a IndexSet, +} + +impl<'a, T, N, S> Iterator for Difference<'a, T, N, S> +where + S: 'a + BuildHasher, + T: 'a + Eq + Hash, + N: 'a + ArrayLength> + ArrayLength>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + loop { + let elt = self.iter.next()?; + if !self.other.contains(elt) { + return Some(elt); + } + } + } +} + +pub struct Intersection<'a, T, N, S> +where + S: 'a + BuildHasher, + T: 'a + Eq + Hash, + N: 'a + ArrayLength> + ArrayLength>, +{ + iter: Iter<'a, T>, + other: &'a IndexSet, +} + +impl<'a, T, N, S> Iterator for Intersection<'a, T, N, S> +where + S: 'a + BuildHasher, + T: 'a + Eq + Hash, + N: 'a + ArrayLength> + ArrayLength>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + loop { + let elt = self.iter.next()?; + if self.other.contains(elt) { + return Some(elt); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6667b496..ef48d8eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,32 +40,41 @@ //! List of currently implemented data structures: //! //! - [`BinaryHeap`](binary_heap/struct.BinaryHeap.html) -- priority queue +//! - [`IndexMap`](struct.IndexMap.html) -- hash table +//! - [`IndexSet`](struct.IndexSet.html) -- hash set //! - [`LinearMap`](struct.LinearMap.html) //! - [`RingBuffer`](ring_buffer/struct.RingBuffer.html) -- single producer single consumer lockless //! queue //! - [`String`](struct.String.html) //! - [`Vec`](struct.Vec.html) -#![allow(stable_features)] +#![allow(warnings)] #![deny(missing_docs)] #![deny(warnings)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(nll)] +#![feature(nonzero)] #![feature(untagged_unions)] #![no_std] extern crate generic_array; +extern crate hash32; #[cfg(test)] extern crate std; pub use binary_heap::BinaryHeap; pub use generic_array::typenum::consts; +pub use indexmap::{FnvIndexMap, IndexMap}; +pub use indexset::{FnvIndexSet, IndexSet}; pub use linear_map::LinearMap; pub use ring_buffer::RingBuffer; pub use string::String; pub use vec::Vec; mod cfail; +mod indexmap; +mod indexset; mod linear_map; mod string; mod vec; diff --git a/src/vec.rs b/src/vec.rs index ac4a0280..a8df1c59 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -17,6 +17,7 @@ impl Vec where N: ArrayLength, { + /* Constructors */ /// Constructs a new, empty vector with a fixed capacity of `N` pub const fn new() -> Self { Vec { @@ -25,9 +26,10 @@ where } } + /* Public API */ /// Returns the maximum number of elements the vector can hold pub fn capacity(&self) -> usize { - self.buffer.as_slice().len() + N::to_usize() } /// Clears the vector, removing all values. @@ -68,35 +70,44 @@ where /// Removes the last element from a vector and return it, or `None` if it's empty pub fn pop(&mut self) -> Option { - let buffer = self.buffer.as_slice(); - if self.len != 0 { - self.len -= 1; - let item = unsafe { ptr::read(&buffer[self.len]) }; - Some(item) + Some(unsafe { self.pop_unchecked() }) } else { None } } + pub(crate) unsafe fn pop_unchecked(&mut self) -> T { + debug_assert!(!self.is_empty()); + + let buffer = self.buffer.as_slice(); + + self.len -= 1; + let item = ptr::read(buffer.get_unchecked(self.len)); + item + } + /// Appends an `item` to the back of the collection /// /// Returns back the `item` if the vector is full pub fn push(&mut self, item: T) -> Result<(), T> { - let capacity = self.capacity(); - let buffer = self.buffer.as_mut_slice(); - - if self.len < capacity { - // NOTE(ptr::write) the memory slot that we are about to write to is uninitialized. We - // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory - unsafe { ptr::write(&mut buffer[self.len], item) } - self.len += 1; + if self.len < self.capacity() { + unsafe { self.push_unchecked(item) } Ok(()) } else { Err(item) } } + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + let buffer = self.buffer.as_mut_slice(); + + // NOTE(ptr::write) the memory slot that we are about to write to is uninitialized. We + // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory + ptr::write(buffer.get_unchecked_mut(self.len), item); + self.len += 1; + } + /// Shortens the vector, keeping the first `len` elements and dropping the rest. pub fn truncate(&mut self, len: usize) { unsafe { @@ -180,9 +191,23 @@ where /// assert_eq!(&*v, ["baz", "qux"]); /// ``` pub fn swap_remove(&mut self, index: usize) -> T { + assert!(index < self.len()); + unsafe { self.swap_remove_unchecked(index) } + } + + pub(crate) unsafe fn swap_remove_unchecked(&mut self, index: usize) -> T { let length = self.len(); - self.swap(index, length - 1); - self.pop().unwrap() + debug_assert!(index < length); + ptr::swap( + self.get_unchecked_mut(index), + self.get_unchecked_mut(length - 1), + ); + self.pop_unchecked() + } + + /* Private API */ + pub(crate) fn is_full(&self) -> bool { + self.capacity() == self.len() } }