From 6ac7330c94bc04ac763a0b9cd8ec9bc576357196 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 10:59:30 -0800 Subject: [PATCH 01/19] Prepare for indexmap 1.9.0, with MSRV 1.56 (cherry picked from commit 61bb73ed5d5b6c092a7ffe43f193db38497c1df2) --- .github/workflows/ci.yml | 4 ++-- .rustfmt.toml | 2 +- Cargo.toml | 28 ++++++---------------------- README.rst | 4 ++-- src/lib.rs | 2 +- test-nostd/Cargo.toml | 3 +-- test-serde/Cargo.toml | 3 +-- 7 files changed, 14 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13696eeb..8c6c10cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: include: - - rust: 1.49.0 # MSRV + - rust: 1.56.0 # MSRV features: - rust: stable features: serde @@ -59,7 +59,7 @@ jobs: strategy: matrix: include: - - rust: 1.49.0 + - rust: 1.56.0 target: thumbv6m-none-eabi - rust: stable target: thumbv6m-none-eabi diff --git a/.rustfmt.toml b/.rustfmt.toml index 32a9786f..3a26366d 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1 +1 @@ -edition = "2018" +edition = "2021" diff --git a/Cargo.toml b/Cargo.toml index b8848f3f..7ebffe60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,37 +1,21 @@ [package] name = "indexmap" -edition = "2018" -version = "1.8.2" -authors = [ -"bluss", -"Josh Stone " -] +edition = "2021" +version = "1.9.0" documentation = "https://docs.rs/indexmap/" repository = "https://github.com/bluss/indexmap" -license = "Apache-2.0/MIT" -description = """ -A hash table with consistent order and fast iteration. - -The indexmap is a hash table where the iteration order of the key-value -pairs is independent of the hash values of the keys. It has the usual -hash table functionality, it preserves insertion order except after -removals, and it allows lookup of its elements by either hash table key -or numerical index. A corresponding hash set type is also provided. - -This crate was initially published under the name ordermap, but it was renamed to -indexmap. -""" - +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"] - -build = "build.rs" +rust-version = "1.56" [lib] bench = false [build-dependencies] autocfg = "1" + [dependencies] serde = { version = "1.0", optional = true, default-features = false } rayon = { version = "1.4.1", optional = true } diff --git a/README.rst b/README.rst index da76a68b..4dac24f4 100644 --- a/README.rst +++ b/README.rst @@ -12,8 +12,8 @@ indexmap .. |docs| image:: https://docs.rs/indexmap/badge.svg .. _docs: https://docs.rs/indexmap -.. |rustc| image:: https://img.shields.io/badge/rust-1.49%2B-orange.svg -.. _rustc: https://img.shields.io/badge/rust-1.49%2B-orange.svg +.. |rustc| image:: https://img.shields.io/badge/rust-1.56%2B-orange.svg +.. _rustc: https://img.shields.io/badge/rust-1.56%2B-orange.svg A pure-Rust hash table which preserves (in a limited sense) insertion order. diff --git a/src/lib.rs b/src/lib.rs index 8c44d7bf..ba613342 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ //! //! ### Rust Version //! -//! This version of indexmap requires Rust 1.49 or later. +//! This version of indexmap requires Rust 1.56 or later. //! //! The indexmap 1.x release series will use a carefully considered version //! upgrade policy, where in a later 1.x version, we will raise the minimum diff --git a/test-nostd/Cargo.toml b/test-nostd/Cargo.toml index 0201a728..3ae606d5 100644 --- a/test-nostd/Cargo.toml +++ b/test-nostd/Cargo.toml @@ -1,9 +1,8 @@ [package] name = "test-nostd" version = "0.1.0" -authors = ["bluss"] publish = false -edition = "2018" +edition = "2021" [dependencies] indexmap = { path = "..", features = ["serde-1"] } diff --git a/test-serde/Cargo.toml b/test-serde/Cargo.toml index f7abc9e5..791c0a38 100644 --- a/test-serde/Cargo.toml +++ b/test-serde/Cargo.toml @@ -1,9 +1,8 @@ [package] name = "test-serde" version = "0.1.0" -authors = ["bluss"] publish = false -edition = "2018" +edition = "2021" [dependencies] From 9c54555ff58aab35149852d4d0908c740c679dd9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 16 Jun 2022 16:21:36 -0700 Subject: [PATCH 02/19] Assume support for const-generics --- build.rs | 1 - src/map.rs | 4 ++-- src/set.rs | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.rs b/build.rs index 7c5b6d5e..9f9fa054 100644 --- a/build.rs +++ b/build.rs @@ -4,6 +4,5 @@ fn main() { Some(_) => autocfg::emit("has_std"), None => autocfg::new().emit_sysroot_crate("std"), } - autocfg::new().emit_rustc_version(1, 51); autocfg::rerun_path("build.rs"); } diff --git a/src/map.rs b/src/map.rs index 56330470..630e23fe 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1391,7 +1391,7 @@ where } } -#[cfg(all(has_std, rustc_1_51))] +#[cfg(has_std)] impl From<[(K, V); N]> for IndexMap where K: Hash + Eq, @@ -1906,7 +1906,7 @@ mod tests { } #[test] - #[cfg(all(has_std, rustc_1_51))] + #[cfg(has_std)] fn from_array() { let map = IndexMap::from([(1, 2), (3, 4)]); let mut expected = IndexMap::new(); diff --git a/src/set.rs b/src/set.rs index e82a8d07..875819f2 100644 --- a/src/set.rs +++ b/src/set.rs @@ -888,7 +888,7 @@ where } } -#[cfg(all(has_std, rustc_1_51))] +#[cfg(has_std)] impl From<[T; N]> for IndexSet where T: Eq + Hash, @@ -1882,7 +1882,7 @@ mod tests { } #[test] - #[cfg(all(has_std, rustc_1_51))] + #[cfg(has_std)] fn from_array() { let set1 = IndexSet::from([1, 2, 3, 4]); let set2: IndexSet<_> = [1, 2, 3, 4].into(); From 73932eff245248fd89356f1efe4d4f92784eb3ec Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 11:22:43 -0800 Subject: [PATCH 03/19] Use prelude FromIterator (cherry picked from commit ffd875b1c03e54299948f2c015965c679ca8737e) --- benches/bench.rs | 1 - benches/faststring.rs | 1 - src/map.rs | 4 ++-- src/set.rs | 4 ++-- test-nostd/src/lib.rs | 1 - tests/quick.rs | 1 - 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index d6de602d..a4e8e21b 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -15,7 +15,6 @@ use test::Bencher; use indexmap::IndexMap; use std::collections::HashMap; -use std::iter::FromIterator; use rand::rngs::SmallRng; use rand::seq::SliceRandom; diff --git a/benches/faststring.rs b/benches/faststring.rs index 86b7e9cf..ecc28b40 100644 --- a/benches/faststring.rs +++ b/benches/faststring.rs @@ -7,7 +7,6 @@ use test::Bencher; use indexmap::IndexMap; use std::collections::HashMap; -use std::iter::FromIterator; use rand::rngs::SmallRng; use rand::seq::SliceRandom; diff --git a/src/map.rs b/src/map.rs index 630e23fe..c8106702 100644 --- a/src/map.rs +++ b/src/map.rs @@ -12,7 +12,7 @@ use crate::vec::{self, Vec}; use ::core::cmp::Ordering; use ::core::fmt; use ::core::hash::{BuildHasher, Hash, Hasher}; -use ::core::iter::{FromIterator, FusedIterator}; +use ::core::iter::FusedIterator; use ::core::ops::{Index, IndexMut, RangeBounds}; use ::core::slice::{Iter as SliceIter, IterMut as SliceIterMut}; @@ -1406,7 +1406,7 @@ where /// assert_eq!(map1, map2); /// ``` fn from(arr: [(K, V); N]) -> Self { - std::array::IntoIter::new(arr).collect() + Self::from_iter(arr) } } diff --git a/src/set.rs b/src/set.rs index 875819f2..36e8c9fa 100644 --- a/src/set.rs +++ b/src/set.rs @@ -10,7 +10,7 @@ use crate::vec::{self, Vec}; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; -use core::iter::{Chain, FromIterator, FusedIterator}; +use core::iter::{Chain, FusedIterator}; use core::ops::{BitAnd, BitOr, BitXor, Index, RangeBounds, Sub}; use core::slice; @@ -903,7 +903,7 @@ where /// assert_eq!(set1, set2); /// ``` fn from(arr: [T; N]) -> Self { - std::array::IntoIter::new(arr).collect() + Self::from_iter(arr) } } diff --git a/test-nostd/src/lib.rs b/test-nostd/src/lib.rs index 0b57b092..27bfe608 100644 --- a/test-nostd/src/lib.rs +++ b/test-nostd/src/lib.rs @@ -2,7 +2,6 @@ use core::hash::BuildHasherDefault; use core::hash::Hasher; -use core::iter::FromIterator; use indexmap::IndexMap; use indexmap::IndexSet; diff --git a/tests/quick.rs b/tests/quick.rs index 6c6cc034..4f462878 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -16,7 +16,6 @@ use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Debug; use std::hash::Hash; -use std::iter::FromIterator; use std::ops::Bound; use std::ops::Deref; From 6faaf77bf40a710886bf470e8ac2191a4af108aa Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 12:06:55 -0800 Subject: [PATCH 04/19] Upgrade to hashbrown 0.12 (cherry picked from commit 8eeea2ce0cbbfb52bc1c1ac9acfab26336e500e0) --- Cargo.toml | 2 +- src/map/core.rs | 24 ++++++------------------ src/map/core/raw.rs | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ebffe60..b75c9a5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rayon = { version = "1.4.1", optional = true } rustc-rayon = { version = "0.4", optional = true } [dependencies.hashbrown] -version = "0.11" +version = "0.12" default-features = false features = ["raw"] diff --git a/src/map/core.rs b/src/map/core.rs index bfd571d9..0ed9b1ff 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -18,7 +18,7 @@ use core::mem::replace; use core::ops::RangeBounds; use crate::equivalent::Equivalent; -use crate::util::{enumerate, simplify_range}; +use crate::util::simplify_range; use crate::{Bucket, Entries, HashValue}; /// Core of the map that does not depend on S @@ -185,9 +185,7 @@ impl IndexMapCore { let entries = self.entries.split_off(at); let mut indices = RawTable::with_capacity(entries.len()); - for (i, entry) in enumerate(&entries) { - indices.insert_no_grow(entry.hash.get(), i); - } + raw::insert_bulk_no_grow(&mut indices, &entries); Self { indices, entries } } @@ -372,15 +370,9 @@ impl IndexMapCore { // Reinsert everything, as there are few kept indices self.indices.clear(); - // Reinsert stable indices - for (i, entry) in enumerate(start_entries) { - self.indices.insert_no_grow(entry.hash.get(), i); - } - - // Reinsert shifted indices - for (i, entry) in (start..).zip(shifted_entries) { - self.indices.insert_no_grow(entry.hash.get(), i); - } + // Reinsert stable indices, then shifted indices + raw::insert_bulk_no_grow(&mut self.indices, start_entries); + raw::insert_bulk_no_grow(&mut self.indices, shifted_entries); } else if erased + shifted < half_capacity { // Find each affected index, as there are few to adjust @@ -429,11 +421,7 @@ impl IndexMapCore { fn rebuild_hash_table(&mut self) { self.indices.clear(); - debug_assert!(self.indices.capacity() >= self.entries.len()); - for (i, entry) in enumerate(&self.entries) { - // We should never have to reallocate, so there's no need for a real hasher. - self.indices.insert_no_grow(entry.hash.get(), i); - } + raw::insert_bulk_no_grow(&mut self.indices, &self.entries); } pub(crate) fn reverse(&mut self) { diff --git a/src/map/core/raw.rs b/src/map/core/raw.rs index 168e1af3..bf1672d5 100644 --- a/src/map/core/raw.rs +++ b/src/map/core/raw.rs @@ -2,13 +2,26 @@ //! This module encapsulates the `unsafe` access to `hashbrown::raw::RawTable`, //! mostly in dealing with its bucket "pointers". -use super::{equivalent, Entry, HashValue, IndexMapCore, VacantEntry}; +use super::{equivalent, Bucket, Entry, HashValue, IndexMapCore, VacantEntry}; use core::fmt; use core::mem::replace; use hashbrown::raw::RawTable; type RawBucket = hashbrown::raw::Bucket; +/// Inserts many entries into a raw table without reallocating. +/// +/// ***Panics*** if there is not sufficient capacity already. +pub(super) fn insert_bulk_no_grow(indices: &mut RawTable, entries: &[Bucket]) { + assert!(indices.capacity() - indices.len() >= entries.len()); + for entry in entries { + // SAFETY: we asserted that sufficient capacity exists for all entries. + unsafe { + indices.insert_no_grow(entry.hash.get(), indices.len()); + } + } +} + pub(super) struct DebugIndices<'a>(pub &'a RawTable); impl fmt::Debug for DebugIndices<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From 23eb69d936656e31af90c29333ba4de39912c60d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 12:07:08 -0800 Subject: [PATCH 05/19] Remove crate::util::enumerate (cherry picked from commit bdd93a379955a250c4629e86cc46ec2ae2848ebf) --- src/map.rs | 7 +++---- src/set.rs | 11 +++++------ src/util.rs | 8 -------- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/map.rs b/src/map.rs index c8106702..9e4d1113 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1495,7 +1495,6 @@ where #[cfg(test)] mod tests { use super::*; - use crate::util::enumerate; use std::string::String; #[test] @@ -1524,7 +1523,7 @@ mod tests { let not_present = [1, 3, 6, 9, 10]; let mut map = IndexMap::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(map.len(), i); map.insert(elt, elt); assert_eq!(map.len(), i + 1); @@ -1544,7 +1543,7 @@ mod tests { let present = vec![1, 6, 2]; let mut map = IndexMap::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(map.len(), i); let (index, existing) = map.insert_full(elt, elt); assert_eq!(existing, None); @@ -1611,7 +1610,7 @@ mod tests { let not_present = [1, 3, 6, 9, 10]; let mut map = IndexMap::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(map.len(), i); map.insert(elt, elt); assert_eq!(map.len(), i + 1); diff --git a/src/set.rs b/src/set.rs index 36e8c9fa..ca475530 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1366,7 +1366,6 @@ where #[cfg(test)] mod tests { use super::*; - use crate::util::enumerate; use std::string::String; #[test] @@ -1395,7 +1394,7 @@ mod tests { let not_present = [1, 3, 6, 9, 10]; let mut set = IndexSet::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(set.len(), i); set.insert(elt); assert_eq!(set.len(), i + 1); @@ -1414,7 +1413,7 @@ mod tests { let present = vec![1, 6, 2]; let mut set = IndexSet::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(set.len(), i); let (index, success) = set.insert_full(elt); assert!(success); @@ -1501,7 +1500,7 @@ mod tests { let not_present = [1, 3, 6, 9, 10]; let mut set = IndexSet::with_capacity(replace.len()); - for (i, &elt) in enumerate(&replace) { + for (i, &elt) in replace.iter().enumerate() { assert_eq!(set.len(), i); set.replace(elt); assert_eq!(set.len(), i + 1); @@ -1520,7 +1519,7 @@ mod tests { let present = vec![1, 6, 2]; let mut set = IndexSet::with_capacity(replace.len()); - for (i, &elt) in enumerate(&replace) { + for (i, &elt) in replace.iter().enumerate() { assert_eq!(set.len(), i); let (index, replaced) = set.replace_full(elt); assert!(replaced.is_none()); @@ -1607,7 +1606,7 @@ mod tests { let not_present = [1, 3, 6, 9, 10]; let mut set = IndexSet::with_capacity(insert.len()); - for (i, &elt) in enumerate(&insert) { + for (i, &elt) in insert.iter().enumerate() { assert_eq!(set.len(), i); set.insert(elt); assert_eq!(set.len(), i + 1); diff --git a/src/util.rs b/src/util.rs index 5388f470..a24dfafd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,17 +1,9 @@ -use core::iter::Enumerate; use core::ops::{Bound, Range, RangeBounds}; pub(crate) fn third(t: (A, B, C)) -> C { t.2 } -pub(crate) fn enumerate(iterable: I) -> Enumerate -where - I: IntoIterator, -{ - iterable.into_iter().enumerate() -} - pub(crate) fn simplify_range(range: R, len: usize) -> Range where R: RangeBounds, From 3d5e38ccd3e55410764c228282a2918b1b443c6f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 13:36:29 -0800 Subject: [PATCH 06/19] Convert the README to markdown (cherry picked from commit bdba2a65005bc22d414720c19ab9999ddfec236e) --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++ README.rst | 69 ------------------------------------------------------ 2 files changed, 55 insertions(+), 69 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 00000000..a227e23d --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# indexmap + +[![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) + +A pure-Rust hash table which preserves (in a limited sense) insertion order. + +This crate implements compact map and set data-structures, +where the iteration order of the keys is independent from their hash or +value. It preserves insertion order (except after removals), and it +allows lookup of entries by either hash table key or numerical index. + +Note: this crate was originally released under the name `ordermap`, +but it was renamed to `indexmap` to better reflect its features. + +# Background + +This was inspired by Python 3.6's new dict implementation (which remembers +the insertion order and is fast to iterate, and is compact in memory). + +Some of those features were translated to Rust, and some were not. The result +was indexmap, a hash table that has following properties: + +- Order is **independent of hash function** and hash values of keys. +- Fast to iterate. +- Indexed in compact space. +- Preserves insertion order **as long** as you don't call `.remove()`. +- Uses hashbrown for the inner table, just like Rust's libstd `HashMap` does. + +## Performance + +`IndexMap` derives a couple of performance facts directly from how it is constructed, +which is roughly: + +> A raw hash table of key-value indices, and a vector of key-value pairs. + +- Iteration is very fast since it is on the dense key-values. +- Removal is fast since it moves memory areas only in the table, + and uses a single swap in the vector. +- Lookup is fast-ish because the initial 7-bit hash lookup uses SIMD, and indices are + densely stored. Lookup also is slow-ish since the actual key-value pairs are stored + separately. (Visible when cpu caches size is limiting.) + +- In practice, `IndexMap` has been tested out as the hashmap in rustc in [PR45282] and + the performance was roughly on par across the whole workload. +- If you want the properties of `IndexMap`, or its strongest performance points + fits your workload, it might be the best hash table implementation. + +[PR45282]: https://github.com/rust-lang/rust/pull/45282 + +# Recent Changes + +See [RELEASES.rst](https://github.com/bluss/indexmap/blob/master/README.rst). diff --git a/README.rst b/README.rst deleted file mode 100644 index 4dac24f4..00000000 --- a/README.rst +++ /dev/null @@ -1,69 +0,0 @@ -indexmap -======== - -|build_status|_ |crates|_ |docs|_ |rustc|_ - -.. |build_status| image:: https://github.com/bluss/indexmap/workflows/Continuous%20integration/badge.svg?branch=master -.. _build_status: https://github.com/bluss/indexmap/actions - -.. |crates| image:: https://img.shields.io/crates/v/indexmap.svg -.. _crates: https://crates.io/crates/indexmap - -.. |docs| image:: https://docs.rs/indexmap/badge.svg -.. _docs: https://docs.rs/indexmap - -.. |rustc| image:: https://img.shields.io/badge/rust-1.56%2B-orange.svg -.. _rustc: https://img.shields.io/badge/rust-1.56%2B-orange.svg - -A pure-Rust hash table which preserves (in a limited sense) insertion order. - -This crate implements compact map and set data-structures, -where the iteration order of the keys is independent from their hash or -value. It preserves insertion order (except after removals), and it -allows lookup of entries by either hash table key or numerical index. - -Note: this crate was originally released under the name ``ordermap``, -but it was renamed to ``indexmap`` to better reflect its features. - -Background -========== - -This was inspired by Python 3.6's new dict implementation (which remembers -the insertion order and is fast to iterate, and is compact in memory). - -Some of those features were translated to Rust, and some were not. The result -was indexmap, a hash table that has following properties: - -- Order is **independent of hash function** and hash values of keys. -- Fast to iterate. -- Indexed in compact space. -- Preserves insertion order **as long** as you don't call ``.remove()``. -- Uses hashbrown for the inner table, just like Rust's libstd ``HashMap`` does. - -Performance ------------ - -``IndexMap`` derives a couple of performance facts directly from how it is constructed, -which is roughly: - - A raw hash table of key-value indices, and a vector of key-value pairs. - -- Iteration is very fast since it is on the dense key-values. -- Removal is fast since it moves memory areas only in the table, - and uses a single swap in the vector. -- Lookup is fast-ish because the initial 7-bit hash lookup uses SIMD, and indices are - densely stored. Lookup also is slow-ish since the actual key-value pairs are stored - separately. (Visible when cpu caches size is limiting.) - -- In practice, ``IndexMap`` has been tested out as the hashmap in rustc in PR45282_ and - the performance was roughly on par across the whole workload. -- If you want the properties of ``IndexMap``, or its strongest performance points - fits your workload, it might be the best hash table implementation. - -.. _PR45282: https://github.com/rust-lang/rust/pull/45282 - - -Recent Changes -============== - -See `RELEASES.rst <./RELEASES.rst>`_. From 9172c9f7655eb001d5c5196857253f17961641f3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 15:54:37 -0800 Subject: [PATCH 07/19] Convert the RELEASES doc to markdown (cherry picked from commit dec33378bbf8490ca30eefd40dc255d12763479f) --- README.md | 2 +- RELEASES.md | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++ RELEASES.rst | 358 --------------------------------------------------- 3 files changed, 359 insertions(+), 359 deletions(-) create mode 100644 RELEASES.md delete mode 100644 RELEASES.rst diff --git a/README.md b/README.md index a227e23d..5dc3f181 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,4 @@ which is roughly: # Recent Changes -See [RELEASES.rst](https://github.com/bluss/indexmap/blob/master/README.rst). +See [RELEASES.md](https://github.com/bluss/indexmap/blob/master/README.md). diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 00000000..21bb0a1b --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,358 @@ +- 1.8.2 + + - Bump the `rustc-rayon` dependency, for compiler use only. + +- 1.8.1 + + - The new `IndexSet::replace_full` will return the index of the item along + with the replaced value, if any, by @zakcutner in PR [222]. + +[222]: https://github.com/bluss/indexmap/pull/222 + +- 1.8.0 + + - The new `IndexMap::into_keys` and `IndexMap::into_values` will consume + the map into keys or values, respectively, matching Rust 1.54's `HashMap` + methods, by @taiki-e in PR [195]. + + - More of the iterator types implement `Debug`, `ExactSizeIterator`, and + `FusedIterator`, by @cuviper in PR [196]. + + - `IndexMap` and `IndexSet` now implement rayon's `ParallelDrainRange`, + by @cuviper in PR [197]. + + - `IndexMap::with_hasher` and `IndexSet::with_hasher` are now `const` + functions, allowing static maps and sets, by @mwillsey in PR [203]. + + - `IndexMap` and `IndexSet` now implement `From` for arrays, matching + Rust 1.56's implementation for `HashMap`, by @rouge8 in PR [205]. + + - `IndexMap` and `IndexSet` now have methods `sort_unstable_keys`, + `sort_unstable_by`, `sorted_unstable_by`, and `par_*` equivalents, + which sort in-place without preserving the order of equal items, by + @bhgomes in PR [211]. + +[195]: https://github.com/bluss/indexmap/pull/195 +[196]: https://github.com/bluss/indexmap/pull/196 +[197]: https://github.com/bluss/indexmap/pull/197 +[203]: https://github.com/bluss/indexmap/pull/203 +[205]: https://github.com/bluss/indexmap/pull/205 +[211]: https://github.com/bluss/indexmap/pull/211 + +- 1.7.0 + + - **MSRV**: Rust 1.49 or later is now required. + + - The `hashbrown` dependency has been updated to version 0.11. + +- 1.6.2 + + - Fixed to match `std` behavior, `OccupiedEntry::key` now references the + existing key in the map instead of the lookup key, by @cuviper in PR [170]. + + - The new `Entry::or_insert_with_key` matches Rust 1.50's `Entry` method, + passing `&K` to the callback to create a value, by @cuviper in PR [175]. + +[170]: https://github.com/bluss/indexmap/pull/170 +[175]: https://github.com/bluss/indexmap/pull/175 + +- 1.6.1 + + - The new `serde_seq` module implements `IndexMap` serialization as a + sequence to ensure order is preserved, by @cuviper in PR [158]. + + - New methods on maps and sets work like the `Vec`/slice methods by the same name: + `truncate`, `split_off`, `first`, `first_mut`, `last`, `last_mut`, and + `swap_indices`, by @cuviper in PR [160]. + +[158]: https://github.com/bluss/indexmap/pull/158 +[160]: https://github.com/bluss/indexmap/pull/160 + +- 1.6.0 + + - **MSRV**: Rust 1.36 or later is now required. + + - The `hashbrown` dependency has been updated to version 0.9. + +- 1.5.2 + + - The new "std" feature will force the use of `std` for users that explicitly + want the default `S = RandomState`, bypassing the autodetection added in 1.3.0, + by @cuviper in PR [145]. + +[145]: https://github.com/bluss/indexmap/pull/145 + +- 1.5.1 + + - Values can now be indexed by their `usize` position by @cuviper in PR [132]. + + - Some of the generic bounds have been relaxed to match `std` by @cuviper in PR [141]. + + - `drain` now accepts any `R: RangeBounds` by @cuviper in PR [142]. + +[132]: https://github.com/bluss/indexmap/pull/132 +[141]: https://github.com/bluss/indexmap/pull/141 +[142]: https://github.com/bluss/indexmap/pull/142 + +- 1.5.0 + + - **MSRV**: Rust 1.32 or later is now required. + + - The inner hash table is now based on `hashbrown` by @cuviper in PR [131]. + This also completes the method `reserve` and adds `shrink_to_fit`. + + - Add new methods `get_key_value`, `remove_entry`, `swap_remove_entry`, + and `shift_remove_entry`, by @cuviper in PR [136] + + - `Clone::clone_from` reuses allocations by @cuviper in PR [125] + + - Add new method `reverse` by @linclelinkpart5 in PR [128] + +[125]: https://github.com/bluss/indexmap/pull/125 +[128]: https://github.com/bluss/indexmap/pull/128 +[131]: https://github.com/bluss/indexmap/pull/131 +[136]: https://github.com/bluss/indexmap/pull/136 + +- 1.4.0 + + - Add new method `get_index_of` by @Thermatrix in PR [115] and [120] + + - Fix build script rebuild-if-changed configuration to use "build.rs"; + fixes issue [123]. Fix by @cuviper. + + - Dev-dependencies (rand and quickcheck) have been updated. The crate's tests + now run using Rust 1.32 or later (MSRV for building the crate has not changed). + by @kjeremy and @bluss + +[123]: https://github.com/bluss/indexmap/issues/123 +[115]: https://github.com/bluss/indexmap/pull/115 +[120]: https://github.com/bluss/indexmap/pull/120 + +- 1.3.2 + + - Maintenance update to regenerate the published `Cargo.toml`. + +- 1.3.1 + + - Maintenance update for formatting and `autocfg` 1.0. + +- 1.3.0 + + - The deprecation messages in the previous version have been removed. + (The methods have not otherwise changed.) Docs for removal methods have been + improved. + - From Rust 1.36, this crate supports being built **without std**, requiring + `alloc` instead. This is enabled automatically when it is detected that + `std` is not available. There is no crate feature to enable/disable to + trigger this. The new build-dep `autocfg` enables this. + +- 1.2.0 + + - Plain `.remove()` now has a deprecation message, it informs the user + about picking one of the removal functions `swap_remove` and `shift_remove` + which have different performance and order semantics. + Plain `.remove()` will not be removed, the warning message and method + will remain until further. + + - Add new method `shift_remove` for order preserving removal on the map, + and `shift_take` for the corresponding operation on the set. + + - Add methods `swap_remove`, `swap_remove_entry` to `Entry`. + + - Fix indexset/indexmap to support full paths, like `indexmap::indexmap!()` + + - Internal improvements: fix warnings, deprecations and style lints + +- 1.1.0 + + - Added optional feature `"rayon"` that adds parallel iterator support + to `IndexMap` and `IndexSet` using Rayon. This includes all the regular + iterators in parallel versions, and parallel sort. + + - Implemented `Clone` for `map::{Iter, Keys, Values}` and + `set::{Difference, Intersection, Iter, SymmetricDifference, Union}` + + - Implemented `Debug` for `map::{Entry, IntoIter, Iter, Keys, Values}` and + `set::{Difference, Intersection, IntoIter, Iter, SymmetricDifference, Union}` + + - Serde trait `IntoDeserializer` are implemented for `IndexMap` and `IndexSet`. + + - Minimum Rust version requirement increased to Rust 1.30 for development builds. + +- 1.0.2 + + - The new methods `IndexMap::insert_full` and `IndexSet::insert_full` are + both like `insert` with the index included in the return value. + + - The new method `Entry::and_modify` can be used to modify occupied + entries, matching the new methods of `std` maps in Rust 1.26. + + - The new method `Entry::or_default` inserts a default value in unoccupied + entries, matching the new methods of `std` maps in Rust 1.28. + +- 1.0.1 + + - Document Rust version policy for the crate (see rustdoc) + +- 1.0.0 + + - This is the 1.0 release for `indexmap`! (the crate and datastructure + formerly known as “ordermap”) + - `OccupiedEntry::insert` changed its signature, to use `&mut self` for + the method receiver, matching the equivalent method for a standard + `HashMap`. Thanks to @dtolnay for finding this bug. + - The deprecated old names from ordermap were removed: `OrderMap`, + `OrderSet`, `ordermap!{}`, `orderset!{}`. Use the new `IndexMap` + etc names instead. + +- 0.4.1 + + - Renamed crate to `indexmap`; the `ordermap` crate is now deprecated + and the types `OrderMap/Set` now have a deprecation notice. + +- 0.4.0 + + - This is the last release series for this `ordermap` under that name, + because the crate is **going to be renamed** to `indexmap` (with types + `IndexMap`, `IndexSet`) and no change in functionality! + - The map and its associated structs moved into the `map` submodule of the + crate, so that the map and set are symmetric + + + The iterators, `Entry` and other structs are now under `ordermap::map::` + + - Internally refactored `OrderMap` so that all the main algorithms + (insertion, lookup, removal etc) that don't use the `S` parameter (the + hasher) are compiled without depending on `S`, which reduces generics bloat. + + - `Entry` no longer has a type parameter `S`, which is just like + the standard `HashMap`'s entry. + + - Minimum Rust version requirement increased to Rust 1.18 + +- 0.3.5 + + - Documentation improvements + +- 0.3.4 + + - The `.retain()` methods for `OrderMap` and `OrderSet` now + traverse the elements in order, and the retained elements **keep their order** + - Added new methods `.sort_by()`, `.sort_keys()` to `OrderMap` and + `.sort_by()`, `.sort()` to `OrderSet`. These methods allow you to + sort the maps in place efficiently. + +- 0.3.3 + + - Document insertion behaviour better by @lucab + - Updated dependences (no feature changes) by @ignatenkobrain + +- 0.3.2 + + - Add `OrderSet` by @cuviper! + - `OrderMap::drain` is now (too) a double ended iterator. + +- 0.3.1 + + - In all ordermap iterators, forward the `collect` method to the underlying + iterator as well. + - Add crates.io categories. + +- 0.3.0 + + - The methods `get_pair`, `get_pair_index` were both replaced by + `get_full` (and the same for the mutable case). + - Method `swap_remove_pair` replaced by `swap_remove_full`. + - Add trait `MutableKeys` for opt-in mutable key access. Mutable key access + is only possible through the methods of this extension trait. + - Add new trait `Equivalent` for key equivalence. This extends the + `Borrow` trait mechanism for `OrderMap::get` in a backwards compatible + way, just some minor type inference related issues may become apparent. + See [#10] for more information. + - Implement `Extend<(&K, &V)>` by @xfix. + +[#10]: https://github.com/bluss/ordermap/pull/10 + +- 0.2.13 + + - Fix deserialization to support custom hashers by @Techcable. + - Add methods `.index()` on the entry types by @garro95. + +- 0.2.12 + + - Add methods `.with_hasher()`, `.hasher()`. + +- 0.2.11 + + - Support `ExactSizeIterator` for the iterators. By @Binero. + - Use `Box<[Pos]>` internally, saving a word in the `OrderMap` struct. + - Serde support, with crate feature `"serde-1"`. By @xfix. + +- 0.2.10 + + - Add iterator `.drain(..)` by @stevej. + +- 0.2.9 + + - Add method `.is_empty()` by @overvenus. + - Implement `PartialEq, Eq` by @overvenus. + - Add method `.sorted_by()`. + +- 0.2.8 + + - Add iterators `.values()` and `.values_mut()`. + - Fix compatibility with 32-bit platforms. + +- 0.2.7 + + - Add `.retain()`. + +- 0.2.6 + + - Add `OccupiedEntry::remove_entry` and other minor entry methods, + so that it now has all the features of `HashMap`'s entries. + +- 0.2.5 + + - Improved `.pop()` slightly. + +- 0.2.4 + + - Improved performance of `.insert()` ([#3]) by @pczarn. + +[#3]: https://github.com/bluss/ordermap/pull/3 + +- 0.2.3 + + - Generalize `Entry` for now, so that it works on hashmaps with non-default + hasher. However, there's a lingering compat issue since libstd `HashMap` + does not parameterize its entries by the hasher (`S` typarm). + - Special case some iterator methods like `.nth()`. + +- 0.2.2 + + - Disable the verbose `Debug` impl by default. + +- 0.2.1 + + - Fix doc links and clarify docs. + +- 0.2.0 + + - Add more `HashMap` methods & compat with its API. + - Experimental support for `.entry()` (the simplest parts of the API). + - Add `.reserve()` (placeholder impl). + - Add `.remove()` as synonym for `.swap_remove()`. + - Changed `.insert()` to swap value if the entry already exists, and + return `Option`. + - Experimental support as an *indexed* hash map! Added methods + `.get_index()`, `.get_index_mut()`, `.swap_remove_index()`, + `.get_pair_index()`, `.get_pair_index_mut()`. + +- 0.1.2 + + - Implement the 32/32 split idea for `Pos` which improves cache utilization + and lookup performance. + +- 0.1.1 + + - Initial release. diff --git a/RELEASES.rst b/RELEASES.rst deleted file mode 100644 index 6e0ae7e4..00000000 --- a/RELEASES.rst +++ /dev/null @@ -1,358 +0,0 @@ -- 1.8.2 - - - Bump the ``rustc-rayon`` dependency, for compiler use only. - -- 1.8.1 - - - The new ``IndexSet::replace_full`` will return the index of the item along - with the replaced value, if any, by @zakcutner in PR 222_. - -.. _222: https://github.com/bluss/indexmap/pull/222 - -- 1.8.0 - - - The new ``IndexMap::into_keys`` and ``IndexMap::into_values`` will consume - the map into keys or values, respectively, matching Rust 1.54's ``HashMap`` - methods, by @taiki-e in PR 195_. - - - More of the iterator types implement ``Debug``, ``ExactSizeIterator``, and - ``FusedIterator``, by @cuviper in PR 196_. - - - ``IndexMap`` and ``IndexSet`` now implement rayon's ``ParallelDrainRange``, - by @cuviper in PR 197_. - - - ``IndexMap::with_hasher`` and ``IndexSet::with_hasher`` are now ``const`` - functions, allowing static maps and sets, by @mwillsey in PR 203_. - - - ``IndexMap`` and ``IndexSet`` now implement ``From`` for arrays, matching - Rust 1.56's implementation for ``HashMap``, by @rouge8 in PR 205_. - - - ``IndexMap`` and ``IndexSet`` now have methods ``sort_unstable_keys``, - ``sort_unstable_by``, ``sorted_unstable_by``, and ``par_*`` equivalents, - which sort in-place without preserving the order of equal items, by - @bhgomes in PR 211_. - -.. _195: https://github.com/bluss/indexmap/pull/195 -.. _196: https://github.com/bluss/indexmap/pull/196 -.. _197: https://github.com/bluss/indexmap/pull/197 -.. _203: https://github.com/bluss/indexmap/pull/203 -.. _205: https://github.com/bluss/indexmap/pull/205 -.. _211: https://github.com/bluss/indexmap/pull/211 - -- 1.7.0 - - - **MSRV**: Rust 1.49 or later is now required. - - - The ``hashbrown`` dependency has been updated to version 0.11. - -- 1.6.2 - - - Fixed to match ``std`` behavior, ``OccupiedEntry::key`` now references the - existing key in the map instead of the lookup key, by @cuviper in PR 170_. - - - The new ``Entry::or_insert_with_key`` matches Rust 1.50's ``Entry`` method, - passing ``&K`` to the callback to create a value, by @cuviper in PR 175_. - -.. _170: https://github.com/bluss/indexmap/pull/170 -.. _175: https://github.com/bluss/indexmap/pull/175 - -- 1.6.1 - - - The new ``serde_seq`` module implements ``IndexMap`` serialization as a - sequence to ensure order is preserved, by @cuviper in PR 158_. - - - New methods on maps and sets work like the ``Vec``/slice methods by the same name: - ``truncate``, ``split_off``, ``first``, ``first_mut``, ``last``, ``last_mut``, and - ``swap_indices``, by @cuviper in PR 160_. - -.. _158: https://github.com/bluss/indexmap/pull/158 -.. _160: https://github.com/bluss/indexmap/pull/160 - -- 1.6.0 - - - **MSRV**: Rust 1.36 or later is now required. - - - The ``hashbrown`` dependency has been updated to version 0.9. - -- 1.5.2 - - - The new "std" feature will force the use of ``std`` for users that explicitly - want the default ``S = RandomState``, bypassing the autodetection added in 1.3.0, - by @cuviper in PR 145_. - -.. _145: https://github.com/bluss/indexmap/pull/145 - -- 1.5.1 - - - Values can now be indexed by their ``usize`` position by @cuviper in PR 132_. - - - Some of the generic bounds have been relaxed to match ``std`` by @cuviper in PR 141_. - - - ``drain`` now accepts any ``R: RangeBounds`` by @cuviper in PR 142_. - -.. _132: https://github.com/bluss/indexmap/pull/132 -.. _141: https://github.com/bluss/indexmap/pull/141 -.. _142: https://github.com/bluss/indexmap/pull/142 - -- 1.5.0 - - - **MSRV**: Rust 1.32 or later is now required. - - - The inner hash table is now based on ``hashbrown`` by @cuviper in PR 131_. - This also completes the method ``reserve`` and adds ``shrink_to_fit``. - - - Add new methods ``get_key_value``, ``remove_entry``, ``swap_remove_entry``, - and ``shift_remove_entry``, by @cuviper in PR 136_ - - - ``Clone::clone_from`` reuses allocations by @cuviper in PR 125_ - - - Add new method ``reverse`` by @linclelinkpart5 in PR 128_ - -.. _125: https://github.com/bluss/indexmap/pull/125 -.. _128: https://github.com/bluss/indexmap/pull/128 -.. _131: https://github.com/bluss/indexmap/pull/131 -.. _136: https://github.com/bluss/indexmap/pull/136 - -- 1.4.0 - - - Add new method ``get_index_of`` by @Thermatrix in PR 115_ and 120_ - - - Fix build script rebuild-if-changed configuration to use "build.rs"; - fixes issue 123_. Fix by @cuviper. - - - Dev-dependencies (rand and quickcheck) have been updated. The crate's tests - now run using Rust 1.32 or later (MSRV for building the crate has not changed). - by @kjeremy and @bluss - -.. _123: https://github.com/bluss/indexmap/issues/123 -.. _115: https://github.com/bluss/indexmap/pull/115 -.. _120: https://github.com/bluss/indexmap/pull/120 - -- 1.3.2 - - - Maintenance update to regenerate the published `Cargo.toml`. - -- 1.3.1 - - - Maintenance update for formatting and ``autocfg`` 1.0. - -- 1.3.0 - - - The deprecation messages in the previous version have been removed. - (The methods have not otherwise changed.) Docs for removal methods have been - improved. - - From Rust 1.36, this crate supports being built **without std**, requiring - ``alloc`` instead. This is enabled automatically when it is detected that - ``std`` is not available. There is no crate feature to enable/disable to - trigger this. The new build-dep ``autocfg`` enables this. - -- 1.2.0 - - - Plain ``.remove()`` now has a deprecation message, it informs the user - about picking one of the removal functions ``swap_remove`` and ``shift_remove`` - which have different performance and order semantics. - Plain ``.remove()`` will not be removed, the warning message and method - will remain until further. - - - Add new method ``shift_remove`` for order preserving removal on the map, - and ``shift_take`` for the corresponding operation on the set. - - - Add methods ``swap_remove``, ``swap_remove_entry`` to ``Entry``. - - - Fix indexset/indexmap to support full paths, like ``indexmap::indexmap!()`` - - - Internal improvements: fix warnings, deprecations and style lints - -- 1.1.0 - - - Added optional feature `"rayon"` that adds parallel iterator support - to `IndexMap` and `IndexSet` using Rayon. This includes all the regular - iterators in parallel versions, and parallel sort. - - - Implemented ``Clone`` for ``map::{Iter, Keys, Values}`` and - ``set::{Difference, Intersection, Iter, SymmetricDifference, Union}`` - - - Implemented ``Debug`` for ``map::{Entry, IntoIter, Iter, Keys, Values}`` and - ``set::{Difference, Intersection, IntoIter, Iter, SymmetricDifference, Union}`` - - - Serde trait ``IntoDeserializer`` are implemented for ``IndexMap`` and ``IndexSet``. - - - Minimum Rust version requirement increased to Rust 1.30 for development builds. - -- 1.0.2 - - - The new methods ``IndexMap::insert_full`` and ``IndexSet::insert_full`` are - both like ``insert`` with the index included in the return value. - - - The new method ``Entry::and_modify`` can be used to modify occupied - entries, matching the new methods of ``std`` maps in Rust 1.26. - - - The new method ``Entry::or_default`` inserts a default value in unoccupied - entries, matching the new methods of ``std`` maps in Rust 1.28. - -- 1.0.1 - - - Document Rust version policy for the crate (see rustdoc) - -- 1.0.0 - - - This is the 1.0 release for ``indexmap``! (the crate and datastructure - formerly known as “ordermap”) - - ``OccupiedEntry::insert`` changed its signature, to use ``&mut self`` for - the method receiver, matching the equivalent method for a standard - ``HashMap``. Thanks to @dtolnay for finding this bug. - - The deprecated old names from ordermap were removed: ``OrderMap``, - ``OrderSet``, ``ordermap!{}``, ``orderset!{}``. Use the new ``IndexMap`` - etc names instead. - -- 0.4.1 - - - Renamed crate to ``indexmap``; the ``ordermap`` crate is now deprecated - and the types ``OrderMap/Set`` now have a deprecation notice. - -- 0.4.0 - - - This is the last release series for this ``ordermap`` under that name, - because the crate is **going to be renamed** to ``indexmap`` (with types - ``IndexMap``, ``IndexSet``) and no change in functionality! - - The map and its associated structs moved into the ``map`` submodule of the - crate, so that the map and set are symmetric - - + The iterators, ``Entry`` and other structs are now under ``ordermap::map::`` - - - Internally refactored ``OrderMap`` so that all the main algorithms - (insertion, lookup, removal etc) that don't use the ``S`` parameter (the - hasher) are compiled without depending on ``S``, which reduces generics bloat. - - - ``Entry`` no longer has a type parameter ``S``, which is just like - the standard ``HashMap``'s entry. - - - Minimum Rust version requirement increased to Rust 1.18 - -- 0.3.5 - - - Documentation improvements - -- 0.3.4 - - - The ``.retain()`` methods for ``OrderMap`` and ``OrderSet`` now - traverse the elements in order, and the retained elements **keep their order** - - Added new methods ``.sort_by()``, ``.sort_keys()`` to ``OrderMap`` and - ``.sort_by()``, ``.sort()`` to ``OrderSet``. These methods allow you to - sort the maps in place efficiently. - -- 0.3.3 - - - Document insertion behaviour better by @lucab - - Updated dependences (no feature changes) by @ignatenkobrain - -- 0.3.2 - - - Add ``OrderSet`` by @cuviper! - - ``OrderMap::drain`` is now (too) a double ended iterator. - -- 0.3.1 - - - In all ordermap iterators, forward the ``collect`` method to the underlying - iterator as well. - - Add crates.io categories. - -- 0.3.0 - - - The methods ``get_pair``, ``get_pair_index`` were both replaced by - ``get_full`` (and the same for the mutable case). - - Method ``swap_remove_pair`` replaced by ``swap_remove_full``. - - Add trait ``MutableKeys`` for opt-in mutable key access. Mutable key access - is only possible through the methods of this extension trait. - - Add new trait ``Equivalent`` for key equivalence. This extends the - ``Borrow`` trait mechanism for ``OrderMap::get`` in a backwards compatible - way, just some minor type inference related issues may become apparent. - See `#10`__ for more information. - - Implement ``Extend<(&K, &V)>`` by @xfix. - -__ https://github.com/bluss/ordermap/pull/10 - -- 0.2.13 - - - Fix deserialization to support custom hashers by @Techcable. - - Add methods ``.index()`` on the entry types by @garro95. - -- 0.2.12 - - - Add methods ``.with_hasher()``, ``.hasher()``. - -- 0.2.11 - - - Support ``ExactSizeIterator`` for the iterators. By @Binero. - - Use ``Box<[Pos]>`` internally, saving a word in the ``OrderMap`` struct. - - Serde support, with crate feature ``"serde-1"``. By @xfix. - -- 0.2.10 - - - Add iterator ``.drain(..)`` by @stevej. - -- 0.2.9 - - - Add method ``.is_empty()`` by @overvenus. - - Implement ``PartialEq, Eq`` by @overvenus. - - Add method ``.sorted_by()``. - -- 0.2.8 - - - Add iterators ``.values()`` and ``.values_mut()``. - - Fix compatibility with 32-bit platforms. - -- 0.2.7 - - - Add ``.retain()``. - -- 0.2.6 - - - Add ``OccupiedEntry::remove_entry`` and other minor entry methods, - so that it now has all the features of ``HashMap``'s entries. - -- 0.2.5 - - - Improved ``.pop()`` slightly. - -- 0.2.4 - - - Improved performance of ``.insert()`` (`#3`__) by @pczarn. - -__ https://github.com/bluss/ordermap/pull/3 - -- 0.2.3 - - - Generalize ``Entry`` for now, so that it works on hashmaps with non-default - hasher. However, there's a lingering compat issue since libstd ``HashMap`` - does not parameterize its entries by the hasher (``S`` typarm). - - Special case some iterator methods like ``.nth()``. - -- 0.2.2 - - - Disable the verbose ``Debug`` impl by default. - -- 0.2.1 - - - Fix doc links and clarify docs. - -- 0.2.0 - - - Add more ``HashMap`` methods & compat with its API. - - Experimental support for ``.entry()`` (the simplest parts of the API). - - Add ``.reserve()`` (placeholder impl). - - Add ``.remove()`` as synonym for ``.swap_remove()``. - - Changed ``.insert()`` to swap value if the entry already exists, and - return ``Option``. - - Experimental support as an *indexed* hash map! Added methods - ``.get_index()``, ``.get_index_mut()``, ``.swap_remove_index()``, - ``.get_pair_index()``, ``.get_pair_index_mut()``. - -- 0.1.2 - - - Implement the 32/32 split idea for ``Pos`` which improves cache utilization - and lookup performance. - -- 0.1.1 - - - Initial release. From 1118babde2e9006b868f4d88e3a66a71331e761e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 16:14:11 -0800 Subject: [PATCH 08/19] Add preliminary release notes for 1.9.0 (cherry picked from commit b41fc6b5ddd3a265d5b3daac947209cbbc0d025b) --- RELEASES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 21bb0a1b..d556b677 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,9 @@ +- 1.9.0 + + - **MSRV**: Rust 1.56 or later is now required. + + - The `hashbrown` dependency has been updated to version 0.12. + - 1.8.2 - Bump the `rustc-rayon` dependency, for compiler use only. From 74b4312a32b98b68a59c9401ba92ea65246c57dc Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 17:14:59 -0800 Subject: [PATCH 09/19] impl Debug for IterMut and ValuesMut (cherry picked from commit 4ddce3a976c1bc88a6373391da542729c4bb1815) --- RELEASES.md | 2 ++ src/map.rs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index d556b677..1c1bf069 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,8 @@ - The `hashbrown` dependency has been updated to version 0.12. + - `IterMut` and `ValuesMut` now implement `Debug`. + - 1.8.2 - Bump the `rustc-rayon` dependency, for compiler use only. diff --git a/src/map.rs b/src/map.rs index 9e4d1113..4a24f04a 100644 --- a/src/map.rs +++ b/src/map.rs @@ -999,7 +999,12 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { impl FusedIterator for ValuesMut<'_, K, V> {} -// TODO: `impl Debug for ValuesMut` once we have MSRV 1.53 for `slice::IterMut::as_slice` +impl fmt::Debug for ValuesMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::value_ref); + f.debug_list().entries(iter).finish() + } +} /// An owning iterator over the values of a `IndexMap`. /// @@ -1110,7 +1115,12 @@ impl ExactSizeIterator for IterMut<'_, K, V> { impl FusedIterator for IterMut<'_, K, V> {} -// TODO: `impl Debug for IterMut` once we have MSRV 1.53 for `slice::IterMut::as_slice` +impl fmt::Debug for IterMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::refs); + f.debug_list().entries(iter).finish() + } +} /// An owning iterator over the entries of a `IndexMap`. /// From b1a5b7bb3230a7d257e26a2ff0d36df098f869f6 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 8 Feb 2022 17:42:03 -0800 Subject: [PATCH 10/19] Add shrink_to (cherry picked from commit 94d11970208e8937e9bd9187b4863706a81e19df) --- RELEASES.md | 3 +++ src/map.rs | 9 ++++++++- src/map/core.rs | 8 ++++---- src/set.rs | 7 +++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 1c1bf069..2aa3528c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,9 @@ - `IterMut` and `ValuesMut` now implement `Debug`. + - The new `IndexMap::shrink_to` and `IndexSet::shrink_to` methods shrink + the capacity with a lower bound. + - 1.8.2 - Bump the `rustc-rayon` dependency, for compiler use only. diff --git a/src/map.rs b/src/map.rs index 4a24f04a..0bf9a36c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -332,7 +332,14 @@ where /// /// Computes in **O(n)** time. pub fn shrink_to_fit(&mut self) { - self.core.shrink_to_fit(); + self.core.shrink_to(0); + } + + /// Shrink the capacity of the map with a lower limit. + /// + /// Computes in **O(n)** time. + pub fn shrink_to(&mut self, min_capacity: usize) { + self.core.shrink_to(min_capacity); } fn hash(&self, key: &Q) -> HashValue { diff --git a/src/map/core.rs b/src/map/core.rs index 0ed9b1ff..6479135d 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -201,10 +201,10 @@ impl IndexMapCore { self.entries.reserve_exact(additional); } - /// Shrink the capacity of the map as much as possible. - pub(crate) fn shrink_to_fit(&mut self) { - self.indices.shrink_to(0, get_hash(&self.entries)); - self.entries.shrink_to_fit(); + /// Shrink the capacity of the map with a lower bound + pub(crate) fn shrink_to(&mut self, min_capacity: usize) { + self.indices.shrink_to(min_capacity, get_hash(&self.entries)); + self.entries.shrink_to(min_capacity); } /// Remove the last key-value pair diff --git a/src/set.rs b/src/set.rs index ca475530..59d1d420 100644 --- a/src/set.rs +++ b/src/set.rs @@ -268,6 +268,13 @@ where self.map.shrink_to_fit(); } + /// Shrink the capacity of the set with a lower limit. + /// + /// Computes in **O(n)** time. + pub fn shrink_to(&mut self, min_capacity: usize) { + self.map.shrink_to(min_capacity); + } + /// Insert the value into the set. /// /// If an equivalent item already exists in the set, it returns From 3921adfa947cba452509703e1662b5e2ee8eeea2 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 29 Mar 2022 16:16:29 -0700 Subject: [PATCH 11/19] Fix clippy::needless_borrow (cherry picked from commit c71567597858e1b186479ad67247fe2d810eaabe) --- src/rayon/set.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rayon/set.rs b/src/rayon/set.rs index 5a4ac97d..6749dc0d 100644 --- a/src/rayon/set.rs +++ b/src/rayon/set.rs @@ -295,7 +295,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() - .entries(self.set1.difference(&self.set2)) + .entries(self.set1.difference(self.set2)) .finish() } } @@ -346,7 +346,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() - .entries(self.set1.intersection(&self.set2)) + .entries(self.set1.intersection(self.set2)) .finish() } } @@ -397,7 +397,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() - .entries(self.set1.symmetric_difference(&self.set2)) + .entries(self.set1.symmetric_difference(self.set2)) .finish() } } @@ -447,7 +447,7 @@ where S2: BuildHasher, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.set1.union(&self.set2)).finish() + f.debug_list().entries(self.set1.union(self.set2)).finish() } } From 20cf99ad66d23013aa705d87268125159f5a5e7b Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Fri, 15 Apr 2022 23:19:49 +0100 Subject: [PATCH 12/19] Documentation assertion in erase_index function (cherry picked from commit 4750dc7dfc28300a37cc8b2a2c6d536fb8b85ecd) --- src/map/core.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/core.rs b/src/map/core.rs index 6479135d..50ebebea 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -44,7 +44,8 @@ fn equivalent<'a, K, V, Q: ?Sized + Equivalent>( #[inline] fn erase_index(table: &mut RawTable, hash: HashValue, index: usize) { - table.erase_entry(hash.get(), move |&i| i == index); + let erased = table.erase_entry(hash.get(), move |&i| i == index); + debug_assert!(erased); } #[inline] From 971f8b92af0794b69d1089a5bed600073c92ef78 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 Apr 2022 16:50:44 -0700 Subject: [PATCH 13/19] Hide unnecessary iterator visibility (cherry picked from commit 9f2b14d678eff8e8d317a758ede7df7705770749) --- src/map.rs | 4 ++-- src/set.rs | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/map.rs b/src/map.rs index 0bf9a36c..8923d3a8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -861,7 +861,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> { @@ -1137,7 +1137,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 59d1d420..64671b10 100644 --- a/src/set.rs +++ b/src/set.rs @@ -192,7 +192,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(), } } @@ -602,8 +602,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(), } } @@ -633,11 +635,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(), } } @@ -877,7 +878,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 855b8eefcdfdc077bfc3b96fd70c56fd26e57c89 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 May 2022 18:09:53 -0700 Subject: [PATCH 14/19] Bump MSRV to 1.56.1, matching hashbrown as of 0.12.1 (cherry picked from commit 8a571c6d68cb38c283d563ff6972613e0eea4111) --- .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 8c6c10cb..4cc1a76a 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 @@ -59,7 +59,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 b75c9a5d..2b01f9d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,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 2aa3528c..c4d05f96 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ - 1.9.0 - - **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 ba613342..cbca4b0b 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 1.x release series will use a carefully considered version //! upgrade policy, where in a later 1.x version, we will raise the minimum From 75de9993cd32dfb95b2805f7dc93a43e0d1157f4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 May 2022 18:30:56 -0700 Subject: [PATCH 15/19] cargo fmt (cherry picked from commit cda1a0b5c947330ece313fe3ab2cc1fe906f1ed4) --- src/map/core.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/core.rs b/src/map/core.rs index 50ebebea..0bc54621 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -204,7 +204,8 @@ impl IndexMapCore { /// Shrink the capacity of the map with a lower bound pub(crate) fn shrink_to(&mut self, min_capacity: usize) { - self.indices.shrink_to(min_capacity, get_hash(&self.entries)); + self.indices + .shrink_to(min_capacity, get_hash(&self.entries)); self.entries.shrink_to(min_capacity); } From 511280aa2ea37844e8ea6f9cfd9b192e0feae6f1 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 19 May 2022 15:38:42 -0700 Subject: [PATCH 16/19] Add an MSRV note for retain_mut (cherry picked from commit 6b425e4e216634a0475ada0e17a56fa7c050d63f) --- src/map/core.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/core.rs b/src/map/core.rs index 0bc54621..b74e6f35 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -399,6 +399,7 @@ impl IndexMapCore { where F: FnMut(&mut K, &mut V) -> bool, { + // FIXME: This could use Vec::retain_mut with MSRV 1.61. // Like Vec::retain in self.entries, but with mutable K and V. // We swap-shift all the items we want to keep, truncate the rest, // then rebuild the raw hash table with the new indexes. From be700efe0b84ee4e5b93646620aa2d9f049e101c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 27 Feb 2021 11:36:53 -0800 Subject: [PATCH 17/19] Add move_index to change the position of an entry This moves the position of a key-value pair from one index to another by shifting all other pairs in-between, making this an O(n) operation. This could be used as a building-block for other operations, like #173 which wants to insert at a particular index. You can `insert_full` to insert it _somewhere_, then choose whether to `move_index` depending on whether you want to also change pre-existing entries. (cherry picked from commit 54a48d2dd1647befc760ab05d807639f79375d97) --- src/map.rs | 13 +++++++++ src/map/core.rs | 72 ++++++++++++++++++++++++++++++++++++++++--------- src/set.rs | 13 +++++++++ tests/quick.rs | 39 +++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/map.rs b/src/map.rs index 8923d3a8..d39448d0 100644 --- a/src/map.rs +++ b/src/map.rs @@ -845,6 +845,19 @@ impl IndexMap { self.core.shift_remove_index(index) } + /// Moves the position of a key-value pair from one index to another + /// by shifting all other pairs in-between. + /// + /// * If `from < to`, the other pairs will shift down while the targeted pair moves up. + /// * If `from > to`, the other pairs will shift up while the targeted pair moves down. + /// + /// ***Panics*** if `from` or `to` are out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn move_index(&mut self, from: usize, to: usize) { + self.core.move_index(from, to) + } + /// Swaps the position of two key-value pairs in the map. /// /// ***Panics*** if `a` or `b` are out of bounds. diff --git a/src/map/core.rs b/src/map/core.rs index b74e6f35..ea7aaae6 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -283,29 +283,77 @@ impl IndexMapCore { /// /// The index should already be removed from `self.indices`. fn shift_remove_finish(&mut self, index: usize) -> (K, V) { - // use Vec::remove, but then we need to update the indices that point - // to all of the other entries that have to move + // Correct indices that point to the entries that followed the removed entry. + self.decrement_indices(index + 1, self.entries.len()); + + // Use Vec::remove to actually remove the entry. let entry = self.entries.remove(index); + (entry.key, entry.value) + } - // correct indices that point to the entries that followed the removed entry. - // use a heuristic between a full sweep vs. a `find()` for every shifted item. - let raw_capacity = self.indices.buckets(); - let shifted_entries = &self.entries[index..]; - if shifted_entries.len() > raw_capacity / 2 { - // shift all indices greater than `index` + /// Decrement all indices in the range `start..end`. + /// + /// The index `start - 1` should not exist in `self.indices`. + /// All entries should still be in their original positions. + fn decrement_indices(&mut self, start: usize, end: usize) { + // Use a heuristic between a full sweep vs. a `find()` for every shifted item. + let shifted_entries = &self.entries[start..end]; + if shifted_entries.len() > self.indices.buckets() / 2 { + // Shift all indices in range. for i in self.indices_mut() { - if *i > index { + if start <= *i && *i < end { *i -= 1; } } } else { - // find each following entry to shift its index - for (i, entry) in (index + 1..).zip(shifted_entries) { + // Find each entry in range to shift its index. + for (i, entry) in (start..end).zip(shifted_entries) { update_index(&mut self.indices, entry.hash, i, i - 1); } } + } - (entry.key, entry.value) + /// Increment all indices in the range `start..end`. + /// + /// The index `end` should not exist in `self.indices`. + /// All entries should still be in their original positions. + fn increment_indices(&mut self, start: usize, end: usize) { + // Use a heuristic between a full sweep vs. a `find()` for every shifted item. + let shifted_entries = &self.entries[start..end]; + if shifted_entries.len() > self.indices.buckets() / 2 { + // Shift all indices in range. + for i in self.indices_mut() { + if start <= *i && *i < end { + *i += 1; + } + } + } else { + // Find each entry in range to shift its index, updated in reverse so + // we never have duplicated indices that might have a hash collision. + for (i, entry) in (start..end).zip(shifted_entries).rev() { + update_index(&mut self.indices, entry.hash, i, i + 1); + } + } + } + + pub(super) fn move_index(&mut self, from: usize, to: usize) { + let from_hash = self.entries[from].hash; + if from != to { + // Use a sentinal index so other indices don't collide. + update_index(&mut self.indices, from_hash, from, usize::MAX); + + // Update all other indices and rotate the entry positions. + if from < to { + self.decrement_indices(from + 1, to + 1); + self.entries[from..=to].rotate_left(1); + } else if to < from { + self.increment_indices(to, from); + self.entries[to..=from].rotate_right(1); + } + + // Change the sentinal index to its final position. + update_index(&mut self.indices, from_hash, usize::MAX, to); + } } /// Remove an entry by swapping it with the last diff --git a/src/set.rs b/src/set.rs index 64671b10..37289474 100644 --- a/src/set.rs +++ b/src/set.rs @@ -700,6 +700,19 @@ impl IndexSet { self.map.shift_remove_index(index).map(|(x, ())| x) } + /// Moves the position of a value from one index to another + /// by shifting all other values in-between. + /// + /// * If `from < to`, the other values will shift down while the targeted value moves up. + /// * If `from > to`, the other values will shift up while the targeted value moves down. + /// + /// ***Panics*** if `from` or `to` are out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn move_index(&mut self, from: usize, to: usize) { + self.map.move_index(from, to) + } + /// Swaps the position of two values in the set. /// /// ***Panics*** if `a` or `b` are out of bounds. diff --git a/tests/quick.rs b/tests/quick.rs index 4f462878..792aeb7a 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -216,6 +216,45 @@ quickcheck_limit! { map[&key] == value && map[i] == value }) } + + fn swap_indices(vec: Vec, a: usize, b: usize) -> TestResult { + let mut set = IndexSet::::from_iter(vec); + if a >= set.len() || b >= set.len() { + return TestResult::discard(); + } + + let mut vec = Vec::from_iter(set.iter().cloned()); + vec.swap(a, b); + + set.swap_indices(a, b); + + // Check both iteration order and hash lookups + assert!(set.iter().eq(vec.iter())); + assert!(vec.iter().enumerate().all(|(i, x)| { + set.get_index_of(x) == Some(i) + })); + TestResult::passed() + } + + fn move_index(vec: Vec, from: usize, to: usize) -> TestResult { + let mut set = IndexSet::::from_iter(vec); + if from >= set.len() || to >= set.len() { + return TestResult::discard(); + } + + let mut vec = Vec::from_iter(set.iter().cloned()); + let x = vec.remove(from); + vec.insert(to, x); + + set.move_index(from, to); + + // Check both iteration order and hash lookups + assert!(set.iter().eq(vec.iter())); + assert!(vec.iter().enumerate().all(|(i, x)| { + set.get_index_of(x) == Some(i) + })); + TestResult::passed() + } } use crate::Op::*; From 8d0216043db7aa81bf5dc5a41f9b60c140bcb247 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 23 May 2022 16:56:34 -0700 Subject: [PATCH 18/19] Use `u8` test indices so quickcheck is less likely to go out of bounds. (cherry picked from commit d1100674244fe629837481d4ef7844b74a896b13) --- tests/quick.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/quick.rs b/tests/quick.rs index 792aeb7a..e9d96acc 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -217,8 +217,12 @@ quickcheck_limit! { }) } - fn swap_indices(vec: Vec, a: usize, b: usize) -> TestResult { + // Use `u8` test indices so quickcheck is less likely to go out of bounds. + fn swap_indices(vec: Vec, a: u8, b: u8) -> TestResult { let mut set = IndexSet::::from_iter(vec); + let a = usize::from(a); + let b = usize::from(b); + if a >= set.len() || b >= set.len() { return TestResult::discard(); } @@ -236,8 +240,12 @@ quickcheck_limit! { TestResult::passed() } - fn move_index(vec: Vec, from: usize, to: usize) -> TestResult { + // Use `u8` test indices so quickcheck is less likely to go out of bounds. + fn move_index(vec: Vec, from: u8, to: u8) -> TestResult { let mut set = IndexSet::::from_iter(vec); + let from = usize::from(from); + let to = usize::from(to); + if from >= set.len() || to >= set.len() { return TestResult::discard(); } From 9eda24a4466bc5366ab4873b48cb2cf62feaf2a1 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 23 May 2022 17:30:44 -0700 Subject: [PATCH 19/19] Add a release note for move_index (cherry picked from commit 3848768aedcf3e91d2cbf73e0099e6880cac8e7c) --- RELEASES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index c4d05f96..ef33190e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,10 @@ - The new `IndexMap::shrink_to` and `IndexSet::shrink_to` methods shrink the capacity with a lower bound. + - The new `IndexMap::move_index` and `IndexSet::move_index` methods change + the position of an item from one index to another, shifting the items + between to accommodate the move. + - 1.8.2 - Bump the `rustc-rayon` dependency, for compiler use only.