diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7a5e6dd..71ad595e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,9 @@ on: branches: [ master ] pull_request: branches: [ master ] + merge_group: -name: Continuous integration +name: CI env: CARGO_TERM_COLOR: always @@ -16,28 +17,42 @@ jobs: strategy: matrix: include: - - rust: 1.36.0 # MSRV + - rust: 1.63.0 # MSRV features: - rust: stable - features: serde + features: arbitrary + - rust: stable + features: quickcheck - rust: stable features: rayon + - rust: stable + features: rustc-rayon + - rust: stable + features: serde + - rust: stable + features: borsh - rust: stable features: std - rust: beta features: - rust: nightly bench: test build benchmarks - - rust: nightly - features: test_low_transition_point steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + if: matrix.rust == '1.63.0' + with: + path: ~/.cargo/registry/index + key: cargo-git-index + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - override: true + - name: Downgrade dependencies + if: matrix.rust == '1.63.0' + run: | + cargo generate-lockfile + cargo update -p hashbrown --precise 0.15.0 - name: Tests run: | cargo build --verbose --features "${{ matrix.features }}" @@ -57,37 +72,85 @@ jobs: strategy: matrix: include: - - rust: 1.36.0 + - rust: 1.63.0 target: thumbv6m-none-eabi - rust: stable target: thumbv6m-none-eabi steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + if: matrix.rust == '1.63.0' + with: + path: ~/.cargo/registry/index + key: cargo-git-index + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - override: true target: ${{ matrix.target }} + - name: Downgrade dependencies + if: matrix.rust == '1.63.0' + run: | + cargo generate-lockfile + cargo update -p hashbrown --precise 0.15.0 - name: Tests run: | - cargo build -vv --target=${{ matrix.target }} + cargo build -vv --target=${{ matrix.target }} --no-default-features cargo build -v -p test-nostd --target=${{ matrix.target }} clippy: runs-on: ubuntu-latest - strategy: - matrix: - rust: - - beta steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@beta with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true components: clippy - - run: cargo clippy + - run: cargo clippy --all-features + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: miri, rust-src + - uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest + - run: cargo miri nextest run + - run: cargo miri test --doc + + minimal-versions: + name: Check MSRV and minimal-versions + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: ~/.cargo/registry/index + key: cargo-git-index + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@1.63.0 # MSRV + - uses: taiki-e/install-action@v2 + with: + tool: cargo-hack + - run: cargo +nightly hack generate-lockfile --remove-dev-deps -Z direct-minimal-versions + - name: Build (nightly) + run: cargo +nightly build --verbose --all-features + - name: Build (MSRV) + run: cargo build --verbose --features arbitrary,quickcheck,serde,rayon + + # One job that "summarizes" the success state of this pipeline. This can then be added to branch + # protection, rather than having to add each job separately. + success: + name: Success + runs-on: ubuntu-latest + needs: [tests, nostd_build, clippy, miri, minimal-versions] + # Github branch protection is exceedingly silly and treats "jobs skipped because a dependency + # failed" as success. So we have to do some contortions to ensure the job fails if any of its + # dependencies fails. + if: always() # make sure this is never "skipped" + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: check if any dependency failed + run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 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 b9bf5498..6c2bad5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,75 +1,64 @@ [package] name = "indexmap" -edition = "2018" -version = "1.6.1" -authors = [ -"bluss", -"Josh Stone " -] +edition = "2021" +version = "2.7.1" 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. -""" - +repository = "https://github.com/indexmap-rs/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.63" [lib] bench = false -[build-dependencies] -autocfg = "1" [dependencies] +equivalent = { version = "1.0", default-features = false } + +arbitrary = { version = "1.0", optional = true, default-features = false } +quickcheck = { version = "1.0", optional = true, default-features = false } serde = { version = "1.0", optional = true, default-features = false } -rayon = { version = "1.0", optional = true } +borsh = { version = "1.2", optional = true, default-features = false } +rayon = { version = "1.9", optional = true } + +# Internal feature, only used when building as part of rustc, +# not part of the stable interface of this crate. +rustc-rayon = { package = "rustc-rayon", version = "0.5", optional = true } [dependencies.hashbrown] -version = "0.9.1" +version = "0.15.0" default-features = false -features = ["raw"] [dev-dependencies] -itertools = "0.9" -rand = {version = "0.7", features = ["small_rng"] } -quickcheck = { version = "0.9", default-features = false } +itertools = "0.14" +rand = {version = "0.8", features = ["small_rng"] } +quickcheck = { version = "1.0", default-features = false } fnv = "1.0" lazy_static = "1.3" -fxhash = "0.2.1" serde_derive = "1.0" [features] -# Serialization with serde 1.0 -serde-1 = ["serde"] - -# Force the use of `std`, bypassing target detection. +default = ["std"] std = [] # for testing only, of course -test_low_transition_point = [] test_debug = [] [profile.bench] debug = true [package.metadata.release] -no-dev-version = true +allow-branch = ["master"] +sign-tag = true tag-name = "{{version}}" [package.metadata.docs.rs] -features = ["serde-1", "rayon"] +features = ["arbitrary", "quickcheck", "serde", "borsh", "rayon"] +rustdoc-args = ["--cfg", "docsrs"] [workspace] members = ["test-nostd", "test-serde"] + +[lints.clippy] +style = "allow" diff --git a/README.md b/README.md new file mode 100644 index 00000000..2585b232 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# indexmap + +[![build status](https://github.com/indexmap-rs/indexmap/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/indexmap-rs/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.63%2B-orange.svg)](https://img.shields.io/badge/rust-1.63%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. +The [`ordermap`](https://crates.io/crates/ordermap) crate now exists +as a wrapper over `indexmap` with stronger ordering properties. + +# 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()`, + `.swap_remove()`, or other methods that explicitly change order. + The alternate `.shift_remove()` does preserve relative order. +- 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.md](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md). diff --git a/README.rst b/README.rst deleted file mode 100644 index 49a71073..00000000 --- a/README.rst +++ /dev/null @@ -1,368 +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.36%2B-orange.svg -.. _rustc: https://img.shields.io/badge/rust-1.36%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 -============== - -- 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. diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 00000000..8867a49c --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,541 @@ +# Releases + +## 2.7.1 (2025-01-19) + +- Added `#[track_caller]` to functions that may panic. +- Improved memory reservation for `insert_entry`. + +## 2.7.0 (2024-11-30) + +- Added methods `Entry::insert_entry` and `VacantEntry::insert_entry`, returning + an `OccupiedEntry` after insertion. + +## 2.6.0 (2024-10-01) + +- Implemented `Clone` for `map::IntoIter` and `set::IntoIter`. +- Updated the `hashbrown` dependency to version 0.15. + +## 2.5.0 (2024-08-30) + +- Added an `insert_before` method to `IndexMap` and `IndexSet`, as an + alternative to `shift_insert` with different behavior on existing entries. +- Added `first_entry` and `last_entry` methods to `IndexMap`. +- Added `From` implementations between `IndexedEntry` and `OccupiedEntry`. + +## 2.4.0 (2024-08-13) + +- Added methods `IndexMap::append` and `IndexSet::append`, moving all items from + one map or set into another, and leaving the original capacity for reuse. + +## 2.3.0 (2024-07-31) + +- Added trait `MutableEntryKey` for opt-in mutable access to map entry keys. +- Added method `MutableKeys::iter_mut2` for opt-in mutable iteration of map + keys and values. + +## 2.2.6 (2024-03-22) + +- Added trait `MutableValues` for opt-in mutable access to set values. + +## 2.2.5 (2024-02-29) + +- Added optional `borsh` serialization support. + +## 2.2.4 (2024-02-28) + +- Added an `insert_sorted` method on `IndexMap`, `IndexSet`, and `VacantEntry`. +- Avoid hashing for lookups in single-entry maps. +- Limit preallocated memory in `serde` deserializers. + +## 2.2.3 (2024-02-11) + +- Added `move_index` and `swap_indices` methods to `IndexedEntry`, + `OccupiedEntry`, and `RawOccupiedEntryMut`, functioning like the existing + methods on `IndexMap`. +- Added `shift_insert` methods on `VacantEntry` and `RawVacantEntryMut`, as + well as `shift_insert_hashed_nocheck` on the latter, to insert the new entry + at a particular index. +- Added `shift_insert` methods on `IndexMap` and `IndexSet` to insert a new + entry at a particular index, or else move an existing entry there. + +## 2.2.2 (2024-01-31) + +- Added indexing methods to raw entries: `RawEntryBuilder::from_hash_full`, + `RawEntryBuilder::index_from_hash`, and `RawEntryMut::index`. + +## 2.2.1 (2024-01-28) + +- Corrected the signature of `RawOccupiedEntryMut::into_key(self) -> &'a mut K`, + This a breaking change from 2.2.0, but that version was published for less + than a day and has now been yanked. + +## 2.2.0 (2024-01-28) + +- The new `IndexMap::get_index_entry` method finds an entry by its index for + in-place manipulation. + +- The `Keys` iterator now implements `Index` for quick access to the + entry's key, compared to indexing the map to get the value. + +- The new `IndexMap::splice` and `IndexSet::splice` methods will drain the + given range as an iterator, and then replace that range with entries from + an input iterator. + +- The new trait `RawEntryApiV1` offers opt-in access to a raw entry API for + `IndexMap`, corresponding to the unstable API on `HashSet` as of Rust 1.75. + +- Many `IndexMap` and `IndexSet` methods have relaxed their type constraints, + e.g. removing `K: Hash` on methods that don't actually need to hash. + +- Removal methods `remove`, `remove_entry`, and `take` are now deprecated + in favor of their `shift_` or `swap_` prefixed variants, which are more + explicit about their effect on the index and order of remaining items. + The deprecated methods will remain to guide drop-in replacements from + `HashMap` and `HashSet` toward the prefixed methods. + +## 2.1.0 (2023-10-31) + +- Empty slices can now be created with `map::Slice::{new, new_mut}` and + `set::Slice::new`. In addition, `Slice::new`, `len`, and `is_empty` are + now `const` functions on both types. + +- `IndexMap`, `IndexSet`, and their respective `Slice`s all have binary + search methods for sorted data: map `binary_search_keys` and set + `binary_search` for plain comparison, `binary_search_by` for custom + comparators, `binary_search_by_key` for key extraction, and + `partition_point` for boolean conditions. + +## 2.0.2 (2023-09-29) + +- The `hashbrown` dependency has been updated to version 0.14.1 to + complete the support for Rust 1.63. + +## 2.0.1 (2023-09-27) + +- **MSRV**: Rust 1.63.0 is now supported as well, pending publication of + `hashbrown`'s relaxed MSRV (or use cargo `--ignore-rust-version`). + +## 2.0.0 (2023-06-23) + +- **MSRV**: Rust 1.64.0 or later is now required. + +- The `"std"` feature is no longer auto-detected. It is included in the + default feature set, or else can be enabled like any other Cargo feature. + +- The `"serde-1"` feature has been removed, leaving just the optional + `"serde"` dependency to be enabled like a feature itself. + +- `IndexMap::get_index_mut` now returns `Option<(&K, &mut V)>`, changing + the key part from `&mut K` to `&K`. There is also a new alternative + `MutableKeys::get_index_mut2` to access the former behavior. + +- The new `map::Slice` and `set::Slice` offer a linear view of maps + and sets, behaving a lot like normal `[(K, V)]` and `[T]` slices. Notably, + comparison traits like `Eq` only consider items in order, rather than hash + lookups, and slices even implement `Hash`. + +- `IndexMap` and `IndexSet` now have `sort_by_cached_key` and + `par_sort_by_cached_key` methods which perform stable sorts in place + using a key extraction function. + +- `IndexMap` and `IndexSet` now have `reserve_exact`, `try_reserve`, and + `try_reserve_exact` methods that correspond to the same methods on `Vec`. + However, exactness only applies to the direct capacity for items, while the + raw hash table still follows its own rules for capacity and load factor. + +- The `Equivalent` trait is now re-exported from the `equivalent` crate, + intended as a common base to allow types to work with multiple map types. + +- The `hashbrown` dependency has been updated to version 0.14. + +- The `serde_seq` module has been moved from the crate root to below the + `map` module. + +## 1.9.3 (2023-03-24) + +- Bump the `rustc-rayon` dependency, for compiler use only. + +## 1.9.2 (2022-11-17) + +- `IndexMap` and `IndexSet` both implement `arbitrary::Arbitrary<'_>` and + `quickcheck::Arbitrary` if those optional dependency features are enabled. + +## 1.9.1 (2022-06-21) + +- The MSRV now allows Rust 1.56.0 as well. However, currently `hashbrown` + 0.12.1 requires 1.56.1, so users on 1.56.0 should downgrade that to 0.12.0 + until there is a later published version relaxing its requirement. + +## 1.9.0 (2022-06-16) + +- **MSRV**: Rust 1.56.1 or later is now required. + +- The `hashbrown` dependency has been updated to version 0.12. + +- `IterMut` and `ValuesMut` now implement `Debug`. + +- 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 (2022-05-27) + +- Bump the `rustc-rayon` dependency, for compiler use only. + +## 1.8.1 (2022-03-29) + +- 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/indexmap-rs/indexmap/pull/222 + +## 1.8.0 (2022-01-07) + +- 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/indexmap-rs/indexmap/pull/195 +[196]: https://github.com/indexmap-rs/indexmap/pull/196 +[197]: https://github.com/indexmap-rs/indexmap/pull/197 +[203]: https://github.com/indexmap-rs/indexmap/pull/203 +[205]: https://github.com/indexmap-rs/indexmap/pull/205 +[211]: https://github.com/indexmap-rs/indexmap/pull/211 + +## 1.7.0 (2021-06-29) + +- **MSRV**: Rust 1.49 or later is now required. + +- The `hashbrown` dependency has been updated to version 0.11. + +## 1.6.2 (2021-03-05) + +- 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/indexmap-rs/indexmap/pull/170 +[175]: https://github.com/indexmap-rs/indexmap/pull/175 + +## 1.6.1 (2020-12-14) + +- 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/indexmap-rs/indexmap/pull/158 +[160]: https://github.com/indexmap-rs/indexmap/pull/160 + +## 1.6.0 (2020-09-05) + +- **MSRV**: Rust 1.36 or later is now required. + +- The `hashbrown` dependency has been updated to version 0.9. + +## 1.5.2 (2020-09-01) + +- 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/indexmap-rs/indexmap/pull/145 + +## 1.5.1 (2020-08-07) + +- 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/indexmap-rs/indexmap/pull/132 +[141]: https://github.com/indexmap-rs/indexmap/pull/141 +[142]: https://github.com/indexmap-rs/indexmap/pull/142 + +## 1.5.0 (2020-07-17) + +- **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/indexmap-rs/indexmap/pull/125 +[128]: https://github.com/indexmap-rs/indexmap/pull/128 +[131]: https://github.com/indexmap-rs/indexmap/pull/131 +[136]: https://github.com/indexmap-rs/indexmap/pull/136 + +## 1.4.0 (2020-06-01) + +- 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/indexmap-rs/indexmap/issues/123 +[115]: https://github.com/indexmap-rs/indexmap/pull/115 +[120]: https://github.com/indexmap-rs/indexmap/pull/120 + +## 1.3.2 (2020-02-05) + +- Maintenance update to regenerate the published `Cargo.toml`. + +## 1.3.1 (2020-01-15) + +- Maintenance update for formatting and `autocfg` 1.0. + +## 1.3.0 (2019-10-18) + +- 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 (2019-09-08) + +- 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 (2019-08-20) + +- 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 (2018-10-22) + +- 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 (2018-03-24) + +- Document Rust version policy for the crate (see rustdoc) + +## 1.0.0 (2018-03-11) + +- 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 (2018-02-14) + +- Renamed crate to `indexmap`; the `ordermap` crate is now deprecated + and the types `OrderMap/Set` now have a deprecation notice. + +## 0.4.0 (2018-02-02) + +- 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 (2018-01-14) + +- Documentation improvements + +## 0.3.4 (2018-01-04) + +- 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 (2017-12-28) + +- Document insertion behaviour better by @lucab +- Updated dependences (no feature changes) by @ignatenkobrain + +## 0.3.2 (2017-11-25) + +- Add `OrderSet` by @cuviper! +- `OrderMap::drain` is now (too) a double ended iterator. + +## 0.3.1 (2017-11-19) + +- In all ordermap iterators, forward the `collect` method to the underlying + iterator as well. +- Add crates.io categories. + +## 0.3.0 (2017-10-07) + +- 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/indexmap-rs/indexmap/pull/10 + +## 0.2.13 (2017-09-30) + +- Fix deserialization to support custom hashers by @Techcable. +- Add methods `.index()` on the entry types by @garro95. + +## 0.2.12 (2017-09-11) + +- Add methods `.with_hasher()`, `.hasher()`. + +## 0.2.11 (2017-08-29) + +- 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 (2017-04-29) + +- Add iterator `.drain(..)` by @stevej. + +## 0.2.9 (2017-03-26) + +- Add method `.is_empty()` by @overvenus. +- Implement `PartialEq, Eq` by @overvenus. +- Add method `.sorted_by()`. + +## 0.2.8 (2017-03-01) + +- Add iterators `.values()` and `.values_mut()`. +- Fix compatibility with 32-bit platforms. + +## 0.2.7 (2016-11-02) + +- Add `.retain()`. + +## 0.2.6 (2016-11-02) + +- Add `OccupiedEntry::remove_entry` and other minor entry methods, + so that it now has all the features of `HashMap`'s entries. + +## 0.2.5 (2016-10-31) + +- Improved `.pop()` slightly. + +## 0.2.4 (2016-10-22) + +- Improved performance of `.insert()` ([#3]) by @pczarn. + +[#3]: https://github.com/indexmap-rs/indexmap/pull/3 + +## 0.2.3 (2016-10-11) + +- 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 (2016-10-02) + +- Disable the verbose `Debug` impl by default. + +## 0.2.1 (2016-10-02) + +- Fix doc links and clarify docs. + +## 0.2.0 (2016-10-01) + +- 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 (2016-09-19) + +- Implement the 32/32 split idea for `Pos` which improves cache utilization + and lookup performance. + +## 0.1.1 (2016-09-16) + +- Initial release. diff --git a/benches/bench.rs b/benches/bench.rs index 102cd49c..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; @@ -561,7 +560,7 @@ fn indexmap_merge_shuffle(b: &mut Bencher) { #[bench] fn swap_remove_indexmap_100_000(b: &mut Bencher) { let map = IMAP_100K.clone(); - let mut keys = Vec::from_iter(map.keys().cloned()); + let mut keys = Vec::from_iter(map.keys().copied()); let mut rng = small_rng(); keys.shuffle(&mut rng); @@ -578,7 +577,7 @@ fn swap_remove_indexmap_100_000(b: &mut Bencher) { #[bench] fn shift_remove_indexmap_100_000_few(b: &mut Bencher) { let map = IMAP_100K.clone(); - let mut keys = Vec::from_iter(map.keys().cloned()); + let mut keys = Vec::from_iter(map.keys().copied()); let mut rng = small_rng(); keys.shuffle(&mut rng); keys.truncate(50); 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/build.rs b/build.rs deleted file mode 100644 index 9f9fa054..00000000 --- a/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - // If "std" is explicitly requested, don't bother probing the target for it. - match std::env::var_os("CARGO_FEATURE_STD") { - Some(_) => autocfg::emit("has_std"), - None => autocfg::new().emit_sysroot_crate("std"), - } - autocfg::rerun_path("build.rs"); -} diff --git a/src/arbitrary.rs b/src/arbitrary.rs new file mode 100644 index 00000000..7798438c --- /dev/null +++ b/src/arbitrary.rs @@ -0,0 +1,77 @@ +#[cfg(feature = "arbitrary")] +#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] +mod impl_arbitrary { + use crate::{IndexMap, IndexSet}; + use arbitrary::{Arbitrary, Result, Unstructured}; + use core::hash::{BuildHasher, Hash}; + + impl<'a, K, V, S> Arbitrary<'a> for IndexMap + where + K: Arbitrary<'a> + Hash + Eq, + V: Arbitrary<'a>, + S: BuildHasher + Default, + { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + u.arbitrary_iter()?.collect() + } + + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { + u.arbitrary_take_rest_iter()?.collect() + } + } + + impl<'a, T, S> Arbitrary<'a> for IndexSet + where + T: Arbitrary<'a> + Hash + Eq, + S: BuildHasher + Default, + { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + u.arbitrary_iter()?.collect() + } + + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { + u.arbitrary_take_rest_iter()?.collect() + } + } +} + +#[cfg(feature = "quickcheck")] +#[cfg_attr(docsrs, doc(cfg(feature = "quickcheck")))] +mod impl_quickcheck { + use crate::{IndexMap, IndexSet}; + use alloc::boxed::Box; + use alloc::vec::Vec; + use core::hash::{BuildHasher, Hash}; + use quickcheck::{Arbitrary, Gen}; + + impl Arbitrary for IndexMap + where + K: Arbitrary + Hash + Eq, + V: Arbitrary, + S: BuildHasher + Default + Clone + 'static, + { + fn arbitrary(g: &mut Gen) -> Self { + Self::from_iter(Vec::arbitrary(g)) + } + + fn shrink(&self) -> Box> { + let vec = Vec::from_iter(self.clone()); + Box::new(vec.shrink().map(Self::from_iter)) + } + } + + impl Arbitrary for IndexSet + where + T: Arbitrary + Hash + Eq, + S: BuildHasher + Default + Clone + 'static, + { + fn arbitrary(g: &mut Gen) -> Self { + Self::from_iter(Vec::arbitrary(g)) + } + + fn shrink(&self) -> Box> { + let vec = Vec::from_iter(self.clone()); + Box::new(vec.shrink().map(Self::from_iter)) + } + } +} diff --git a/src/borsh.rs b/src/borsh.rs new file mode 100644 index 00000000..c485bd52 --- /dev/null +++ b/src/borsh.rs @@ -0,0 +1,122 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "borsh")))] + +use alloc::vec::Vec; +use core::hash::BuildHasher; +use core::hash::Hash; +use core::mem::size_of; + +use borsh::error::ERROR_ZST_FORBIDDEN; +use borsh::io::{Error, ErrorKind, Read, Result, Write}; +use borsh::{BorshDeserialize, BorshSerialize}; + +use crate::map::IndexMap; +use crate::set::IndexSet; + +impl BorshSerialize for IndexMap +where + K: BorshSerialize, + V: BorshSerialize, +{ + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + check_zst::()?; + + let iterator = self.iter(); + + u32::try_from(iterator.len()) + .map_err(|_| ErrorKind::InvalidData)? + .serialize(writer)?; + + for (key, value) in iterator { + key.serialize(writer)?; + value.serialize(writer)?; + } + + Ok(()) + } +} + +impl BorshDeserialize for IndexMap +where + K: BorshDeserialize + Eq + Hash, + V: BorshDeserialize, + S: BuildHasher + Default, +{ + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + check_zst::()?; + let vec = >::deserialize_reader(reader)?; + Ok(vec.into_iter().collect::>()) + } +} + +impl BorshSerialize for IndexSet +where + T: BorshSerialize, +{ + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + check_zst::()?; + + let iterator = self.iter(); + + u32::try_from(iterator.len()) + .map_err(|_| ErrorKind::InvalidData)? + .serialize(writer)?; + + for item in iterator { + item.serialize(writer)?; + } + + Ok(()) + } +} + +impl BorshDeserialize for IndexSet +where + T: BorshDeserialize + Eq + Hash, + S: BuildHasher + Default, +{ + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + check_zst::()?; + let vec = >::deserialize_reader(reader)?; + Ok(vec.into_iter().collect::>()) + } +} + +fn check_zst() -> Result<()> { + if size_of::() == 0 { + return Err(Error::new(ErrorKind::InvalidData, ERROR_ZST_FORBIDDEN)); + } + Ok(()) +} + +#[cfg(test)] +mod borsh_tests { + use super::*; + + #[test] + fn map_borsh_roundtrip() { + let original_map: IndexMap = { + let mut map = IndexMap::new(); + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + map + }; + let serialized_map = borsh::to_vec(&original_map).unwrap(); + let deserialized_map: IndexMap = + BorshDeserialize::try_from_slice(&serialized_map).unwrap(); + assert_eq!(original_map, deserialized_map); + } + + #[test] + fn set_borsh_roundtrip() { + let original_map: IndexSet = [1, 2, 3, 4, 5, 6].into_iter().collect(); + let serialized_map = borsh::to_vec(&original_map).unwrap(); + let deserialized_map: IndexSet = + BorshDeserialize::try_from_slice(&serialized_map).unwrap(); + assert_eq!(original_map, deserialized_map); + } +} diff --git a/src/equivalent.rs b/src/equivalent.rs deleted file mode 100644 index ad6635ff..00000000 --- a/src/equivalent.rs +++ /dev/null @@ -1,27 +0,0 @@ -use core::borrow::Borrow; - -/// Key equivalence trait. -/// -/// This trait allows hash table lookup to be customized. -/// It has one blanket implementation that uses the regular `Borrow` solution, -/// just like `HashMap` and `BTreeMap` do, so that you can pass `&str` to lookup -/// into a map with `String` keys and so on. -/// -/// # Contract -/// -/// The implementor **must** hash like `K`, if it is hashable. -pub trait Equivalent { - /// Compare self to `key` and return `true` if they are equal. - fn equivalent(&self, key: &K) -> bool; -} - -impl Equivalent for Q -where - Q: Eq, - K: Borrow, -{ - #[inline] - fn equivalent(&self, key: &K) -> bool { - *self == *key.borrow() - } -} diff --git a/src/lib.rs b/src/lib.rs index 1a58a830..4fd9aac1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ -// We *mostly* avoid unsafe code, but `map::core::raw` allows it to use `RawTable` buckets. +// We *mostly* avoid unsafe code, but `Slice` allows it for DST casting. #![deny(unsafe_code)] #![warn(rust_2018_idioms)] -#![doc(html_root_url = "https://docs.rs/indexmap/1/")] #![no_std] //! [`IndexMap`] is a hash table where the iteration order of the key-value @@ -10,11 +9,7 @@ //! [`IndexSet`] is a corresponding hash set using the same implementation and //! with similar properties. //! -//! [`IndexMap`]: map/struct.IndexMap.html -//! [`IndexSet`]: set/struct.IndexSet.html -//! -//! -//! ### Feature Highlights +//! ### Highlights //! //! [`IndexMap`] and [`IndexSet`] are drop-in compatible with the std `HashMap` //! and `HashSet`, but they also have some features of note: @@ -24,80 +19,104 @@ //! - The [`Equivalent`] trait, which offers more flexible equality definitions //! between borrowed and owned versions of keys. //! - The [`MutableKeys`][map::MutableKeys] trait, which gives opt-in mutable -//! access to hash map keys. +//! access to map keys, and [`MutableValues`][set::MutableValues] for sets. +//! +//! ### Feature Flags +//! +//! To reduce the amount of compiled code in the crate by default, certain +//! features are gated behind [feature flags]. These allow you to opt in to (or +//! out of) functionality. Below is a list of the features available in this +//! crate. +//! +//! * `std`: Enables features which require the Rust standard library. For more +//! information see the section on [`no_std`]. +//! * `rayon`: Enables parallel iteration and other parallel methods. +//! * `serde`: Adds implementations for [`Serialize`] and [`Deserialize`] +//! to [`IndexMap`] and [`IndexSet`]. Alternative implementations for +//! (de)serializing [`IndexMap`] as an ordered sequence are available in the +//! [`map::serde_seq`] module. +//! * `borsh`: Adds implementations for [`BorshSerialize`] and [`BorshDeserialize`] +//! to [`IndexMap`] and [`IndexSet`]. **Note:** When this feature is enabled, +//! you cannot enable the `derive` feature of [`borsh`] due to a cyclic +//! dependency. Instead, add the `borsh-derive` crate as an explicit +//! dependency in your Cargo.toml and import as e.g. +//! `use borsh_derive::{BorshSerialize, BorshDeserialize};`. +//! * `arbitrary`: Adds implementations for the [`arbitrary::Arbitrary`] trait +//! to [`IndexMap`] and [`IndexSet`]. +//! * `quickcheck`: Adds implementations for the [`quickcheck::Arbitrary`] trait +//! to [`IndexMap`] and [`IndexSet`]. +//! +//! _Note: only the `std` feature is enabled by default._ +//! +//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section +//! [`no_std`]: #no-standard-library-targets +//! [`Serialize`]: `::serde::Serialize` +//! [`Deserialize`]: `::serde::Deserialize` +//! [`BorshSerialize`]: `::borsh::BorshSerialize` +//! [`BorshDeserialize`]: `::borsh::BorshDeserialize` +//! [`borsh`]: `::borsh` +//! [`arbitrary::Arbitrary`]: `::arbitrary::Arbitrary` +//! [`quickcheck::Arbitrary`]: `::quickcheck::Arbitrary` //! //! ### Alternate Hashers //! -//! [`IndexMap`] and [`IndexSet`] have a default hasher type `S = RandomState`, +//! [`IndexMap`] and [`IndexSet`] have a default hasher type +//! [`S = RandomState`][std::collections::hash_map::RandomState], //! just like the standard `HashMap` and `HashSet`, which is resistant to //! HashDoS attacks but not the most performant. Type aliases can make it easier //! to use alternate hashers: //! //! ``` //! use fnv::FnvBuildHasher; -//! use fxhash::FxBuildHasher; //! use indexmap::{IndexMap, IndexSet}; //! //! type FnvIndexMap = IndexMap; //! type FnvIndexSet = IndexSet; //! -//! type FxIndexMap = IndexMap; -//! type FxIndexSet = IndexSet; -//! //! let std: IndexSet = (0..100).collect(); //! let fnv: FnvIndexSet = (0..100).collect(); -//! let fx: FxIndexSet = (0..100).collect(); //! assert_eq!(std, fnv); -//! assert_eq!(std, fx); //! ``` //! //! ### Rust Version //! -//! This version of indexmap requires Rust 1.36 or later. +//! This version of indexmap requires Rust 1.63 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 +//! The indexmap 2.x release series will use a carefully considered version +//! upgrade policy, where in a later 2.x version, we will raise the minimum //! required Rust version. //! //! ## No Standard Library Targets //! -//! 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. It can be tested by building for a std-less target. +//! This crate supports being built without `std`, requiring `alloc` instead. +//! This is chosen by disabling the default "std" cargo feature, by adding +//! `default-features = false` to your dependency specification. //! //! - Creating maps and sets using [`new`][IndexMap::new] and -//! [`with_capacity`][IndexMap::with_capacity] is unavailable without `std`. -//! Use methods [`IndexMap::default`][def], -//! [`with_hasher`][IndexMap::with_hasher], +//! [`with_capacity`][IndexMap::with_capacity] is unavailable without `std`. +//! Use methods [`IndexMap::default`], [`with_hasher`][IndexMap::with_hasher], //! [`with_capacity_and_hasher`][IndexMap::with_capacity_and_hasher] instead. //! A no-std compatible hasher will be needed as well, for example //! from the crate `twox-hash`. //! - Macros [`indexmap!`] and [`indexset!`] are unavailable without `std`. -//! -//! [def]: map/struct.IndexMap.html#impl-Default -#[cfg(not(has_std))] +#![cfg_attr(docsrs, feature(doc_cfg))] + extern crate alloc; -#[cfg(has_std)] +#[cfg(feature = "std")] #[macro_use] extern crate std; -#[cfg(not(has_std))] use alloc::vec::{self, Vec}; -#[cfg(has_std)] -use std::vec::{self, Vec}; - +mod arbitrary; #[macro_use] mod macros; -mod equivalent; -mod mutable_keys; +#[cfg(feature = "borsh")] +mod borsh; #[cfg(feature = "serde")] mod serde; -#[cfg(feature = "serde")] -pub mod serde_seq; mod util; pub mod map; @@ -108,9 +127,12 @@ pub mod set; #[cfg(feature = "rayon")] mod rayon; -pub use crate::equivalent::Equivalent; +#[cfg(feature = "rustc-rayon")] +mod rustc; + pub use crate::map::IndexMap; pub use crate::set::IndexSet; +pub use equivalent::Equivalent; // shared private items @@ -167,6 +189,9 @@ impl Bucket { fn key(self) -> K { self.key } + fn value(self) -> V { + self.value + } fn key_value(self) -> (K, V) { (self.key, self.value) } @@ -190,3 +215,59 @@ trait Entries { where F: FnOnce(&mut [Self::Entry]); } + +/// The error type for [`try_reserve`][IndexMap::try_reserve] methods. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TryReserveError { + kind: TryReserveErrorKind, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +enum TryReserveErrorKind { + // The standard library's kind is currently opaque to us, otherwise we could unify this. + Std(alloc::collections::TryReserveError), + CapacityOverflow, + AllocError { layout: alloc::alloc::Layout }, +} + +// These are not `From` so we don't expose them in our public API. +impl TryReserveError { + fn from_alloc(error: alloc::collections::TryReserveError) -> Self { + Self { + kind: TryReserveErrorKind::Std(error), + } + } + + fn from_hashbrown(error: hashbrown::TryReserveError) -> Self { + Self { + kind: match error { + hashbrown::TryReserveError::CapacityOverflow => { + TryReserveErrorKind::CapacityOverflow + } + hashbrown::TryReserveError::AllocError { layout } => { + TryReserveErrorKind::AllocError { layout } + } + }, + } + } +} + +impl core::fmt::Display for TryReserveError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let reason = match &self.kind { + TryReserveErrorKind::Std(e) => return core::fmt::Display::fmt(e, f), + TryReserveErrorKind::CapacityOverflow => { + " because the computed capacity exceeded the collection's maximum" + } + TryReserveErrorKind::AllocError { .. } => { + " because the memory allocator returned an error" + } + }; + f.write_str("memory allocation failed")?; + f.write_str(reason) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for TryReserveError {} diff --git a/src/macros.rs b/src/macros.rs index c4d84217..b347de22 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,7 @@ -#[cfg(has_std)] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[macro_export] -/// Create an `IndexMap` from a list of key-value pairs +/// Create an [`IndexMap`][crate::IndexMap] from a list of key-value pairs /// /// ## Example /// @@ -19,25 +20,25 @@ /// assert_eq!(map.keys().next(), Some(&"a")); /// ``` macro_rules! indexmap { - (@single $($x:tt)*) => (()); - (@count $($rest:expr),*) => (<[()]>::len(&[$($crate::indexmap!(@single $rest)),*])); - ($($key:expr => $value:expr,)+) => { $crate::indexmap!($($key => $value),+) }; ($($key:expr => $value:expr),*) => { { - let _cap = $crate::indexmap!(@count $($key),*); - let mut _map = $crate::IndexMap::with_capacity(_cap); + // Note: `stringify!($key)` is just here to consume the repetition, + // but we throw away that string literal during constant evaluation. + const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]); + let mut map = $crate::IndexMap::with_capacity(CAP); $( - _map.insert($key, $value); + map.insert($key, $value); )* - _map + map } }; } -#[cfg(has_std)] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[macro_export] -/// Create an `IndexSet` from a list of values +/// Create an [`IndexSet`][crate::IndexSet] from a list of values /// /// ## Example /// @@ -56,18 +57,17 @@ macro_rules! indexmap { /// assert_eq!(set.iter().next(), Some(&"a")); /// ``` macro_rules! indexset { - (@single $($x:tt)*) => (()); - (@count $($rest:expr),*) => (<[()]>::len(&[$($crate::indexset!(@single $rest)),*])); - ($($value:expr,)+) => { $crate::indexset!($($value),+) }; ($($value:expr),*) => { { - let _cap = $crate::indexset!(@count $($value),*); - let mut _set = $crate::IndexSet::with_capacity(_cap); + // Note: `stringify!($value)` is just here to consume the repetition, + // but we throw away that string literal during constant evaluation. + const CAP: usize = <[()]>::len(&[$({ stringify!($value); }),*]); + let mut set = $crate::IndexSet::with_capacity(CAP); $( - _set.insert($value); + set.insert($value); )* - _set + set } }; } @@ -116,5 +116,63 @@ macro_rules! double_ended_iterator_methods { fn next_back(&mut self) -> Option { self.iter.next_back().map($map_elt) } + + fn nth_back(&mut self, n: usize) -> Option { + self.iter.nth_back(n).map($map_elt) + } + }; +} + +// generate `ParallelIterator` methods by just forwarding to the underlying +// self.entries and mapping its elements. +#[cfg(any(feature = "rayon", feature = "rustc-rayon"))] +macro_rules! parallel_iterator_methods { + // $map_elt is the mapping function from the underlying iterator's element + ($map_elt:expr) => { + fn drive_unindexed(self, consumer: C) -> C::Result + where + C: UnindexedConsumer, + { + self.entries + .into_par_iter() + .map($map_elt) + .drive_unindexed(consumer) + } + + // NB: This allows indexed collection, e.g. directly into a `Vec`, but the + // underlying iterator must really be indexed. We should remove this if we + // start having tombstones that must be filtered out. + fn opt_len(&self) -> Option { + Some(self.entries.len()) + } + }; +} + +// generate `IndexedParallelIterator` methods by just forwarding to the underlying +// self.entries and mapping its elements. +#[cfg(any(feature = "rayon", feature = "rustc-rayon"))] +macro_rules! indexed_parallel_iterator_methods { + // $map_elt is the mapping function from the underlying iterator's element + ($map_elt:expr) => { + fn drive(self, consumer: C) -> C::Result + where + C: Consumer, + { + self.entries.into_par_iter().map($map_elt).drive(consumer) + } + + fn len(&self) -> usize { + self.entries.len() + } + + fn with_producer(self, callback: CB) -> CB::Output + where + CB: ProducerCallback, + { + self.entries + .into_par_iter() + .map($map_elt) + .with_producer(callback) + } }; } diff --git a/src/map.rs b/src/map.rs index 532791cf..347649f8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,36 +1,51 @@ -//! `IndexMap` is a hash table where the iteration order of the key-value +//! [`IndexMap`] is a hash table where the iteration order of the key-value //! pairs is independent of the hash values of the keys. mod core; +mod iter; +mod mutable; +mod slice; -pub use crate::mutable_keys::MutableKeys; +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +pub mod serde_seq; + +#[cfg(test)] +mod tests; + +pub use self::core::raw_entry_v1::{self, RawEntryApiV1}; +pub use self::core::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; +pub use self::iter::{ + Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, IterMut2, Keys, Splice, Values, ValuesMut, +}; +pub use self::mutable::MutableEntryKey; +pub use self::mutable::MutableKeys; +pub use self::slice::Slice; #[cfg(feature = "rayon")] pub use crate::rayon::map as rayon; -use crate::vec::{self, Vec}; use ::core::cmp::Ordering; use ::core::fmt; use ::core::hash::{BuildHasher, Hash, Hasher}; -use ::core::iter::FromIterator; +use ::core::mem; use ::core::ops::{Index, IndexMut, RangeBounds}; -use ::core::slice::{Iter as SliceIter, IterMut as SliceIterMut}; +use alloc::boxed::Box; +use alloc::vec::Vec; -#[cfg(has_std)] +#[cfg(feature = "std")] use std::collections::hash_map::RandomState; use self::core::IndexMapCore; -use crate::equivalent::Equivalent; -use crate::util::third; -use crate::{Bucket, Entries, HashValue}; - -pub use self::core::{Entry, OccupiedEntry, VacantEntry}; +use crate::util::{third, try_simplify_range}; +use crate::{Bucket, Entries, Equivalent, HashValue, TryReserveError}; /// A hash table where the iteration order of the key-value pairs is independent /// of the hash values of the keys. /// -/// The interface is closely compatible with the standard `HashMap`, but also -/// has additional features. +/// The interface is closely compatible with the standard +/// [`HashMap`][std::collections::HashMap], +/// but also has additional features. /// /// # Order /// @@ -41,7 +56,8 @@ pub use self::core::{Entry, OccupiedEntry, VacantEntry}; /// All iterators traverse the map in *the order*. /// /// The insertion order is preserved, with **notable exceptions** like the -/// `.remove()` or `.swap_remove()` methods. Methods such as `.sort_by()` of +/// [`.remove()`][Self::remove] or [`.swap_remove()`][Self::swap_remove] methods. +/// Methods such as [`.sort_by()`][Self::sort_by] of /// course result in a new order, depending on the sorting order. /// /// # Indices @@ -67,14 +83,14 @@ pub use self::core::{Entry, OccupiedEntry, VacantEntry}; /// assert_eq!(letters[&'u'], 1); /// assert_eq!(letters.get(&'y'), None); /// ``` -#[cfg(has_std)] +#[cfg(feature = "std")] pub struct IndexMap { - core: IndexMapCore, + pub(crate) core: IndexMapCore, hash_builder: S, } -#[cfg(not(has_std))] +#[cfg(not(feature = "std"))] pub struct IndexMap { - core: IndexMapCore, + pub(crate) core: IndexMapCore, hash_builder: S, } @@ -128,19 +144,22 @@ where K: fmt::Debug, V: fmt::Debug, { + #[cfg(not(feature = "test_debug"))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if cfg!(not(feature = "test_debug")) { - f.debug_map().entries(self.iter()).finish() - } else { - // Let the inner `IndexMapCore` print all of its details - f.debug_struct("IndexMap") - .field("core", &self.core) - .finish() - } + f.debug_map().entries(self.iter()).finish() + } + + #[cfg(feature = "test_debug")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Let the inner `IndexMapCore` print all of its details + f.debug_struct("IndexMap") + .field("core", &self.core) + .finish() } } -#[cfg(has_std)] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl IndexMap { /// Create a new map. (Does not allocate.) #[inline] @@ -166,10 +185,7 @@ impl IndexMap { #[inline] pub fn with_capacity_and_hasher(n: usize, hash_builder: S) -> Self { if n == 0 { - IndexMap { - core: IndexMapCore::new(), - hash_builder, - } + Self::with_hasher(hash_builder) } else { IndexMap { core: IndexMapCore::with_capacity(n), @@ -178,11 +194,22 @@ impl IndexMap { } } - /// Create a new map with `hash_builder` - pub fn with_hasher(hash_builder: S) -> Self { - Self::with_capacity_and_hasher(0, hash_builder) + /// Create a new map with `hash_builder`. + /// + /// This function is `const`, so it + /// can be called in `static` contexts. + pub const fn with_hasher(hash_builder: S) -> Self { + IndexMap { + core: IndexMapCore::new(), + hash_builder, + } } + /// Return the number of elements the map can hold without reallocating. + /// + /// This number is a lower bound; the map might be able to hold more, + /// but is guaranteed to be able to hold at least this many. + /// /// Computes in **O(1)** time. pub fn capacity(&self) -> usize { self.core.capacity() @@ -211,38 +238,38 @@ impl IndexMap { /// Return an iterator over the key-value pairs of the map, in their order pub fn iter(&self) -> Iter<'_, K, V> { - Iter { - iter: self.as_entries().iter(), - } + Iter::new(self.as_entries()) } /// Return an iterator over the key-value pairs of the map, in their order pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut { - iter: self.as_entries_mut().iter_mut(), - } + IterMut::new(self.as_entries_mut()) } /// Return an iterator over the keys of the map, in their order pub fn keys(&self) -> Keys<'_, K, V> { - Keys { - iter: self.as_entries().iter(), - } + Keys::new(self.as_entries()) + } + + /// Return an owning iterator over the keys of the map, in their order + pub fn into_keys(self) -> IntoKeys { + IntoKeys::new(self.into_entries()) } /// Return an iterator over the values of the map, in their order pub fn values(&self) -> Values<'_, K, V> { - Values { - iter: self.as_entries().iter(), - } + Values::new(self.as_entries()) } - /// Return an iterator over mutable references to the the values of the map, + /// Return an iterator over mutable references to the values of the map, /// in their order pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { - ValuesMut { - iter: self.as_entries_mut().iter_mut(), - } + ValuesMut::new(self.as_entries_mut()) + } + + /// Return an owning iterator over the values of the map, in their order + pub fn into_values(self) -> IntoValues { + IntoValues::new(self.into_entries()) } /// Remove all key-value pairs in the map, while preserving its capacity. @@ -262,7 +289,7 @@ impl IndexMap { /// Clears the `IndexMap` in the given index range, returning those /// key-value pairs as a drain iterator. /// - /// The range may be any type that implements `RangeBounds`, + /// The range may be any type that implements [`RangeBounds`], /// including all of the `std::ops::Range*` types, or even a tuple pair of /// `Bound` start and end values. To drain the map entirely, use `RangeFull` /// like `map.drain(..)`. @@ -272,13 +299,12 @@ impl IndexMap { /// /// ***Panics*** if the starting point is greater than the end point or if /// the end point is greater than the length of the map. + #[track_caller] pub fn drain(&mut self, range: R) -> Drain<'_, K, V> where R: RangeBounds, { - Drain { - iter: self.core.drain(range), - } + Drain::new(self.core.drain(range)) } /// Splits the collection into two at the given index. @@ -288,6 +314,7 @@ impl IndexMap { /// the elements `[0, at)` with its previous capacity unchanged. /// /// ***Panics*** if `at > len`. + #[track_caller] pub fn split_off(&mut self, at: usize) -> Self where S: Clone, @@ -297,13 +324,7 @@ impl IndexMap { hash_builder: self.hash_builder.clone(), } } -} -impl IndexMap -where - K: Hash + Eq, - S: BuildHasher, -{ /// Reserve capacity for `additional` more key-value pairs. /// /// Computes in **O(n)** time. @@ -311,32 +332,71 @@ where self.core.reserve(additional); } + /// Reserve capacity for `additional` more key-value pairs, without over-allocating. + /// + /// Unlike `reserve`, this does not deliberately over-allocate the entry capacity to avoid + /// frequent re-allocations. However, the underlying data structures may still have internal + /// capacity requirements, and the allocator itself may give more space than requested, so this + /// cannot be relied upon to be precisely minimal. + /// + /// Computes in **O(n)** time. + pub fn reserve_exact(&mut self, additional: usize) { + self.core.reserve_exact(additional); + } + + /// Try to reserve capacity for `additional` more key-value pairs. + /// + /// Computes in **O(n)** time. + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.core.try_reserve(additional) + } + + /// Try to reserve capacity for `additional` more key-value pairs, without over-allocating. + /// + /// Unlike `try_reserve`, this does not deliberately over-allocate the entry capacity to avoid + /// frequent re-allocations. However, the underlying data structures may still have internal + /// capacity requirements, and the allocator itself may give more space than requested, so this + /// cannot be relied upon to be precisely minimal. + /// + /// Computes in **O(n)** time. + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.core.try_reserve_exact(additional) + } + /// Shrink the capacity of the map as much as possible. /// /// Computes in **O(n)** time. pub fn shrink_to_fit(&mut self) { - self.core.shrink_to_fit(); + self.core.shrink_to(0); } - fn hash(&self, key: &Q) -> HashValue { - let mut h = self.hash_builder.build_hasher(); - key.hash(&mut h); - HashValue(h.finish() as usize) + /// 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); } +} +impl IndexMap +where + K: Hash + Eq, + S: BuildHasher, +{ /// Insert a key-value pair in 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(_)`. + /// 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 (amortized average). /// - /// See also [`entry`](#method.entry) if you you want to insert *or* modify - /// or if you need to get the index of the corresponding key-value pair. + /// See also [`entry`][Self::entry] if you want to insert *or* modify, + /// or [`insert_full`][Self::insert_full] if you need to get the index of + /// the corresponding key-value pair. pub fn insert(&mut self, key: K, value: V) -> Option { self.insert_full(key, value).1 } @@ -345,20 +405,207 @@ where /// /// 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 `(index, Some(_))`. + /// with `value`, and the older value is returned inside `(index, Some(_))`. /// /// If no equivalent key existed in the map: the new key-value pair is /// inserted, last in order, and `(index, None)` is returned. /// /// Computes in **O(1)** time (amortized average). /// - /// See also [`entry`](#method.entry) if you you want to insert *or* modify - /// or if you need to get the index of the corresponding key-value pair. + /// See also [`entry`][Self::entry] if you want to insert *or* modify. pub fn insert_full(&mut self, key: K, value: V) -> (usize, Option) { let hash = self.hash(&key); self.core.insert_full(hash, key, value) } + /// Insert a key-value pair in the map at its ordered position among sorted keys. + /// + /// This is equivalent to finding the position with + /// [`binary_search_keys`][Self::binary_search_keys], then either updating + /// it or calling [`insert_before`][Self::insert_before] for a new key. + /// + /// If the sorted key is found in the map, its corresponding value is + /// updated with `value`, and the older value is returned inside + /// `(index, Some(_))`. Otherwise, the new key-value pair is inserted at + /// the sorted position, and `(index, None)` is returned. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). Instead of repeating calls to + /// `insert_sorted`, it may be faster to call batched [`insert`][Self::insert] + /// or [`extend`][Self::extend] and only call [`sort_keys`][Self::sort_keys] + /// or [`sort_unstable_keys`][Self::sort_unstable_keys] once. + pub fn insert_sorted(&mut self, key: K, value: V) -> (usize, Option) + where + K: Ord, + { + match self.binary_search_keys(&key) { + Ok(i) => (i, Some(mem::replace(&mut self[i], value))), + Err(i) => self.insert_before(i, key, value), + } + } + + /// Insert a key-value pair in the map before the entry at the given index, or at the end. + /// + /// If an equivalent key already exists in the map: the key remains and + /// is moved to the new position in the map, its corresponding value is updated + /// with `value`, and the older value is returned inside `Some(_)`. The returned index + /// will either be the given index or one less, depending on how the entry moved. + /// (See [`shift_insert`](Self::shift_insert) for different behavior here.) + /// + /// If no equivalent key existed in the map: the new key-value pair is + /// inserted exactly at the given index, and `None` is returned. + /// + /// ***Panics*** if `index` is out of bounds. + /// Valid indices are `0..=map.len()` (inclusive). + /// + /// Computes in **O(n)** time (average). + /// + /// See also [`entry`][Self::entry] if you want to insert *or* modify, + /// perhaps only using the index for new entries with [`VacantEntry::shift_insert`]. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexMap; + /// let mut map: IndexMap = ('a'..='z').map(|c| (c, ())).collect(); + /// + /// // The new key '*' goes exactly at the given index. + /// assert_eq!(map.get_index_of(&'*'), None); + /// assert_eq!(map.insert_before(10, '*', ()), (10, None)); + /// assert_eq!(map.get_index_of(&'*'), Some(10)); + /// + /// // Moving the key 'a' up will shift others down, so this moves *before* 10 to index 9. + /// assert_eq!(map.insert_before(10, 'a', ()), (9, Some(()))); + /// assert_eq!(map.get_index_of(&'a'), Some(9)); + /// assert_eq!(map.get_index_of(&'*'), Some(10)); + /// + /// // Moving the key 'z' down will shift others up, so this moves to exactly 10. + /// assert_eq!(map.insert_before(10, 'z', ()), (10, Some(()))); + /// assert_eq!(map.get_index_of(&'z'), Some(10)); + /// assert_eq!(map.get_index_of(&'*'), Some(11)); + /// + /// // Moving or inserting before the endpoint is also valid. + /// assert_eq!(map.len(), 27); + /// assert_eq!(map.insert_before(map.len(), '*', ()), (26, Some(()))); + /// assert_eq!(map.get_index_of(&'*'), Some(26)); + /// assert_eq!(map.insert_before(map.len(), '+', ()), (27, None)); + /// assert_eq!(map.get_index_of(&'+'), Some(27)); + /// assert_eq!(map.len(), 28); + /// ``` + #[track_caller] + pub fn insert_before(&mut self, mut index: usize, key: K, value: V) -> (usize, Option) { + let len = self.len(); + + assert!( + index <= len, + "index out of bounds: the len is {len} but the index is {index}. Expected index <= len" + ); + + match self.entry(key) { + Entry::Occupied(mut entry) => { + if index > entry.index() { + // Some entries will shift down when this one moves up, + // so "insert before index" becomes "move to index - 1", + // keeping the entry at the original index unmoved. + index -= 1; + } + let old = mem::replace(entry.get_mut(), value); + entry.move_index(index); + (index, Some(old)) + } + Entry::Vacant(entry) => { + entry.shift_insert(index, value); + (index, None) + } + } + } + + /// Insert a key-value pair in the map at the given index. + /// + /// If an equivalent key already exists in the map: the key remains and + /// is moved to the given index in the map, its corresponding value is updated + /// with `value`, and the older value is returned inside `Some(_)`. + /// Note that existing entries **cannot** be moved to `index == map.len()`! + /// (See [`insert_before`](Self::insert_before) for different behavior here.) + /// + /// If no equivalent key existed in the map: the new key-value pair is + /// inserted at the given index, and `None` is returned. + /// + /// ***Panics*** if `index` is out of bounds. + /// Valid indices are `0..map.len()` (exclusive) when moving an existing entry, or + /// `0..=map.len()` (inclusive) when inserting a new key. + /// + /// Computes in **O(n)** time (average). + /// + /// See also [`entry`][Self::entry] if you want to insert *or* modify, + /// perhaps only using the index for new entries with [`VacantEntry::shift_insert`]. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexMap; + /// let mut map: IndexMap = ('a'..='z').map(|c| (c, ())).collect(); + /// + /// // The new key '*' goes exactly at the given index. + /// assert_eq!(map.get_index_of(&'*'), None); + /// assert_eq!(map.shift_insert(10, '*', ()), None); + /// assert_eq!(map.get_index_of(&'*'), Some(10)); + /// + /// // Moving the key 'a' up to 10 will shift others down, including the '*' that was at 10. + /// assert_eq!(map.shift_insert(10, 'a', ()), Some(())); + /// assert_eq!(map.get_index_of(&'a'), Some(10)); + /// assert_eq!(map.get_index_of(&'*'), Some(9)); + /// + /// // Moving the key 'z' down to 9 will shift others up, including the '*' that was at 9. + /// assert_eq!(map.shift_insert(9, 'z', ()), Some(())); + /// assert_eq!(map.get_index_of(&'z'), Some(9)); + /// assert_eq!(map.get_index_of(&'*'), Some(10)); + /// + /// // Existing keys can move to len-1 at most, but new keys can insert at the endpoint. + /// assert_eq!(map.len(), 27); + /// assert_eq!(map.shift_insert(map.len() - 1, '*', ()), Some(())); + /// assert_eq!(map.get_index_of(&'*'), Some(26)); + /// assert_eq!(map.shift_insert(map.len(), '+', ()), None); + /// assert_eq!(map.get_index_of(&'+'), Some(27)); + /// assert_eq!(map.len(), 28); + /// ``` + /// + /// ```should_panic + /// use indexmap::IndexMap; + /// let mut map: IndexMap = ('a'..='z').map(|c| (c, ())).collect(); + /// + /// // This is an invalid index for moving an existing key! + /// map.shift_insert(map.len(), 'a', ()); + /// ``` + #[track_caller] + pub fn shift_insert(&mut self, index: usize, key: K, value: V) -> Option { + let len = self.len(); + match self.entry(key) { + Entry::Occupied(mut entry) => { + assert!( + index < len, + "index out of bounds: the len is {len} but the index is {index}" + ); + + let old = mem::replace(entry.get_mut(), value); + entry.move_index(index); + Some(old) + } + Entry::Vacant(entry) => { + assert!( + index <= len, + "index out of bounds: the len is {len} but the index is {index}. Expected index <= len" + ); + + entry.shift_insert(index, value); + None + } + } + } + /// Get the given key’s corresponding entry in the map for insertion and/or /// in-place manipulation. /// @@ -368,12 +615,91 @@ where self.core.entry(hash, key) } + /// Creates a splicing iterator that replaces the specified range in the map + /// with the given `replace_with` key-value iterator and yields the removed + /// items. `replace_with` does not need to be the same length as `range`. + /// + /// The `range` is removed even if the iterator is not consumed until the + /// end. It is unspecified how many elements are removed from the map if the + /// `Splice` value is leaked. + /// + /// The input iterator `replace_with` is only consumed when the `Splice` + /// value is dropped. If a key from the iterator matches an existing entry + /// in the map (outside of `range`), then the value will be updated in that + /// position. Otherwise, the new key-value pair will be inserted in the + /// replaced `range`. + /// + /// ***Panics*** if the starting point is greater than the end point or if + /// the end point is greater than the length of the map. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexMap; + /// + /// let mut map = IndexMap::from([(0, '_'), (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]); + /// let new = [(5, 'E'), (4, 'D'), (3, 'C'), (2, 'B'), (1, 'A')]; + /// let removed: Vec<_> = map.splice(2..4, new).collect(); + /// + /// // 1 and 4 got new values, while 5, 3, and 2 were newly inserted. + /// assert!(map.into_iter().eq([(0, '_'), (1, 'A'), (5, 'E'), (3, 'C'), (2, 'B'), (4, 'D')])); + /// assert_eq!(removed, &[(2, 'b'), (3, 'c')]); + /// ``` + #[track_caller] + pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter, K, V, S> + where + R: RangeBounds, + I: IntoIterator, + { + Splice::new(self, range, replace_with.into_iter()) + } + + /// Moves all key-value pairs from `other` into `self`, leaving `other` empty. + /// + /// This is equivalent to calling [`insert`][Self::insert] for each + /// key-value pair from `other` in order, which means that for keys that + /// already exist in `self`, their value is updated in the current position. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexMap; + /// + /// // Note: Key (3) is present in both maps. + /// let mut a = IndexMap::from([(3, "c"), (2, "b"), (1, "a")]); + /// let mut b = IndexMap::from([(3, "d"), (4, "e"), (5, "f")]); + /// let old_capacity = b.capacity(); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// assert_eq!(b.capacity(), old_capacity); + /// + /// assert!(a.keys().eq(&[3, 2, 1, 4, 5])); + /// assert_eq!(a[&3], "d"); // "c" was overwritten. + /// ``` + pub fn append(&mut self, other: &mut IndexMap) { + self.extend(other.drain(..)); + } +} + +impl IndexMap +where + S: BuildHasher, +{ + pub(crate) fn hash(&self, key: &Q) -> HashValue { + let mut h = self.hash_builder.build_hasher(); + key.hash(&mut h); + HashValue(h.finish() as usize) + } + /// Return `true` if an equivalent to `key` exists in the map. /// /// Computes in **O(1)** time (average). - pub fn contains_key(&self, key: &Q) -> bool + pub fn contains_key(&self, key: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.get_index_of(key).is_some() } @@ -382,9 +708,9 @@ where /// else `None`. /// /// Computes in **O(1)** time (average). - pub fn get(&self, key: &Q) -> Option<&V> + pub fn get(&self, key: &Q) -> Option<&V> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -398,9 +724,9 @@ where /// if it is present, else `None`. /// /// Computes in **O(1)** time (average). - pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -411,9 +737,9 @@ where } /// Return item index, key and value - pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> + pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -424,21 +750,25 @@ where } /// Return item index, if it exists in the map - pub fn get_index_of(&self, key: &Q) -> Option + /// + /// Computes in **O(1)** time (average). + pub fn get_index_of(&self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { - if self.is_empty() { - None - } else { - let hash = self.hash(key); - self.core.get_index_of(hash, key) + match self.as_entries() { + [] => None, + [x] => key.equivalent(&x.key).then_some(0), + _ => { + let hash = self.hash(key); + self.core.get_index_of(hash, key) + } } } - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &mut self.as_entries_mut()[i]; @@ -448,9 +778,9 @@ where } } - pub fn get_full_mut(&mut self, key: &Q) -> Option<(usize, &K, &mut V)> + pub fn get_full_mut(&mut self, key: &Q) -> Option<(usize, &K, &mut V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &mut self.as_entries_mut()[i]; @@ -460,46 +790,33 @@ where } } - pub(crate) fn get_full_mut2_impl( - &mut self, - key: &Q, - ) -> Option<(usize, &mut K, &mut V)> - where - Q: Hash + Equivalent, - { - if let Some(i) = self.get_index_of(key) { - let entry = &mut self.as_entries_mut()[i]; - Some((i, &mut entry.key, &mut entry.value)) - } else { - None - } - } - /// Remove the key-value pair equivalent to `key` and return /// its value. /// - /// **NOTE:** This is equivalent to `.swap_remove(key)`, if you need to - /// preserve the order of the keys in the map, use `.shift_remove(key)` - /// instead. - /// - /// Computes in **O(1)** time (average). - pub fn remove(&mut self, key: &Q) -> Option + /// **NOTE:** This is equivalent to [`.swap_remove(key)`][Self::swap_remove], replacing this + /// entry's position with the last element, and it is deprecated in favor of calling that + /// explicitly. If you need to preserve the relative order of the keys in the map, use + /// [`.shift_remove(key)`][Self::shift_remove] instead. + #[deprecated(note = "`remove` disrupts the map order -- \ + use `swap_remove` or `shift_remove` for explicit behavior.")] + pub fn remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove(key) } /// Remove and return the key-value pair equivalent to `key`. /// - /// **NOTE:** This is equivalent to `.swap_remove_entry(key)`, if you need to - /// preserve the order of the keys in the map, use `.shift_remove_entry(key)` - /// instead. - /// - /// Computes in **O(1)** time (average). - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + /// **NOTE:** This is equivalent to [`.swap_remove_entry(key)`][Self::swap_remove_entry], + /// replacing this entry's position with the last element, and it is deprecated in favor of + /// calling that explicitly. If you need to preserve the relative order of the keys in the map, + /// use [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead. + #[deprecated(note = "`remove_entry` disrupts the map order -- \ + use `swap_remove_entry` or `shift_remove_entry` for explicit behavior.")] + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove_entry(key) } @@ -507,32 +824,32 @@ where /// 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 + /// 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!** + /// the position 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 + pub fn swap_remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove_full(key).map(third) } /// Remove and return the key-value pair equivalent to `key`. /// - /// Like `Vec::swap_remove`, the pair is removed by swapping it with the + /// 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!** + /// the position 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_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.swap_remove_full(key) { Some((_, key, value)) => Some((key, value)), @@ -543,53 +860,59 @@ where /// Remove the key-value pair equivalent to `key` and return it and /// the index it had. /// - /// Like `Vec::swap_remove`, the pair is removed by swapping it with the + /// 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!** + /// the position 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_full(&mut self, key: &Q) -> Option<(usize, K, V)> + pub fn swap_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { - if self.is_empty() { - return None; + match self.as_entries() { + [x] if key.equivalent(&x.key) => { + let (k, v) = self.core.pop()?; + Some((0, k, v)) + } + [_] | [] => None, + _ => { + let hash = self.hash(key); + self.core.swap_remove_full(hash, key) + } } - let hash = self.hash(key); - self.core.swap_remove_full(hash, key) } /// Remove the key-value pair equivalent to `key` and return /// its value. /// - /// Like `Vec::remove`, the pair is removed by shifting all of the + /// Like [`Vec::remove`], the pair is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove(&mut self, key: &Q) -> Option + pub fn shift_remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.shift_remove_full(key).map(third) } /// Remove and return the key-value pair equivalent to `key`. /// - /// Like `Vec::remove`, the pair is removed by shifting all of the + /// Like [`Vec::remove`], the pair is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.shift_remove_full(key) { Some((_, key, value)) => Some((key, value)), @@ -600,27 +923,38 @@ where /// Remove the key-value pair equivalent to `key` and return it and /// the index it had. /// - /// Like `Vec::remove`, the pair is removed by shifting all of the + /// Like [`Vec::remove`], the pair is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> + pub fn shift_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { - if self.is_empty() { - return None; + match self.as_entries() { + [x] if key.equivalent(&x.key) => { + let (k, v) = self.core.pop()?; + Some((0, k, v)) + } + [_] | [] => None, + _ => { + let hash = self.hash(key); + self.core.shift_remove_full(hash, key) + } } - let hash = self.hash(key); - self.core.shift_remove_full(hash, key) } +} +impl IndexMap { /// Remove the last key-value pair /// + /// This preserves the order of the remaining elements. + /// /// Computes in **O(1)** time (average). + #[doc(alias = "pop_last")] // like `BTreeMap` pub fn pop(&mut self) -> Option<(K, V)> { self.core.pop() } @@ -639,27 +973,24 @@ where self.core.retain_in_order(move |k, v| keep(k, v)); } - pub(crate) fn retain_mut(&mut self, keep: F) - where - F: FnMut(&mut K, &mut V) -> bool, - { - self.core.retain_in_order(keep); - } - /// Sort the map’s key-value pairs by the default ordering of the keys. /// - /// See `sort_by` for details. + /// This is a stable sort -- but equivalent keys should not normally coexist in + /// a map at all, so [`sort_unstable_keys`][Self::sort_unstable_keys] is preferred + /// because it is generally faster and doesn't allocate auxiliary memory. + /// + /// See [`sort_by`](Self::sort_by) for details. pub fn sort_keys(&mut self) where K: Ord, { - self.with_entries(|entries| { - entries.sort_by(|a, b| Ord::cmp(&a.key, &b.key)); + self.with_entries(move |entries| { + entries.sort_by(move |a, b| K::cmp(&a.key, &b.key)); }); } /// Sort the map’s key-value pairs in place using the comparison - /// function `compare`. + /// function `cmp`. /// /// The comparison function receives two key and value pairs to compare (you /// can sort by keys or values or their combination as needed). @@ -675,7 +1006,7 @@ where }); } - /// Sort the key-value pairs of the map and return a by value iterator of + /// Sort the key-value pairs of the map and return a by-value iterator of /// the key-value pairs with the result. /// /// The sort is stable. @@ -685,9 +1016,126 @@ where { let mut entries = self.into_entries(); entries.sort_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); - IntoIter { - iter: entries.into_iter(), - } + IntoIter::new(entries) + } + + /// Sort the map's key-value pairs by the default ordering of the keys, but + /// may not preserve the order of equal elements. + /// + /// See [`sort_unstable_by`](Self::sort_unstable_by) for details. + pub fn sort_unstable_keys(&mut self) + where + K: Ord, + { + self.with_entries(move |entries| { + entries.sort_unstable_by(move |a, b| K::cmp(&a.key, &b.key)); + }); + } + + /// Sort the map's key-value pairs in place using the comparison function `cmp`, but + /// may not preserve the order of equal elements. + /// + /// The comparison function receives two key and value pairs to compare (you + /// can sort by keys or values or their combination as needed). + /// + /// Computes in **O(n log n + c)** time where *n* is + /// the length of the map and *c* is the capacity. The sort is unstable. + pub fn sort_unstable_by(&mut self, mut cmp: F) + where + F: FnMut(&K, &V, &K, &V) -> Ordering, + { + self.with_entries(move |entries| { + entries.sort_unstable_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); + }); + } + + /// Sort the key-value pairs of the map and return a by-value iterator of + /// the key-value pairs with the result. + /// + /// The sort is unstable. + #[inline] + pub fn sorted_unstable_by(self, mut cmp: F) -> IntoIter + where + F: FnMut(&K, &V, &K, &V) -> Ordering, + { + let mut entries = self.into_entries(); + entries.sort_unstable_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); + IntoIter::new(entries) + } + + /// Sort the map’s key-value pairs in place using a sort-key extraction function. + /// + /// During sorting, the function is called at most once per entry, by using temporary storage + /// to remember the results of its evaluation. The order of calls to the function is + /// unspecified and may change between versions of `indexmap` or the standard library. + /// + /// Computes in **O(m n + n log n + c)** time () and **O(n)** space, where the function is + /// **O(m)**, *n* is the length of the map, and *c* the capacity. The sort is stable. + pub fn sort_by_cached_key(&mut self, mut sort_key: F) + where + T: Ord, + F: FnMut(&K, &V) -> T, + { + self.with_entries(move |entries| { + entries.sort_by_cached_key(move |a| sort_key(&a.key, &a.value)); + }); + } + + /// Search over a sorted map for a key. + /// + /// Returns the position where that key is present, or the position where it can be inserted to + /// maintain the sort. See [`slice::binary_search`] for more details. + /// + /// Computes in **O(log(n))** time, which is notably less scalable than looking the key up + /// using [`get_index_of`][IndexMap::get_index_of], but this can also position missing keys. + pub fn binary_search_keys(&self, x: &K) -> Result + where + K: Ord, + { + self.as_slice().binary_search_keys(x) + } + + /// Search over a sorted map with a comparator function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result + where + F: FnMut(&'a K, &'a V) -> Ordering, + { + self.as_slice().binary_search_by(f) + } + + /// Search over a sorted map with an extraction function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by_key`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result + where + F: FnMut(&'a K, &'a V) -> B, + B: Ord, + { + self.as_slice().binary_search_by_key(b, f) + } + + /// Returns the index of the partition point of a sorted map according to the given predicate + /// (the index of the first element of the second partition). + /// + /// See [`slice::partition_point`] for more details. + /// + /// Computes in **O(log(n))** time. + #[must_use] + pub fn partition_point

(&self, pred: P) -> usize + where + P: FnMut(&K, &V) -> bool, + { + self.as_slice().partition_point(pred) } /// Reverses the order of the map’s key-value pairs in place. @@ -696,12 +1144,31 @@ where pub fn reverse(&mut self) { self.core.reverse() } -} -impl IndexMap { + /// Returns a slice of all the key-value pairs in the map. + /// + /// Computes in **O(1)** time. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } + + /// Returns a mutable slice of all the key-value pairs in the map. + /// + /// Computes in **O(1)** time. + pub fn as_mut_slice(&mut self) -> &mut Slice { + Slice::from_mut_slice(self.as_entries_mut()) + } + + /// Converts into a boxed slice of all the key-value pairs in the map. + /// + /// Note that this will drop the inner hash table and any excess capacity. + pub fn into_boxed_slice(self) -> Box> { + Slice::from_boxed(self.into_entries().into_boxed_slice()) + } + /// Get a key-value pair by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// /// Computes in **O(1)** time. pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { @@ -710,16 +1177,51 @@ impl IndexMap { /// Get a key-value pair by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { + self.as_entries_mut().get_mut(index).map(Bucket::ref_mut) + } + + /// Get an entry in the map by index for in-place manipulation. + /// + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + pub fn get_index_entry(&mut self, index: usize) -> Option> { + if index >= self.len() { + return None; + } + Some(IndexedEntry::new(&mut self.core, index)) + } + + /// Returns a slice of key-value pairs in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } + + /// Returns a mutable slice of key-value pairs in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. /// /// Computes in **O(1)** time. - pub fn get_index_mut(&mut self, index: usize) -> Option<(&mut K, &mut V)> { - self.as_entries_mut().get_mut(index).map(Bucket::muts) + pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Slice> { + let entries = self.as_entries_mut(); + let range = try_simplify_range(range, entries.len())?; + entries.get_mut(range).map(Slice::from_mut_slice) } /// Get the first key-value pair /// /// Computes in **O(1)** time. + #[doc(alias = "first_key_value")] // like `BTreeMap` pub fn first(&self) -> Option<(&K, &V)> { self.as_entries().first().map(Bucket::refs) } @@ -731,9 +1233,17 @@ impl IndexMap { self.as_entries_mut().first_mut().map(Bucket::ref_mut) } + /// Get the first entry in the map for in-place manipulation. + /// + /// Computes in **O(1)** time. + pub fn first_entry(&mut self) -> Option> { + self.get_index_entry(0) + } + /// Get the last key-value pair /// /// Computes in **O(1)** time. + #[doc(alias = "last_key_value")] // like `BTreeMap` pub fn last(&self) -> Option<(&K, &V)> { self.as_entries().last().map(Bucket::refs) } @@ -745,13 +1255,20 @@ impl IndexMap { self.as_entries_mut().last_mut().map(Bucket::ref_mut) } + /// Get the last entry in the map for in-place manipulation. + /// + /// Computes in **O(1)** time. + pub fn last_entry(&mut self) -> Option> { + self.get_index_entry(self.len().checked_sub(1)?) + } + /// Remove the key-value pair by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// - /// Like `Vec::swap_remove`, the pair is removed by swapping it with the + /// 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!** + /// the position of what used to be the last element!** /// /// Computes in **O(1)** time (average). pub fn swap_remove_index(&mut self, index: usize) -> Option<(K, V)> { @@ -760,9 +1277,9 @@ impl IndexMap { /// Remove the key-value pair by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// - /// Like `Vec::remove`, the pair is removed by shifting all of the + /// Like [`Vec::remove`], the pair is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// @@ -771,288 +1288,32 @@ 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). + #[track_caller] + 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. + /// + /// Computes in **O(1)** time (average). + #[track_caller] pub fn swap_indices(&mut self, a: usize, b: usize) { self.core.swap_indices(a, b) } } -/// An iterator over the keys of a `IndexMap`. -/// -/// This `struct` is created by the [`keys`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`keys`]: struct.IndexMap.html#method.keys -/// [`IndexMap`]: struct.IndexMap.html -pub struct Keys<'a, K, V> { - pub(crate) iter: SliceIter<'a, Bucket>, -} - -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - iterator_methods!(Bucket::key_ref); -} - -impl DoubleEndedIterator for Keys<'_, K, V> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::key_ref) - } -} - -impl ExactSizeIterator for Keys<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -impl Clone for Keys<'_, K, V> { - fn clone(&self) -> Self { - Keys { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for Keys<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// An iterator over the values of a `IndexMap`. -/// -/// This `struct` is created by the [`values`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`values`]: struct.IndexMap.html#method.values -/// [`IndexMap`]: struct.IndexMap.html -pub struct Values<'a, K, V> { - iter: SliceIter<'a, Bucket>, -} - -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - iterator_methods!(Bucket::value_ref); -} - -impl DoubleEndedIterator for Values<'_, K, V> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::value_ref) - } -} - -impl ExactSizeIterator for Values<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -impl Clone for Values<'_, K, V> { - fn clone(&self) -> Self { - Values { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for Values<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the values of a `IndexMap`. -/// -/// This `struct` is created by the [`values_mut`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`values_mut`]: struct.IndexMap.html#method.values_mut -/// [`IndexMap`]: struct.IndexMap.html -pub struct ValuesMut<'a, K, V> { - iter: SliceIterMut<'a, Bucket>, -} - -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - iterator_methods!(Bucket::value_mut); -} - -impl DoubleEndedIterator for ValuesMut<'_, K, V> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::value_mut) - } -} - -impl ExactSizeIterator for ValuesMut<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -/// An iterator over the entries of a `IndexMap`. -/// -/// This `struct` is created by the [`iter`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.IndexMap.html#method.iter -/// [`IndexMap`]: struct.IndexMap.html -pub struct Iter<'a, K, V> { - iter: SliceIter<'a, Bucket>, -} - -impl<'a, K, V> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - iterator_methods!(Bucket::refs); -} - -impl DoubleEndedIterator for Iter<'_, K, V> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::refs) - } -} - -impl ExactSizeIterator for Iter<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -impl Clone for Iter<'_, K, V> { - fn clone(&self) -> Self { - Iter { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for Iter<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the entries of a `IndexMap`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: struct.IndexMap.html#method.iter_mut -/// [`IndexMap`]: struct.IndexMap.html -pub struct IterMut<'a, K, V> { - iter: SliceIterMut<'a, Bucket>, -} - -impl<'a, K, V> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - iterator_methods!(Bucket::ref_mut); -} - -impl DoubleEndedIterator for IterMut<'_, K, V> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::ref_mut) - } -} - -impl ExactSizeIterator for IterMut<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -/// An owning iterator over the entries of a `IndexMap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`IndexMap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.IndexMap.html#method.into_iter -/// [`IndexMap`]: struct.IndexMap.html -pub struct IntoIter { - pub(crate) iter: vec::IntoIter>, -} - -impl Iterator for IntoIter { - type Item = (K, V); - - iterator_methods!(Bucket::key_value); -} - -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::key_value) - } -} - -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl fmt::Debug for IntoIter { - 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() - } -} - -/// A draining iterator over the entries of a `IndexMap`. -/// -/// This `struct` is created by the [`drain`] method on [`IndexMap`]. See its -/// documentation for more. -/// -/// [`drain`]: struct.IndexMap.html#method.drain -/// [`IndexMap`]: struct.IndexMap.html -pub struct Drain<'a, K, V> { - pub(crate) iter: vec::Drain<'a, Bucket>, -} - -impl Iterator for Drain<'_, K, V> { - type Item = (K, V); - - iterator_methods!(Bucket::key_value); -} - -impl DoubleEndedIterator for Drain<'_, K, V> { - double_ended_iterator_methods!(Bucket::key_value); -} - -impl<'a, K, V, S> IntoIterator for &'a IndexMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl<'a, K, V, S> IntoIterator for &'a mut IndexMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} - -impl IntoIterator for IndexMap { - type Item = (K, V); - type IntoIter = IntoIter; - fn into_iter(self) -> Self::IntoIter { - IntoIter { - iter: self.into_entries().into_iter(), - } - } -} - -/// Access `IndexMap` values corresponding to a key. +/// Access [`IndexMap`] values corresponding to a key. /// /// # Examples /// @@ -1077,7 +1338,6 @@ impl IntoIterator for IndexMap { impl Index<&Q> for IndexMap where Q: Hash + Equivalent, - K: Hash + Eq, S: BuildHasher, { type Output = V; @@ -1086,11 +1346,11 @@ where /// /// ***Panics*** if `key` is not present in the map. fn index(&self, key: &Q) -> &V { - self.get(key).expect("IndexMap: key not found") + self.get(key).expect("no entry found for key") } } -/// Access `IndexMap` values corresponding to a key. +/// Access [`IndexMap`] values corresponding to a key. /// /// Mutable indexing allows changing / updating values of key-value /// pairs that are already present. @@ -1122,18 +1382,21 @@ where impl IndexMut<&Q> for IndexMap where Q: Hash + Equivalent, - K: Hash + Eq, S: BuildHasher, { /// Returns a mutable reference to the value corresponding to the supplied `key`. /// /// ***Panics*** if `key` is not present in the map. fn index_mut(&mut self, key: &Q) -> &mut V { - self.get_mut(key).expect("IndexMap: key not found") + self.get_mut(key).expect("no entry found for key") } } -/// Access `IndexMap` values at indexed positions. +/// Access [`IndexMap`] values at indexed positions. +/// +/// See [`Index for Keys`][keys] to access a map's keys instead. +/// +/// [keys]: Keys#impl-Index-for-Keys<'a,+K,+V> /// /// # Examples /// @@ -1169,17 +1432,22 @@ impl Index for IndexMap { /// ***Panics*** if `index` is out of bounds. fn index(&self, index: usize) -> &V { self.get_index(index) - .expect("IndexMap: index out of bounds") + .unwrap_or_else(|| { + panic!( + "index out of bounds: the len is {len} but the index is {index}", + len = self.len() + ); + }) .1 } } -/// Access `IndexMap` values at indexed positions. +/// Access [`IndexMap`] values at indexed positions. /// /// Mutable indexing allows changing / updating indexed values /// that are already present. /// -/// You can **not** insert new values with index syntax, use `.insert()`. +/// You can **not** insert new values with index syntax -- use [`.insert()`][IndexMap::insert]. /// /// # Examples /// @@ -1208,8 +1476,12 @@ impl IndexMut for IndexMap { /// /// ***Panics*** if `index` is out of bounds. fn index_mut(&mut self, index: usize) -> &mut V { + let len: usize = self.len(); + self.get_index_mut(index) - .expect("IndexMap: index out of bounds") + .unwrap_or_else(|| { + panic!("index out of bounds: the len is {len} but the index is {index}"); + }) .1 } } @@ -1223,7 +1495,7 @@ where /// iterable. /// /// `from_iter` uses the same logic as `extend`. See - /// [`extend`](#method.extend) for more details. + /// [`extend`][IndexMap::extend] for more details. fn from_iter>(iterable: I) -> Self { let iter = iterable.into_iter(); let (low, _) = iter.size_hint(); @@ -1233,6 +1505,26 @@ where } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<[(K, V); N]> for IndexMap +where + K: Hash + Eq, +{ + /// # Examples + /// + /// ``` + /// use indexmap::IndexMap; + /// + /// let map1 = IndexMap::from([(1, 2), (3, 4)]); + /// let map2: IndexMap<_, _> = [(1, 2), (3, 4)].into(); + /// assert_eq!(map1, map2); + /// ``` + fn from(arr: [(K, V); N]) -> Self { + Self::from_iter(arr) + } +} + impl Extend<(K, V)> for IndexMap where K: Hash + Eq, @@ -1240,7 +1532,7 @@ where { /// Extend the map with all key-value pairs in the iterable. /// - /// This is equivalent to calling [`insert`](#method.insert) for each of + /// This is equivalent to calling [`insert`][IndexMap::insert] for each of /// them in order, which means that for keys that already existed /// in the map, their value is updated but it keeps the existing order. /// @@ -1284,7 +1576,7 @@ impl Default for IndexMap where S: Default, { - /// Return an empty `IndexMap` + /// Return an empty [`IndexMap`] fn default() -> Self { Self::with_capacity_and_hasher(0, S::default()) } @@ -1314,373 +1606,3 @@ where S: BuildHasher, { } - -#[cfg(test)] -mod tests { - use super::*; - use crate::util::enumerate; - use std::string::String; - - #[test] - fn it_works() { - let mut map = IndexMap::new(); - assert_eq!(map.is_empty(), true); - map.insert(1, ()); - map.insert(1, ()); - assert_eq!(map.len(), 1); - assert!(map.get(&1).is_some()); - assert_eq!(map.is_empty(), false); - } - - #[test] - fn new() { - let map = IndexMap::::new(); - println!("{:?}", map); - assert_eq!(map.capacity(), 0); - assert_eq!(map.len(), 0); - assert_eq!(map.is_empty(), true); - } - - #[test] - fn insert() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5]; - let not_present = [1, 3, 6, 9, 10]; - let mut map = IndexMap::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(map.len(), i); - map.insert(elt, elt); - assert_eq!(map.len(), i + 1); - assert_eq!(map.get(&elt), Some(&elt)); - assert_eq!(map[&elt], elt); - } - println!("{:?}", map); - - for &elt in ¬_present { - assert!(map.get(&elt).is_none()); - } - } - - #[test] - fn insert_full() { - let insert = vec![9, 2, 7, 1, 4, 6, 13]; - let present = vec![1, 6, 2]; - let mut map = IndexMap::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(map.len(), i); - let (index, existing) = map.insert_full(elt, elt); - assert_eq!(existing, None); - assert_eq!(Some(index), map.get_full(&elt).map(|x| x.0)); - assert_eq!(map.len(), i + 1); - } - - let len = map.len(); - for &elt in &present { - let (index, existing) = map.insert_full(elt, elt); - assert_eq!(existing, Some(elt)); - assert_eq!(Some(index), map.get_full(&elt).map(|x| x.0)); - assert_eq!(map.len(), len); - } - } - - #[test] - fn insert_2() { - let mut map = IndexMap::with_capacity(16); - - let mut keys = vec![]; - keys.extend(0..16); - keys.extend(128..267); - - for &i in &keys { - let old_map = map.clone(); - map.insert(i, ()); - for key in old_map.keys() { - if map.get(key).is_none() { - println!("old_map: {:?}", old_map); - println!("map: {:?}", map); - panic!("did not find {} in map", key); - } - } - } - - for &i in &keys { - assert!(map.get(&i).is_some(), "did not find {}", i); - } - } - - #[test] - fn insert_order() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut map = IndexMap::new(); - - for &elt in &insert { - map.insert(elt, ()); - } - - assert_eq!(map.keys().count(), map.len()); - assert_eq!(map.keys().count(), insert.len()); - for (a, b) in insert.iter().zip(map.keys()) { - assert_eq!(a, b); - } - for (i, k) in (0..insert.len()).zip(map.keys()) { - assert_eq!(map.get_index(i).unwrap().0, k); - } - } - - #[test] - fn grow() { - let insert = [0, 4, 2, 12, 8, 7, 11]; - let not_present = [1, 3, 6, 9, 10]; - let mut map = IndexMap::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(map.len(), i); - map.insert(elt, elt); - assert_eq!(map.len(), i + 1); - assert_eq!(map.get(&elt), Some(&elt)); - assert_eq!(map[&elt], elt); - } - - println!("{:?}", map); - for &elt in &insert { - map.insert(elt * 10, elt); - } - for &elt in &insert { - map.insert(elt * 100, elt); - } - for (i, &elt) in insert.iter().cycle().enumerate().take(100) { - map.insert(elt * 100 + i as i32, elt); - } - println!("{:?}", map); - for &elt in ¬_present { - assert!(map.get(&elt).is_none()); - } - } - - #[test] - fn reserve() { - let mut map = IndexMap::::new(); - assert_eq!(map.capacity(), 0); - map.reserve(100); - let capacity = map.capacity(); - assert!(capacity >= 100); - for i in 0..capacity { - assert_eq!(map.len(), i); - map.insert(i, i * i); - assert_eq!(map.len(), i + 1); - assert_eq!(map.capacity(), capacity); - assert_eq!(map.get(&i), Some(&(i * i))); - } - map.insert(capacity, std::usize::MAX); - assert_eq!(map.len(), capacity + 1); - assert!(map.capacity() > capacity); - assert_eq!(map.get(&capacity), Some(&std::usize::MAX)); - } - - #[test] - fn shrink_to_fit() { - let mut map = IndexMap::::new(); - assert_eq!(map.capacity(), 0); - for i in 0..100 { - assert_eq!(map.len(), i); - map.insert(i, i * i); - assert_eq!(map.len(), i + 1); - assert!(map.capacity() >= i + 1); - assert_eq!(map.get(&i), Some(&(i * i))); - map.shrink_to_fit(); - assert_eq!(map.len(), i + 1); - assert_eq!(map.capacity(), i + 1); - assert_eq!(map.get(&i), Some(&(i * i))); - } - } - - #[test] - fn remove() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut map = IndexMap::new(); - - for &elt in &insert { - map.insert(elt, elt); - } - - assert_eq!(map.keys().count(), map.len()); - assert_eq!(map.keys().count(), insert.len()); - for (a, b) in insert.iter().zip(map.keys()) { - assert_eq!(a, b); - } - - let remove_fail = [99, 77]; - let remove = [4, 12, 8, 7]; - - for &key in &remove_fail { - assert!(map.swap_remove_full(&key).is_none()); - } - println!("{:?}", map); - for &key in &remove { - //println!("{:?}", map); - let index = map.get_full(&key).unwrap().0; - assert_eq!(map.swap_remove_full(&key), Some((index, key, key))); - } - println!("{:?}", map); - - for key in &insert { - assert_eq!(map.get(key).is_some(), !remove.contains(key)); - } - assert_eq!(map.len(), insert.len() - remove.len()); - assert_eq!(map.keys().count(), insert.len() - remove.len()); - } - - #[test] - fn remove_to_empty() { - let mut map = indexmap! { 0 => 0, 4 => 4, 5 => 5 }; - map.swap_remove(&5).unwrap(); - map.swap_remove(&4).unwrap(); - map.swap_remove(&0).unwrap(); - assert!(map.is_empty()); - } - - #[test] - fn swap_remove_index() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut map = IndexMap::new(); - - for &elt in &insert { - map.insert(elt, elt * 2); - } - - let mut vector = insert.to_vec(); - let remove_sequence = &[3, 3, 10, 4, 5, 4, 3, 0, 1]; - - // check that the same swap remove sequence on vec and map - // have the same result. - for &rm in remove_sequence { - let out_vec = vector.swap_remove(rm); - let (out_map, _) = map.swap_remove_index(rm).unwrap(); - assert_eq!(out_vec, out_map); - } - assert_eq!(vector.len(), map.len()); - for (a, b) in vector.iter().zip(map.keys()) { - assert_eq!(a, b); - } - } - - #[test] - fn partial_eq_and_eq() { - let mut map_a = IndexMap::new(); - map_a.insert(1, "1"); - map_a.insert(2, "2"); - let mut map_b = map_a.clone(); - assert_eq!(map_a, map_b); - map_b.swap_remove(&1); - assert_ne!(map_a, map_b); - - let map_c: IndexMap<_, String> = map_b.into_iter().map(|(k, v)| (k, v.into())).collect(); - assert_ne!(map_a, map_c); - assert_ne!(map_c, map_a); - } - - #[test] - fn extend() { - let mut map = IndexMap::new(); - map.extend(vec![(&1, &2), (&3, &4)]); - map.extend(vec![(5, 6)]); - assert_eq!( - map.into_iter().collect::>(), - vec![(1, 2), (3, 4), (5, 6)] - ); - } - - #[test] - fn entry() { - let mut map = IndexMap::new(); - - map.insert(1, "1"); - map.insert(2, "2"); - { - let e = map.entry(3); - assert_eq!(e.index(), 2); - let e = e.or_insert("3"); - assert_eq!(e, &"3"); - } - - let e = map.entry(2); - assert_eq!(e.index(), 1); - assert_eq!(e.key(), &2); - match e { - Entry::Occupied(ref e) => assert_eq!(e.get(), &"2"), - Entry::Vacant(_) => panic!(), - } - assert_eq!(e.or_insert("4"), &"2"); - } - - #[test] - fn entry_and_modify() { - let mut map = IndexMap::new(); - - map.insert(1, "1"); - map.entry(1).and_modify(|x| *x = "2"); - assert_eq!(Some(&"2"), map.get(&1)); - - map.entry(2).and_modify(|x| *x = "doesn't exist"); - assert_eq!(None, map.get(&2)); - } - - #[test] - fn entry_or_default() { - let mut map = IndexMap::new(); - - #[derive(Debug, PartialEq)] - enum TestEnum { - DefaultValue, - NonDefaultValue, - } - - impl Default for TestEnum { - fn default() -> Self { - TestEnum::DefaultValue - } - } - - map.insert(1, TestEnum::NonDefaultValue); - assert_eq!(&mut TestEnum::NonDefaultValue, map.entry(1).or_default()); - - assert_eq!(&mut TestEnum::DefaultValue, map.entry(2).or_default()); - } - - #[test] - fn keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: IndexMap<_, _> = vec.into_iter().collect(); - let keys: Vec<_> = map.keys().cloned().collect(); - assert_eq!(keys.len(), 3); - assert!(keys.contains(&1)); - assert!(keys.contains(&2)); - assert!(keys.contains(&3)); - } - - #[test] - fn values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: IndexMap<_, _> = vec.into_iter().collect(); - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&'a')); - assert!(values.contains(&'b')); - assert!(values.contains(&'c')); - } - - #[test] - fn values_mut() { - let vec = vec![(1, 1), (2, 2), (3, 3)]; - let mut map: IndexMap<_, _> = vec.into_iter().collect(); - for value in map.values_mut() { - *value *= 2 - } - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&2)); - assert!(values.contains(&4)); - assert!(values.contains(&6)); - } -} diff --git a/src/map/core.rs b/src/map/core.rs index 02e99e07..9022c8ee 100644 --- a/src/map/core.rs +++ b/src/map/core.rs @@ -7,26 +7,43 @@ //! //! However, we should probably not let this show in the public API or docs. -mod raw; +mod entry; -use hashbrown::raw::RawTable; +pub mod raw_entry_v1; -use crate::vec::{Drain, Vec}; -use core::cmp; -use core::fmt; -use core::mem::replace; +use hashbrown::hash_table; + +use crate::vec::{self, Vec}; +use crate::TryReserveError; +use core::mem; use core::ops::RangeBounds; -use crate::equivalent::Equivalent; -use crate::util::{enumerate, simplify_range}; -use crate::{Bucket, Entries, HashValue}; +use crate::util::simplify_range; +use crate::{Bucket, Equivalent, HashValue}; + +type Indices = hash_table::HashTable; +type Entries = Vec>; + +pub use entry::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; /// Core of the map that does not depend on S +#[derive(Debug)] pub(crate) struct IndexMapCore { /// indices mapping from the entry hash to its index. - indices: RawTable, - /// entries is a dense vec of entries in their order. - entries: Vec>, + indices: Indices, + /// entries is a dense vec maintaining entry order. + entries: Entries, +} + +/// Mutable references to the parts of an `IndexMapCore`. +/// +/// When using `HashTable::find_entry`, that takes hold of `&mut indices`, so we have to borrow our +/// `&mut entries` separately, and there's no way to go back to a `&mut IndexMapCore`. So this type +/// is used to implement methods on the split references, and `IndexMapCore` can also call those to +/// avoid duplication. +struct RefMut<'a, K, V> { + indices: &'a mut Indices, + entries: &'a mut Entries, } #[inline(always)] @@ -43,55 +60,56 @@ 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); +fn erase_index(table: &mut Indices, hash: HashValue, index: usize) { + if let Ok(entry) = table.find_entry(hash.get(), move |&i| i == index) { + entry.remove(); + } else if cfg!(debug_assertions) { + panic!("index not found"); + } } #[inline] -fn update_index(table: &mut RawTable, hash: HashValue, old: usize, new: usize) { +fn update_index(table: &mut Indices, hash: HashValue, old: usize, new: usize) { let index = table - .get_mut(hash.get(), move |&i| i == old) + .find_mut(hash.get(), move |&i| i == old) .expect("index not found"); *index = new; } +/// Inserts many entries into the indices table without reallocating, +/// and without regard for duplication. +/// +/// ***Panics*** if there is not sufficient capacity already. +fn insert_bulk_no_grow(indices: &mut Indices, entries: &[Bucket]) { + assert!(indices.capacity() - indices.len() >= entries.len()); + for entry in entries { + indices.insert_unique(entry.hash.get(), indices.len(), |_| unreachable!()); + } +} + impl Clone for IndexMapCore where K: Clone, V: Clone, { fn clone(&self) -> Self { - let indices = self.indices.clone(); - let mut entries = Vec::with_capacity(indices.capacity()); - entries.clone_from(&self.entries); - IndexMapCore { indices, entries } + let mut new = Self::new(); + new.clone_from(self); + new } fn clone_from(&mut self, other: &Self) { - let hasher = get_hash(&other.entries); - self.indices.clone_from_with_hasher(&other.indices, hasher); + self.indices.clone_from(&other.indices); if self.entries.capacity() < other.entries.len() { - // If we must resize, match the indices capacity - self.reserve_entries(); + // If we must resize, match the indices capacity. + let additional = other.entries.len() - self.entries.len(); + self.borrow_mut().reserve_entries(additional); } self.entries.clone_from(&other.entries); } } -impl fmt::Debug for IndexMapCore -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("IndexMapCore") - .field("indices", &raw::DebugIndices(&self.indices)) - .field("entries", &self.entries) - .finish() - } -} - -impl Entries for IndexMapCore { +impl crate::Entries for IndexMapCore { type Entry = Bucket; #[inline] @@ -119,18 +137,26 @@ impl Entries for IndexMapCore { } impl IndexMapCore { + /// The maximum capacity before the `entries` allocation would exceed `isize::MAX`. + const MAX_ENTRIES_CAPACITY: usize = (isize::MAX as usize) / mem::size_of::>(); + #[inline] - pub(crate) fn new() -> Self { + pub(crate) const fn new() -> Self { IndexMapCore { - indices: RawTable::new(), + indices: Indices::new(), entries: Vec::new(), } } + #[inline] + fn borrow_mut(&mut self) -> RefMut<'_, K, V> { + RefMut::new(&mut self.indices, &mut self.entries) + } + #[inline] pub(crate) fn with_capacity(n: usize) -> Self { IndexMapCore { - indices: RawTable::with_capacity(n), + indices: Indices::with_capacity(n), entries: Vec::with_capacity(n), } } @@ -142,7 +168,7 @@ impl IndexMapCore { #[inline] pub(crate) fn capacity(&self) -> usize { - cmp::min(self.indices.capacity(), self.entries.capacity()) + Ord::min(self.indices.capacity(), self.entries.capacity()) } pub(crate) fn clear(&mut self) { @@ -157,7 +183,8 @@ impl IndexMapCore { } } - pub(crate) fn drain(&mut self, range: R) -> Drain<'_, Bucket> + #[track_caller] + pub(crate) fn drain(&mut self, range: R) -> vec::Drain<'_, Bucket> where R: RangeBounds, { @@ -166,34 +193,115 @@ impl IndexMapCore { self.entries.drain(range) } + #[cfg(feature = "rayon")] + pub(crate) fn par_drain(&mut self, range: R) -> rayon::vec::Drain<'_, Bucket> + where + K: Send, + V: Send, + R: RangeBounds, + { + use rayon::iter::ParallelDrainRange; + let range = simplify_range(range, self.entries.len()); + self.erase_indices(range.start, range.end); + self.entries.par_drain(range) + } + + #[track_caller] pub(crate) fn split_off(&mut self, at: usize) -> Self { - assert!(at <= self.entries.len()); + let len = self.entries.len(); + assert!( + at <= len, + "index out of bounds: the len is {len} but the index is {at}. Expected index <= len" + ); + self.erase_indices(at, self.entries.len()); 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); - } + let mut indices = Indices::with_capacity(entries.len()); + insert_bulk_no_grow(&mut indices, &entries); Self { indices, entries } } + #[track_caller] + pub(crate) fn split_splice(&mut self, range: R) -> (Self, vec::IntoIter>) + where + R: RangeBounds, + { + let range = simplify_range(range, self.len()); + self.erase_indices(range.start, self.entries.len()); + let entries = self.entries.split_off(range.end); + let drained = self.entries.split_off(range.start); + + let mut indices = Indices::with_capacity(entries.len()); + insert_bulk_no_grow(&mut indices, &entries); + (Self { indices, entries }, drained.into_iter()) + } + + /// Append from another map without checking whether items already exist. + pub(crate) fn append_unchecked(&mut self, other: &mut Self) { + self.reserve(other.len()); + insert_bulk_no_grow(&mut self.indices, &other.entries); + self.entries.append(&mut other.entries); + other.indices.clear(); + } + /// Reserve capacity for `additional` more key-value pairs. pub(crate) fn reserve(&mut self, additional: usize) { self.indices.reserve(additional, get_hash(&self.entries)); - self.reserve_entries(); + // Only grow entries if necessary, since we also round up capacity. + if additional > self.entries.capacity() - self.entries.len() { + self.borrow_mut().reserve_entries(additional); + } } - /// Reserve entries capacity to match the indices - fn reserve_entries(&mut self) { - let additional = self.indices.capacity() - self.entries.len(); + /// Reserve capacity for `additional` more key-value pairs, without over-allocating. + pub(crate) fn reserve_exact(&mut self, additional: usize) { + self.indices.reserve(additional, get_hash(&self.entries)); 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(); + /// Try to reserve capacity for `additional` more key-value pairs. + pub(crate) fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.indices + .try_reserve(additional, get_hash(&self.entries)) + .map_err(TryReserveError::from_hashbrown)?; + // Only grow entries if necessary, since we also round up capacity. + if additional > self.entries.capacity() - self.entries.len() { + self.try_reserve_entries(additional) + } else { + Ok(()) + } + } + + /// Try to reserve entries capacity, rounded up to match the indices + fn try_reserve_entries(&mut self, additional: usize) -> Result<(), TryReserveError> { + // Use a soft-limit on the maximum capacity, but if the caller explicitly + // requested more, do it and let them have the resulting error. + let new_capacity = Ord::min(self.indices.capacity(), Self::MAX_ENTRIES_CAPACITY); + let try_add = new_capacity - self.entries.len(); + if try_add > additional && self.entries.try_reserve_exact(try_add).is_ok() { + return Ok(()); + } + self.entries + .try_reserve_exact(additional) + .map_err(TryReserveError::from_alloc) + } + + /// Try to reserve capacity for `additional` more key-value pairs, without over-allocating. + pub(crate) fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.indices + .try_reserve(additional, get_hash(&self.entries)) + .map_err(TryReserveError::from_hashbrown)?; + self.entries + .try_reserve_exact(additional) + .map_err(TryReserveError::from_alloc) + } + + /// 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 @@ -207,36 +315,76 @@ impl IndexMapCore { } } - /// Append a key-value pair, *without* checking whether it already exists, - /// and return the pair's new index. - fn push(&mut self, hash: HashValue, key: K, value: V) -> usize { - let i = self.entries.len(); - self.indices.insert(hash.get(), i, get_hash(&self.entries)); - if i == self.entries.capacity() { + /// Return the index in `entries` where an equivalent key can be found + pub(crate) fn get_index_of(&self, hash: HashValue, key: &Q) -> Option + where + Q: ?Sized + Equivalent, + { + let eq = equivalent(key, &self.entries); + self.indices.find(hash.get(), eq).copied() + } + + /// Append a key-value pair to `entries`, + /// *without* checking whether it already exists. + fn push_entry(&mut self, hash: HashValue, key: K, value: V) { + if self.entries.len() == self.entries.capacity() { // Reserve our own capacity synced to the indices, // rather than letting `Vec::push` just double it. - self.reserve_entries(); + self.borrow_mut().reserve_entries(1); } self.entries.push(Bucket { hash, key, value }); - i } - /// Return the index in `entries` where an equivalent key can be found - pub(crate) fn get_index_of(&self, hash: HashValue, key: &Q) -> Option + pub(crate) fn insert_full(&mut self, hash: HashValue, key: K, value: V) -> (usize, Option) where - Q: ?Sized + Equivalent, + K: Eq, { - let eq = equivalent(key, &self.entries); - self.indices.get(hash.get(), eq).copied() + let eq = equivalent(&key, &self.entries); + let hasher = get_hash(&self.entries); + match self.indices.entry(hash.get(), eq, hasher) { + hash_table::Entry::Occupied(entry) => { + let i = *entry.get(); + (i, Some(mem::replace(&mut self.entries[i].value, value))) + } + hash_table::Entry::Vacant(entry) => { + let i = self.entries.len(); + entry.insert(i); + self.push_entry(hash, key, value); + debug_assert_eq!(self.indices.len(), self.entries.len()); + (i, None) + } + } } - pub(crate) fn insert_full(&mut self, hash: HashValue, key: K, value: V) -> (usize, Option) + /// Same as `insert_full`, except it also replaces the key + pub(crate) fn replace_full( + &mut self, + hash: HashValue, + key: K, + value: V, + ) -> (usize, Option<(K, V)>) where K: Eq, { - match self.get_index_of(hash, &key) { - Some(i) => (i, Some(replace(&mut self.entries[i].value, value))), - None => (self.push(hash, key, value), None), + let eq = equivalent(&key, &self.entries); + let hasher = get_hash(&self.entries); + match self.indices.entry(hash.get(), eq, hasher) { + hash_table::Entry::Occupied(entry) => { + let i = *entry.get(); + let entry = &mut self.entries[i]; + let kv = ( + mem::replace(&mut entry.key, key), + mem::replace(&mut entry.value, value), + ); + (i, Some(kv)) + } + hash_table::Entry::Vacant(entry) => { + let i = self.entries.len(); + entry.insert(i); + self.push_entry(hash, key, value); + debug_assert_eq!(self.indices.len(), self.entries.len()); + (i, None) + } } } @@ -246,53 +394,32 @@ impl IndexMapCore { Q: ?Sized + Equivalent, { let eq = equivalent(key, &self.entries); - match self.indices.remove_entry(hash.get(), eq) { - Some(index) => { - let (key, value) = self.shift_remove_finish(index); + match self.indices.find_entry(hash.get(), eq) { + Ok(entry) => { + let (index, _) = entry.remove(); + let (key, value) = self.borrow_mut().shift_remove_finish(index); Some((index, key, value)) } - None => None, + Err(_) => None, } } /// Remove an entry by shifting all entries that follow it + #[inline] pub(crate) fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> { - match self.entries.get(index) { - Some(entry) => { - erase_index(&mut self.indices, entry.hash, index); - Some(self.shift_remove_finish(index)) - } - None => None, - } + self.borrow_mut().shift_remove_index(index) } - /// Remove an entry by shifting all entries that follow it - /// - /// 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 - let entry = self.entries.remove(index); - - // 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` - for i in self.indices_mut() { - if *i > index { - *i -= 1; - } - } - } else { - // find each following entry to shift its index - for (i, entry) in (index + 1..).zip(shifted_entries) { - update_index(&mut self.indices, entry.hash, i, i - 1); - } - } + #[inline] + #[track_caller] + pub(super) fn move_index(&mut self, from: usize, to: usize) { + self.borrow_mut().move_index(from, to); + } - (entry.key, entry.value) + #[inline] + #[track_caller] + pub(crate) fn swap_indices(&mut self, a: usize, b: usize) { + self.borrow_mut().swap_indices(a, b); } /// Remove an entry by swapping it with the last @@ -301,43 +428,20 @@ impl IndexMapCore { Q: ?Sized + Equivalent, { let eq = equivalent(key, &self.entries); - match self.indices.remove_entry(hash.get(), eq) { - Some(index) => { - let (key, value) = self.swap_remove_finish(index); + match self.indices.find_entry(hash.get(), eq) { + Ok(entry) => { + let (index, _) = entry.remove(); + let (key, value) = self.borrow_mut().swap_remove_finish(index); Some((index, key, value)) } - None => None, + Err(_) => None, } } /// Remove an entry by swapping it with the last + #[inline] pub(crate) fn swap_remove_index(&mut self, index: usize) -> Option<(K, V)> { - match self.entries.get(index) { - Some(entry) => { - erase_index(&mut self.indices, entry.hash, index); - Some(self.swap_remove_finish(index)) - } - None => None, - } - } - - /// Finish removing an entry by swapping it with the last - /// - /// The index should already be removed from `self.indices`. - fn swap_remove_finish(&mut self, index: usize) -> (K, V) { - // use swap_remove, but then we need to update the index that points - // to the other entry that has to move - let entry = self.entries.swap_remove(index); - - // correct index that points to the entry that had to swap places - if let Some(entry) = self.entries.get(index) { - // was not last element - // examine new element in `index` and find it in indices - let last = self.entries.len(); - update_index(&mut self.indices, entry.hash, last, index); - } - - (entry.key, entry.value) + self.borrow_mut().swap_remove_index(index) } /// Erase `start..end` from `indices`, and shift `end..` indices down to `start..` @@ -350,7 +454,7 @@ impl IndexMapCore { let erased = erased_entries.len(); let shifted = shifted_entries.len(); - let half_capacity = self.indices.buckets() / 2; + let half_capacity = self.indices.capacity() / 2; // Use a heuristic between different strategies if erased == 0 { @@ -359,15 +463,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 + insert_bulk_no_grow(&mut self.indices, start_entries); + 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 @@ -382,7 +480,15 @@ impl IndexMapCore { } } else { // Sweep the whole table for adjustments - self.erase_indices_sweep(start, end); + let offset = end - start; + self.indices.retain(move |i| { + if *i >= end { + *i -= offset; + true + } else { + *i < start + } + }); } debug_assert_eq!(self.indices.len(), start + shifted); @@ -392,35 +498,16 @@ impl IndexMapCore { where F: FnMut(&mut K, &mut V) -> bool, { - // 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. - let len = self.entries.len(); - let mut n_deleted = 0; - for i in 0..len { - let will_keep = { - let entry = &mut self.entries[i]; - keep(&mut entry.key, &mut entry.value) - }; - if !will_keep { - n_deleted += 1; - } else if n_deleted > 0 { - self.entries.swap(i - n_deleted, i); - } - } - if n_deleted > 0 { - self.entries.truncate(len - n_deleted); + self.entries + .retain_mut(|entry| keep(&mut entry.key, &mut entry.value)); + if self.entries.len() < self.indices.len() { self.rebuild_hash_table(); } } 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); - } + insert_bulk_no_grow(&mut self.indices, &self.entries); } pub(crate) fn reverse(&mut self) { @@ -429,184 +516,215 @@ impl IndexMapCore { // No need to save hash indices, can easily calculate what they should // be, given that this is an in-place reversal. let len = self.entries.len(); - for i in self.indices_mut() { + for i in &mut self.indices { *i = len - *i - 1; } } } -/// Entry for an existing key-value pair or a vacant location to -/// insert one. -pub enum Entry<'a, K, V> { - /// Existing slot with equivalent key. - Occupied(OccupiedEntry<'a, K, V>), - /// Vacant slot (no equivalent key in the map). - Vacant(VacantEntry<'a, K, V>), -} - -impl<'a, K, V> Entry<'a, K, V> { - /// Computes in **O(1)** time (amortized average). - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(default), - } +/// Reserve entries capacity, rounded up to match the indices (via `try_capacity`). +fn reserve_entries(entries: &mut Entries, additional: usize, try_capacity: usize) { + // Use a soft-limit on the maximum capacity, but if the caller explicitly + // requested more, do it and let them have the resulting panic. + let try_capacity = try_capacity.min(IndexMapCore::::MAX_ENTRIES_CAPACITY); + let try_add = try_capacity - entries.len(); + if try_add > additional && entries.try_reserve_exact(try_add).is_ok() { + return; } + entries.reserve_exact(additional); +} - /// Computes in **O(1)** time (amortized average). - pub fn or_insert_with(self, call: F) -> &'a mut V - where - F: FnOnce() -> V, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(call()), - } +impl<'a, K, V> RefMut<'a, K, V> { + #[inline] + fn new(indices: &'a mut Indices, entries: &'a mut Entries) -> Self { + Self { indices, entries } } - pub fn key(&self) -> &K { - match *self { - Entry::Occupied(ref entry) => entry.key(), - Entry::Vacant(ref entry) => entry.key(), + /// Reserve entries capacity, rounded up to match the indices + #[inline] + fn reserve_entries(&mut self, additional: usize) { + reserve_entries(self.entries, additional, self.indices.capacity()); + } + + /// Insert a key-value pair in `entries`, + /// *without* checking whether it already exists. + fn insert_unique(self, hash: HashValue, key: K, value: V) -> OccupiedEntry<'a, K, V> { + let i = self.indices.len(); + debug_assert_eq!(i, self.entries.len()); + let entry = self + .indices + .insert_unique(hash.get(), i, get_hash(self.entries)); + if self.entries.len() == self.entries.capacity() { + // We can't call `indices.capacity()` while this `entry` has borrowed it, so we'll have + // to amortize growth on our own. It's still an improvement over the basic `Vec::push` + // doubling though, since we also consider `MAX_ENTRIES_CAPACITY`. + reserve_entries(self.entries, 1, 2 * self.entries.capacity()); } - } - - /// Return the index where the key-value pair exists or will be inserted. - pub fn index(&self) -> usize { - match *self { - Entry::Occupied(ref entry) => entry.index(), - Entry::Vacant(ref entry) => entry.index(), + self.entries.push(Bucket { hash, key, value }); + OccupiedEntry::new(self.entries, entry) + } + + /// Insert a key-value pair in `entries` at a particular index, + /// *without* checking whether it already exists. + fn shift_insert_unique(&mut self, index: usize, hash: HashValue, key: K, value: V) { + let end = self.indices.len(); + assert!(index <= end); + // Increment others first so we don't have duplicate indices. + self.increment_indices(index, end); + let entries = &*self.entries; + self.indices.insert_unique(hash.get(), index, move |&i| { + // Adjust for the incremented indices to find hashes. + debug_assert_ne!(i, index); + let i = if i < index { i } else { i - 1 }; + entries[i].hash.get() + }); + if self.entries.len() == self.entries.capacity() { + // Reserve our own capacity synced to the indices, + // rather than letting `Vec::insert` just double it. + self.reserve_entries(1); } + self.entries.insert(index, Bucket { hash, key, value }); } - /// Modifies the entry if it is occupied. - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Entry::Occupied(mut o) => { - f(o.get_mut()); - Entry::Occupied(o) + /// Remove an entry by shifting all entries that follow it + fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> { + match self.entries.get(index) { + Some(entry) => { + erase_index(self.indices, entry.hash, index); + Some(self.shift_remove_finish(index)) } - x => x, + None => None, } } - /// Inserts a default-constructed value in the entry if it is vacant and returns a mutable - /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// Remove an entry by shifting all entries that follow it /// - /// Computes in **O(1)** time (amortized average). - pub fn or_default(self) -> &'a mut V - where - V: Default, - { - match self { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(V::default()), - } - } -} + /// The index should already be removed from `self.indices`. + fn shift_remove_finish(&mut self, index: usize) -> (K, V) { + // Correct indices that point to the entries that followed the removed entry. + self.decrement_indices(index + 1, self.entries.len()); -impl fmt::Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Entry::Vacant(ref v) => f.debug_tuple(stringify!(Entry)).field(v).finish(), - Entry::Occupied(ref o) => f.debug_tuple(stringify!(Entry)).field(o).finish(), - } + // Use Vec::remove to actually remove the entry. + let entry = self.entries.remove(index); + (entry.key, entry.value) } -} -pub use self::raw::OccupiedEntry; - -// Extra methods that don't threaten the unsafe encapsulation. -impl OccupiedEntry<'_, K, V> { - /// Sets the value of the entry to `value`, and returns the entry's old value. - pub fn insert(&mut self, value: V) -> V { - replace(self.get_mut(), value) + /// Remove an entry by swapping it with the last + fn swap_remove_index(&mut self, index: usize) -> Option<(K, V)> { + match self.entries.get(index) { + Some(entry) => { + erase_index(self.indices, entry.hash, index); + Some(self.swap_remove_finish(index)) + } + None => None, + } } - /// Remove the key, value pair stored in the map for this entry, and return the value. + /// Finish removing an entry by swapping it with the last /// - /// **NOTE:** This is equivalent to `.swap_remove()`. - pub fn remove(self) -> V { - self.swap_remove() - } + /// The index should already be removed from `self.indices`. + fn swap_remove_finish(&mut self, index: usize) -> (K, V) { + // use swap_remove, but then we need to update the index that points + // to the other entry that has to move + let entry = self.entries.swap_remove(index); - /// Remove the key, value pair stored in the map for this entry, and return the 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!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove(self) -> V { - self.swap_remove_entry().1 - } + // correct index that points to the entry that had to swap places + if let Some(entry) = self.entries.get(index) { + // was not last element + // examine new element in `index` and find it in indices + let last = self.entries.len(); + update_index(self.indices, entry.hash, last, index); + } - /// Remove the key, value pair stored in the map for this entry, and return the value. - /// - /// Like `Vec::remove`, the pair is removed by shifting all of the - /// elements that follow it, preserving their relative order. - /// **This perturbs the index of all of those elements!** - /// - /// Computes in **O(n)** time (average). - pub fn shift_remove(self) -> V { - self.shift_remove_entry().1 + (entry.key, entry.value) } - /// Remove and return the key, value pair stored in the map for this entry + /// Decrement all indices in the range `start..end`. /// - /// **NOTE:** This is equivalent to `.swap_remove_entry()`. - pub fn remove_entry(self) -> (K, V) { - self.swap_remove_entry() - } -} - -impl fmt::Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(stringify!(OccupiedEntry)) - .field("key", self.key()) - .field("value", self.get()) - .finish() + /// 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.capacity() / 2 { + // Shift all indices in range. + for i in &mut *self.indices { + if start <= *i && *i < end { + *i -= 1; + } + } + } else { + // Find each entry in range to shift its index. + for (i, entry) in (start..end).zip(shifted_entries) { + update_index(self.indices, entry.hash, i, i - 1); + } + } } -} -/// A view into a vacant entry in a `IndexMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -pub struct VacantEntry<'a, K, V> { - map: &'a mut IndexMapCore, - hash: HashValue, - key: K, -} - -impl<'a, K, V> VacantEntry<'a, K, V> { - pub fn key(&self) -> &K { - &self.key + /// 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.capacity() / 2 { + // Shift all indices in range. + for i in &mut *self.indices { + 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(self.indices, entry.hash, i, i + 1); + } + } } - pub fn into_key(self) -> K { - self.key - } + #[track_caller] + fn move_index(&mut self, from: usize, to: usize) { + let from_hash = self.entries[from].hash; + let _ = self.entries[to]; // explicit bounds check + if from != to { + // Use a sentinel index so other indices don't collide. + update_index(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); + } - /// Return the index where the key-value pair will be inserted. - pub fn index(&self) -> usize { - self.map.len() + // Change the sentinel index to its final position. + update_index(self.indices, from_hash, usize::MAX, to); + } } - pub fn insert(self, value: V) -> &'a mut V { - let i = self.map.push(self.hash, self.key, value); - &mut self.map.entries[i].value - } -} + #[track_caller] + fn swap_indices(&mut self, a: usize, b: usize) { + // If they're equal and in-bounds, there's nothing to do. + if a == b && a < self.entries.len() { + return; + } -impl fmt::Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple(stringify!(VacantEntry)) - .field(self.key()) - .finish() + // We'll get a "nice" bounds-check from indexing `entries`, + // and then we expect to find it in the table as well. + match self.indices.get_many_mut( + [self.entries[a].hash.get(), self.entries[b].hash.get()], + move |i, &x| if i == 0 { x == a } else { x == b }, + ) { + [Some(ref_a), Some(ref_b)] => { + mem::swap(ref_a, ref_b); + self.entries.swap(a, b); + } + _ => panic!("indices not found"), + } } } @@ -615,4 +733,6 @@ fn assert_send_sync() { fn assert_send_sync() {} assert_send_sync::>(); assert_send_sync::>(); + assert_send_sync::>(); + assert_send_sync::>(); } diff --git a/src/map/core/entry.rs b/src/map/core/entry.rs new file mode 100644 index 00000000..6ab29ca5 --- /dev/null +++ b/src/map/core/entry.rs @@ -0,0 +1,571 @@ +use super::{equivalent, Entries, IndexMapCore, RefMut}; +use crate::HashValue; +use core::{fmt, mem}; +use hashbrown::hash_table; + +impl IndexMapCore { + pub(crate) fn entry(&mut self, hash: HashValue, key: K) -> Entry<'_, K, V> + where + K: Eq, + { + let entries = &mut self.entries; + let eq = equivalent(&key, entries); + match self.indices.find_entry(hash.get(), eq) { + Ok(index) => Entry::Occupied(OccupiedEntry { entries, index }), + Err(absent) => Entry::Vacant(VacantEntry { + map: RefMut::new(absent.into_table(), entries), + hash, + key, + }), + } + } +} + +/// Entry for an existing key-value pair in an [`IndexMap`][crate::IndexMap] +/// or a vacant location to insert one. +pub enum Entry<'a, K, V> { + /// Existing slot with equivalent key. + Occupied(OccupiedEntry<'a, K, V>), + /// Vacant slot (no equivalent key in the map). + Vacant(VacantEntry<'a, K, V>), +} + +impl<'a, K, V> Entry<'a, K, V> { + /// Return the index where the key-value pair exists or will be inserted. + pub fn index(&self) -> usize { + match *self { + Entry::Occupied(ref entry) => entry.index(), + Entry::Vacant(ref entry) => entry.index(), + } + } + + /// Sets the value of the entry (after inserting if vacant), and returns an `OccupiedEntry`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + match self { + Entry::Occupied(mut entry) => { + entry.insert(value); + entry + } + Entry::Vacant(entry) => entry.insert_entry(value), + } + } + + /// Inserts the given default value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default), + } + } + + /// Inserts the result of the `call` function in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_insert_with(self, call: F) -> &'a mut V + where + F: FnOnce() -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(call()), + } + } + + /// Inserts the result of the `call` function with a reference to the entry's key if it is + /// vacant, and returns a mutable reference to the new value. Otherwise a mutable reference to + /// an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_insert_with_key(self, call: F) -> &'a mut V + where + F: FnOnce(&K) -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let value = call(&entry.key); + entry.insert(value) + } + } + } + + /// Gets a reference to the entry's key, either within the map if occupied, + /// or else the new key that was used to find the entry. + pub fn key(&self) -> &K { + match *self { + Entry::Occupied(ref entry) => entry.key(), + Entry::Vacant(ref entry) => entry.key(), + } + } + + /// Modifies the entry if it is occupied. + pub fn and_modify(mut self, f: F) -> Self + where + F: FnOnce(&mut V), + { + if let Entry::Occupied(entry) = &mut self { + f(entry.get_mut()); + } + self + } + + /// Inserts a default-constructed value in the entry if it is vacant and returns a mutable + /// reference to it. Otherwise a mutable reference to an already existent value is returned. + /// + /// Computes in **O(1)** time (amortized average). + pub fn or_default(self) -> &'a mut V + where + V: Default, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(V::default()), + } + } +} + +impl fmt::Debug for Entry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut tuple = f.debug_tuple("Entry"); + match self { + Entry::Vacant(v) => tuple.field(v), + Entry::Occupied(o) => tuple.field(o), + }; + tuple.finish() + } +} + +/// A view into an occupied entry in an [`IndexMap`][crate::IndexMap]. +/// It is part of the [`Entry`] enum. +pub struct OccupiedEntry<'a, K, V> { + entries: &'a mut Entries, + index: hash_table::OccupiedEntry<'a, usize>, +} + +impl<'a, K, V> OccupiedEntry<'a, K, V> { + pub(crate) fn new( + entries: &'a mut Entries, + index: hash_table::OccupiedEntry<'a, usize>, + ) -> Self { + Self { entries, index } + } + + /// Return the index of the key-value pair + #[inline] + pub fn index(&self) -> usize { + *self.index.get() + } + + #[inline] + fn into_ref_mut(self) -> RefMut<'a, K, V> { + RefMut::new(self.index.into_table(), self.entries) + } + + /// Gets a reference to the entry's key in the map. + /// + /// Note that this is not the key that was used to find the entry. There may be an observable + /// difference if the key type has any distinguishing features outside of `Hash` and `Eq`, like + /// extra fields or the memory address of an allocation. + pub fn key(&self) -> &K { + &self.entries[self.index()].key + } + + pub(crate) fn key_mut(&mut self) -> &mut K { + let index = self.index(); + &mut self.entries[index].key + } + + /// Gets a reference to the entry's value in the map. + pub fn get(&self) -> &V { + &self.entries[self.index()].value + } + + /// Gets a mutable reference to the entry's value in the map. + /// + /// If you need a reference which may outlive the destruction of the + /// [`Entry`] value, see [`into_mut`][Self::into_mut]. + pub fn get_mut(&mut self) -> &mut V { + let index = self.index(); + &mut self.entries[index].value + } + + /// Converts into a mutable reference to the entry's value in the map, + /// with a lifetime bound to the map itself. + pub fn into_mut(self) -> &'a mut V { + let index = self.index(); + &mut self.entries[index].value + } + + pub(super) fn into_muts(self) -> (&'a mut K, &'a mut V) { + let index = self.index(); + self.entries[index].muts() + } + + /// Sets the value of the entry to `value`, and returns the entry's old value. + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// **NOTE:** This is equivalent to [`.swap_remove()`][Self::swap_remove], replacing this + /// entry's position with the last element, and it is deprecated in favor of calling that + /// explicitly. If you need to preserve the relative order of the keys in the map, use + /// [`.shift_remove()`][Self::shift_remove] instead. + #[deprecated(note = "`remove` disrupts the map order -- \ + use `swap_remove` or `shift_remove` for explicit behavior.")] + pub fn remove(self) -> V { + self.swap_remove() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove(self) -> V { + self.swap_remove_entry().1 + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove(self) -> V { + self.shift_remove_entry().1 + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// **NOTE:** This is equivalent to [`.swap_remove_entry()`][Self::swap_remove_entry], + /// replacing this entry's position with the last element, and it is deprecated in favor of + /// calling that explicitly. If you need to preserve the relative order of the keys in the map, + /// use [`.shift_remove_entry()`][Self::shift_remove_entry] instead. + #[deprecated(note = "`remove_entry` disrupts the map order -- \ + use `swap_remove_entry` or `shift_remove_entry` for explicit behavior.")] + pub fn remove_entry(self) -> (K, V) { + self.swap_remove_entry() + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_entry(self) -> (K, V) { + let (index, entry) = self.index.remove(); + RefMut::new(entry.into_table(), self.entries).swap_remove_finish(index) + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_entry(self) -> (K, V) { + let (index, entry) = self.index.remove(); + RefMut::new(entry.into_table(), self.entries).shift_remove_finish(index) + } + + /// Moves the position of the entry to a new index + /// by shifting all other entries in-between. + /// + /// This is equivalent to [`IndexMap::move_index`][`crate::IndexMap::move_index`] + /// coming `from` the current [`.index()`][Self::index]. + /// + /// * If `self.index() < to`, the other pairs will shift down while the targeted pair moves up. + /// * If `self.index() > to`, the other pairs will shift up while the targeted pair moves down. + /// + /// ***Panics*** if `to` is out of bounds. + /// + /// Computes in **O(n)** time (average). + #[track_caller] + pub fn move_index(self, to: usize) { + let index = self.index(); + self.into_ref_mut().move_index(index, to); + } + + /// Swaps the position of entry with another. + /// + /// This is equivalent to [`IndexMap::swap_indices`][`crate::IndexMap::swap_indices`] + /// with the current [`.index()`][Self::index] as one of the two being swapped. + /// + /// ***Panics*** if the `other` index is out of bounds. + /// + /// Computes in **O(1)** time (average). + pub fn swap_indices(self, other: usize) { + let index = self.index(); + self.into_ref_mut().swap_indices(index, other); + } +} + +impl fmt::Debug for OccupiedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +impl<'a, K, V> From> for OccupiedEntry<'a, K, V> { + fn from(other: IndexedEntry<'a, K, V>) -> Self { + let IndexedEntry { + map: RefMut { indices, entries }, + index, + } = other; + let hash = entries[index].hash; + Self { + entries, + index: indices + .find_entry(hash.get(), move |&i| i == index) + .expect("index not found"), + } + } +} + +/// A view into a vacant entry in an [`IndexMap`][crate::IndexMap]. +/// It is part of the [`Entry`] enum. +pub struct VacantEntry<'a, K, V> { + map: RefMut<'a, K, V>, + hash: HashValue, + key: K, +} + +impl<'a, K, V> VacantEntry<'a, K, V> { + /// Return the index where a key-value pair may be inserted. + pub fn index(&self) -> usize { + self.map.indices.len() + } + + /// Gets a reference to the key that was used to find the entry. + pub fn key(&self) -> &K { + &self.key + } + + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.key + } + + /// Takes ownership of the key, leaving the entry vacant. + pub fn into_key(self) -> K { + self.key + } + + /// Inserts the entry's key and the given value into the map, and returns a mutable reference + /// to the value. + /// + /// Computes in **O(1)** time (amortized average). + pub fn insert(self, value: V) -> &'a mut V { + self.insert_entry(value).into_mut() + } + + /// Inserts the entry's key and the given value into the map, and returns an `OccupiedEntry`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + let Self { map, hash, key } = self; + map.insert_unique(hash, key, value) + } + + /// Inserts the entry's key and the given value into the map at its ordered + /// position among sorted keys, and returns the new index and a mutable + /// reference to the value. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted(self, value: V) -> (usize, &'a mut V) + where + K: Ord, + { + let slice = crate::map::Slice::from_slice(self.map.entries); + let i = slice.binary_search_keys(&self.key).unwrap_err(); + (i, self.shift_insert(i, value)) + } + + /// Inserts the entry's key and the given value into the map at the given index, + /// shifting others to the right, and returns a mutable reference to the value. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn shift_insert(mut self, index: usize, value: V) -> &'a mut V { + self.map + .shift_insert_unique(index, self.hash, self.key, value); + &mut self.map.entries[index].value + } +} + +impl fmt::Debug for VacantEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() + } +} + +/// A view into an occupied entry in an [`IndexMap`][crate::IndexMap] obtained by index. +/// +/// This `struct` is created from the [`get_index_entry`][crate::IndexMap::get_index_entry] method. +pub struct IndexedEntry<'a, K, V> { + map: RefMut<'a, K, V>, + // We have a mutable reference to the map, which keeps the index + // valid and pointing to the correct entry. + index: usize, +} + +impl<'a, K, V> IndexedEntry<'a, K, V> { + pub(crate) fn new(map: &'a mut IndexMapCore, index: usize) -> Self { + Self { + map: map.borrow_mut(), + index, + } + } + + /// Return the index of the key-value pair + #[inline] + pub fn index(&self) -> usize { + self.index + } + + /// Gets a reference to the entry's key in the map. + pub fn key(&self) -> &K { + &self.map.entries[self.index].key + } + + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.map.entries[self.index].key + } + + /// Gets a reference to the entry's value in the map. + pub fn get(&self) -> &V { + &self.map.entries[self.index].value + } + + /// Gets a mutable reference to the entry's value in the map. + /// + /// If you need a reference which may outlive the destruction of the + /// `IndexedEntry` value, see [`into_mut`][Self::into_mut]. + pub fn get_mut(&mut self) -> &mut V { + &mut self.map.entries[self.index].value + } + + /// Sets the value of the entry to `value`, and returns the entry's old value. + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Converts into a mutable reference to the entry's value in the map, + /// with a lifetime bound to the map itself. + pub fn into_mut(self) -> &'a mut V { + &mut self.map.entries[self.index].value + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_entry(mut self) -> (K, V) { + self.map.swap_remove_index(self.index).unwrap() + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_entry(mut self) -> (K, V) { + self.map.shift_remove_index(self.index).unwrap() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove(self) -> V { + self.swap_remove_entry().1 + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove(self) -> V { + self.shift_remove_entry().1 + } + + /// Moves the position of the entry to a new index + /// by shifting all other entries in-between. + /// + /// This is equivalent to [`IndexMap::move_index`][`crate::IndexMap::move_index`] + /// coming `from` the current [`.index()`][Self::index]. + /// + /// * If `self.index() < to`, the other pairs will shift down while the targeted pair moves up. + /// * If `self.index() > to`, the other pairs will shift up while the targeted pair moves down. + /// + /// ***Panics*** if `to` is out of bounds. + /// + /// Computes in **O(n)** time (average). + #[track_caller] + pub fn move_index(mut self, to: usize) { + self.map.move_index(self.index, to); + } + + /// Swaps the position of entry with another. + /// + /// This is equivalent to [`IndexMap::swap_indices`][`crate::IndexMap::swap_indices`] + /// with the current [`.index()`][Self::index] as one of the two being swapped. + /// + /// ***Panics*** if the `other` index is out of bounds. + /// + /// Computes in **O(1)** time (average). + pub fn swap_indices(mut self, other: usize) { + self.map.swap_indices(self.index, other); + } +} + +impl fmt::Debug for IndexedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IndexedEntry") + .field("index", &self.index) + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +impl<'a, K, V> From> for IndexedEntry<'a, K, V> { + fn from(other: OccupiedEntry<'a, K, V>) -> Self { + Self { + index: other.index(), + map: other.into_ref_mut(), + } + } +} diff --git a/src/map/core/raw.rs b/src/map/core/raw.rs deleted file mode 100644 index 3388f123..00000000 --- a/src/map/core/raw.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![allow(unsafe_code)] -//! 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 core::fmt; -use core::mem::replace; -use hashbrown::raw::RawTable; - -type RawBucket = hashbrown::raw::Bucket; - -pub(super) struct DebugIndices<'a>(pub &'a RawTable); -impl fmt::Debug for DebugIndices<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // SAFETY: we're not letting any of the buckets escape this function - let indices = unsafe { self.0.iter().map(|raw_bucket| raw_bucket.read()) }; - f.debug_list().entries(indices).finish() - } -} - -impl IndexMapCore { - /// Sweep the whole table to erase indices start..end - pub(super) fn erase_indices_sweep(&mut self, start: usize, end: usize) { - // SAFETY: we're not letting any of the buckets escape this function - unsafe { - let offset = end - start; - for bucket in self.indices.iter() { - let i = bucket.read(); - if i >= end { - bucket.write(i - offset); - } else if i >= start { - self.indices.erase(bucket); - } - } - } - } - - pub(crate) fn entry(&mut self, hash: HashValue, key: K) -> Entry<'_, K, V> - where - K: Eq, - { - let eq = equivalent(&key, &self.entries); - match self.indices.find(hash.get(), eq) { - // SAFETY: The entry is created with a live raw bucket, at the same time - // we have a &mut reference to the map, so it can not be modified further. - Some(raw_bucket) => Entry::Occupied(OccupiedEntry { - map: self, - raw_bucket, - key, - }), - None => Entry::Vacant(VacantEntry { - map: self, - hash, - key, - }), - } - } - - pub(super) fn indices_mut(&mut self) -> impl Iterator { - // SAFETY: we're not letting any of the buckets escape this function, - // only the item references that are appropriately bound to `&mut self`. - unsafe { self.indices.iter().map(|bucket| bucket.as_mut()) } - } - - /// Return the raw bucket for the given index - fn find_index(&self, index: usize) -> RawBucket { - // We'll get a "nice" bounds-check from indexing `self.entries`, - // and then we expect to find it in the table as well. - let hash = self.entries[index].hash.get(); - self.indices - .find(hash, move |&i| i == index) - .expect("index not found") - } - - pub(crate) fn swap_indices(&mut self, a: usize, b: usize) { - // SAFETY: Can't take two `get_mut` references from one table, so we - // must use raw buckets to do the swap. This is still safe because we - // are locally sure they won't dangle, and we write them individually. - unsafe { - let raw_bucket_a = self.find_index(a); - let raw_bucket_b = self.find_index(b); - raw_bucket_a.write(b); - raw_bucket_b.write(a); - } - self.entries.swap(a, b); - } -} - -/// A view into an occupied entry in a `IndexMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -// SAFETY: The lifetime of the map reference also constrains the raw bucket, -// which is essentially a raw pointer into the map indices. -pub struct OccupiedEntry<'a, K, V> { - map: &'a mut IndexMapCore, - raw_bucket: RawBucket, - key: K, -} - -// `hashbrown::raw::Bucket` is only `Send`, not `Sync`. -// SAFETY: `&self` only accesses the bucket to read it. -unsafe impl Sync for OccupiedEntry<'_, K, V> {} - -// The parent module also adds methods that don't threaten the unsafe encapsulation. -impl<'a, K, V> OccupiedEntry<'a, K, V> { - pub fn key(&self) -> &K { - &self.key - } - - pub fn get(&self) -> &V { - &self.map.entries[self.index()].value - } - - pub fn get_mut(&mut self) -> &mut V { - let index = self.index(); - &mut self.map.entries[index].value - } - - /// Put the new key in the occupied entry's key slot - pub(crate) fn replace_key(self) -> K { - let index = self.index(); - let old_key = &mut self.map.entries[index].key; - replace(old_key, self.key) - } - - /// Return the index of the key-value pair - #[inline] - pub fn index(&self) -> usize { - // SAFETY: we have &mut map keep keeping the bucket stable - unsafe { self.raw_bucket.read() } - } - - pub fn into_mut(self) -> &'a mut V { - let index = self.index(); - &mut self.map.entries[index].value - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// 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!** - /// - /// Computes in **O(1)** time (average). - pub fn swap_remove_entry(self) -> (K, V) { - // SAFETY: This is safe because it can only happen once (self is consumed) - // and map.indices have not been modified since entry construction - let index = unsafe { self.map.indices.remove(self.raw_bucket) }; - self.map.swap_remove_finish(index) - } - - /// Remove and return the key, value pair stored in the map for this entry - /// - /// Like `Vec::remove`, the pair is removed by shifting all of the - /// elements that follow it, preserving their relative order. - /// **This perturbs the index of all of those elements!** - /// - /// Computes in **O(n)** time (average). - pub fn shift_remove_entry(self) -> (K, V) { - // SAFETY: This is safe because it can only happen once (self is consumed) - // and map.indices have not been modified since entry construction - let index = unsafe { self.map.indices.remove(self.raw_bucket) }; - self.map.shift_remove_finish(index) - } -} diff --git a/src/map/core/raw_entry_v1.rs b/src/map/core/raw_entry_v1.rs new file mode 100644 index 00000000..757a3cee --- /dev/null +++ b/src/map/core/raw_entry_v1.rs @@ -0,0 +1,665 @@ +//! Opt-in access to the experimental raw entry API. +//! +//! This module is designed to mimic the raw entry API of [`HashMap`][std::collections::hash_map], +//! matching its unstable state as of Rust 1.75. See the tracking issue +//! [rust#56167](https://github.com/rust-lang/rust/issues/56167) for more details. +//! +//! The trait [`RawEntryApiV1`] and the `_v1` suffix on its methods are meant to insulate this for +//! the future, in case later breaking changes are needed. If the standard library stabilizes its +//! `hash_raw_entry` feature (or some replacement), matching *inherent* methods will be added to +//! `IndexMap` without such an opt-in trait. + +use super::{Entries, RefMut}; +use crate::{Equivalent, HashValue, IndexMap}; +use core::fmt; +use core::hash::{BuildHasher, Hash, Hasher}; +use core::marker::PhantomData; +use core::mem; +use hashbrown::hash_table; + +/// Opt-in access to the experimental raw entry API. +/// +/// See the [`raw_entry_v1`][self] module documentation for more information. +pub trait RawEntryApiV1: private::Sealed { + /// Creates a raw immutable entry builder for the [`IndexMap`]. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. + /// + /// This is useful for + /// * Hash memoization + /// * Using a search key that doesn't work with the [`Equivalent`] trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// [`get`][IndexMap::get] should be preferred. + /// + /// Immutable raw entries have very limited use; you might instead want + /// [`raw_entry_mut_v1`][Self::raw_entry_mut_v1]. + /// + /// # Examples + /// + /// ``` + /// use core::hash::{BuildHasher, Hash}; + /// use indexmap::map::{IndexMap, RawEntryApiV1}; + /// + /// let mut map = IndexMap::new(); + /// map.extend([("a", 100), ("b", 200), ("c", 300)]); + /// + /// fn compute_hash(hash_builder: &S, key: &K) -> u64 { + /// use core::hash::Hasher; + /// let mut state = hash_builder.build_hasher(); + /// key.hash(&mut state); + /// state.finish() + /// } + /// + /// for k in ["a", "b", "c", "d", "e", "f"] { + /// let hash = compute_hash(map.hasher(), k); + /// let i = map.get_index_of(k); + /// let v = map.get(k); + /// let kv = map.get_key_value(k); + /// let ikv = map.get_full(k); + /// + /// println!("Key: {} and value: {:?}", k, v); + /// + /// assert_eq!(map.raw_entry_v1().from_key(k), kv); + /// assert_eq!(map.raw_entry_v1().from_hash(hash, |q| *q == k), kv); + /// assert_eq!(map.raw_entry_v1().from_key_hashed_nocheck(hash, k), kv); + /// assert_eq!(map.raw_entry_v1().from_hash_full(hash, |q| *q == k), ikv); + /// assert_eq!(map.raw_entry_v1().index_from_hash(hash, |q| *q == k), i); + /// } + /// ``` + fn raw_entry_v1(&self) -> RawEntryBuilder<'_, K, V, S>; + + /// Creates a raw entry builder for the [`IndexMap`]. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. After this, insertions into a vacant entry + /// still require an owned key to be provided. + /// + /// Raw entries are useful for such exotic situations as: + /// + /// * Hash memoization + /// * Deferring the creation of an owned key until it is known to be required + /// * Using a search key that doesn't work with the [`Equivalent`] trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Because raw entries provide much more low-level control, it's much easier + /// to put the `IndexMap` into an inconsistent state which, while memory-safe, + /// will cause the map to produce seemingly random results. Higher-level and more + /// foolproof APIs like [`entry`][IndexMap::entry] should be preferred when possible. + /// + /// Raw entries give mutable access to the keys. This must not be used + /// to modify how the key would compare or hash, as the map will not re-evaluate + /// where the key should go, meaning the keys may become "lost" if their + /// location does not reflect their state. For instance, if you change a key + /// so that the map now contains keys which compare equal, search may start + /// acting erratically, with two keys randomly masking each other. Implementations + /// are free to assume this doesn't happen (within the limits of memory-safety). + /// + /// # Examples + /// + /// ``` + /// use core::hash::{BuildHasher, Hash}; + /// use indexmap::map::{IndexMap, RawEntryApiV1}; + /// use indexmap::map::raw_entry_v1::RawEntryMut; + /// + /// let mut map = IndexMap::new(); + /// map.extend([("a", 100), ("b", 200), ("c", 300)]); + /// + /// fn compute_hash(hash_builder: &S, key: &K) -> u64 { + /// use core::hash::Hasher; + /// let mut state = hash_builder.build_hasher(); + /// key.hash(&mut state); + /// state.finish() + /// } + /// + /// // Existing key (insert and update) + /// match map.raw_entry_mut_v1().from_key("a") { + /// RawEntryMut::Vacant(_) => unreachable!(), + /// RawEntryMut::Occupied(mut view) => { + /// assert_eq!(view.index(), 0); + /// assert_eq!(view.get(), &100); + /// let v = view.get_mut(); + /// let new_v = (*v) * 10; + /// *v = new_v; + /// assert_eq!(view.insert(1111), 1000); + /// } + /// } + /// + /// assert_eq!(map["a"], 1111); + /// assert_eq!(map.len(), 3); + /// + /// // Existing key (take) + /// let hash = compute_hash(map.hasher(), "c"); + /// match map.raw_entry_mut_v1().from_key_hashed_nocheck(hash, "c") { + /// RawEntryMut::Vacant(_) => unreachable!(), + /// RawEntryMut::Occupied(view) => { + /// assert_eq!(view.index(), 2); + /// assert_eq!(view.shift_remove_entry(), ("c", 300)); + /// } + /// } + /// assert_eq!(map.raw_entry_v1().from_key("c"), None); + /// assert_eq!(map.len(), 2); + /// + /// // Nonexistent key (insert and update) + /// let key = "d"; + /// let hash = compute_hash(map.hasher(), key); + /// match map.raw_entry_mut_v1().from_hash(hash, |q| *q == key) { + /// RawEntryMut::Occupied(_) => unreachable!(), + /// RawEntryMut::Vacant(view) => { + /// assert_eq!(view.index(), 2); + /// let (k, value) = view.insert("d", 4000); + /// assert_eq!((*k, *value), ("d", 4000)); + /// *value = 40000; + /// } + /// } + /// assert_eq!(map["d"], 40000); + /// assert_eq!(map.len(), 3); + /// + /// match map.raw_entry_mut_v1().from_hash(hash, |q| *q == key) { + /// RawEntryMut::Vacant(_) => unreachable!(), + /// RawEntryMut::Occupied(view) => { + /// assert_eq!(view.index(), 2); + /// assert_eq!(view.swap_remove_entry(), ("d", 40000)); + /// } + /// } + /// assert_eq!(map.get("d"), None); + /// assert_eq!(map.len(), 2); + /// ``` + fn raw_entry_mut_v1(&mut self) -> RawEntryBuilderMut<'_, K, V, S>; +} + +impl RawEntryApiV1 for IndexMap { + fn raw_entry_v1(&self) -> RawEntryBuilder<'_, K, V, S> { + RawEntryBuilder { map: self } + } + + fn raw_entry_mut_v1(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { + RawEntryBuilderMut { map: self } + } +} + +/// A builder for computing where in an [`IndexMap`] a key-value pair would be stored. +/// +/// This `struct` is created by the [`IndexMap::raw_entry_v1`] method, provided by the +/// [`RawEntryApiV1`] trait. See its documentation for more. +pub struct RawEntryBuilder<'a, K, V, S> { + map: &'a IndexMap, +} + +impl fmt::Debug for RawEntryBuilder<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish_non_exhaustive() + } +} + +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { + /// Access an entry by key. + pub fn from_key(self, key: &Q) -> Option<(&'a K, &'a V)> + where + S: BuildHasher, + Q: ?Sized + Hash + Equivalent, + { + self.map.get_key_value(key) + } + + /// Access an entry by a key and its hash. + pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> Option<(&'a K, &'a V)> + where + Q: ?Sized + Equivalent, + { + let hash = HashValue(hash as usize); + let i = self.map.core.get_index_of(hash, key)?; + self.map.get_index(i) + } + + /// Access an entry by hash. + pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + let map = self.map; + let i = self.index_from_hash(hash, is_match)?; + map.get_index(i) + } + + /// Access an entry by hash, including its index. + pub fn from_hash_full(self, hash: u64, is_match: F) -> Option<(usize, &'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + let map = self.map; + let i = self.index_from_hash(hash, is_match)?; + let (key, value) = map.get_index(i)?; + Some((i, key, value)) + } + + /// Access the index of an entry by hash. + pub fn index_from_hash(self, hash: u64, mut is_match: F) -> Option + where + F: FnMut(&K) -> bool, + { + let hash = HashValue(hash as usize); + let entries = &*self.map.core.entries; + let eq = move |&i: &usize| is_match(&entries[i].key); + self.map.core.indices.find(hash.get(), eq).copied() + } +} + +/// A builder for computing where in an [`IndexMap`] a key-value pair would be stored. +/// +/// This `struct` is created by the [`IndexMap::raw_entry_mut_v1`] method, provided by the +/// [`RawEntryApiV1`] trait. See its documentation for more. +pub struct RawEntryBuilderMut<'a, K, V, S> { + map: &'a mut IndexMap, +} + +impl fmt::Debug for RawEntryBuilderMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilderMut").finish_non_exhaustive() + } +} + +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { + /// Access an entry by key. + pub fn from_key(self, key: &Q) -> RawEntryMut<'a, K, V, S> + where + S: BuildHasher, + Q: ?Sized + Hash + Equivalent, + { + let hash = self.map.hash(key); + self.from_key_hashed_nocheck(hash.get(), key) + } + + /// Access an entry by a key and its hash. + pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> RawEntryMut<'a, K, V, S> + where + Q: ?Sized + Equivalent, + { + self.from_hash(hash, |k| Q::equivalent(key, k)) + } + + /// Access an entry by hash. + pub fn from_hash(self, hash: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S> + where + F: FnMut(&K) -> bool, + { + let ref_entries = &*self.map.core.entries; + let eq = move |&i: &usize| is_match(&ref_entries[i].key); + match self.map.core.indices.find_entry(hash, eq) { + Ok(index) => RawEntryMut::Occupied(RawOccupiedEntryMut { + entries: &mut self.map.core.entries, + index, + hash_builder: PhantomData, + }), + Err(absent) => RawEntryMut::Vacant(RawVacantEntryMut { + map: RefMut::new(absent.into_table(), &mut self.map.core.entries), + hash_builder: &self.map.hash_builder, + }), + } + } +} + +/// Raw entry for an existing key-value pair or a vacant location to +/// insert one. +pub enum RawEntryMut<'a, K, V, S> { + /// Existing slot with equivalent key. + Occupied(RawOccupiedEntryMut<'a, K, V, S>), + /// Vacant slot (no equivalent key in the map). + Vacant(RawVacantEntryMut<'a, K, V, S>), +} + +impl fmt::Debug for RawEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut tuple = f.debug_tuple("RawEntryMut"); + match self { + Self::Vacant(v) => tuple.field(v), + Self::Occupied(o) => tuple.field(o), + }; + tuple.finish() + } +} + +impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { + /// Return the index where the key-value pair exists or may be inserted. + #[inline] + pub fn index(&self) -> usize { + match self { + Self::Occupied(entry) => entry.index(), + Self::Vacant(entry) => entry.index(), + } + } + + /// Inserts the given default key and value in the entry if it is vacant and returns mutable + /// references to them. Otherwise mutable references to an already existent pair are returned. + pub fn or_insert(self, default_key: K, default_value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + match self { + Self::Occupied(entry) => entry.into_key_value_mut(), + Self::Vacant(entry) => entry.insert(default_key, default_value), + } + } + + /// Inserts the result of the `call` function in the entry if it is vacant and returns mutable + /// references to them. Otherwise mutable references to an already existent pair are returned. + pub fn or_insert_with(self, call: F) -> (&'a mut K, &'a mut V) + where + F: FnOnce() -> (K, V), + K: Hash, + S: BuildHasher, + { + match self { + Self::Occupied(entry) => entry.into_key_value_mut(), + Self::Vacant(entry) => { + let (key, value) = call(); + entry.insert(key, value) + } + } + } + + /// Modifies the entry if it is occupied. + pub fn and_modify(mut self, f: F) -> Self + where + F: FnOnce(&mut K, &mut V), + { + if let Self::Occupied(entry) = &mut self { + let (k, v) = entry.get_key_value_mut(); + f(k, v); + } + self + } +} + +/// A raw view into an occupied entry in an [`IndexMap`]. +/// It is part of the [`RawEntryMut`] enum. +pub struct RawOccupiedEntryMut<'a, K, V, S> { + entries: &'a mut Entries, + index: hash_table::OccupiedEntry<'a, usize>, + hash_builder: PhantomData<&'a S>, +} + +impl fmt::Debug for RawOccupiedEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawOccupiedEntryMut") + .field("key", self.key()) + .field("value", self.get()) + .finish_non_exhaustive() + } +} + +impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { + /// Return the index of the key-value pair + #[inline] + pub fn index(&self) -> usize { + *self.index.get() + } + + #[inline] + fn into_ref_mut(self) -> RefMut<'a, K, V> { + RefMut::new(self.index.into_table(), self.entries) + } + + /// Gets a reference to the entry's key in the map. + /// + /// Note that this is not the key that was used to find the entry. There may be an observable + /// difference if the key type has any distinguishing features outside of `Hash` and `Eq`, like + /// extra fields or the memory address of an allocation. + pub fn key(&self) -> &K { + &self.entries[self.index()].key + } + + /// Gets a mutable reference to the entry's key in the map. + /// + /// Note that this is not the key that was used to find the entry. There may be an observable + /// difference if the key type has any distinguishing features outside of `Hash` and `Eq`, like + /// extra fields or the memory address of an allocation. + pub fn key_mut(&mut self) -> &mut K { + let index = self.index(); + &mut self.entries[index].key + } + + /// Converts into a mutable reference to the entry's key in the map, + /// with a lifetime bound to the map itself. + /// + /// Note that this is not the key that was used to find the entry. There may be an observable + /// difference if the key type has any distinguishing features outside of `Hash` and `Eq`, like + /// extra fields or the memory address of an allocation. + pub fn into_key(self) -> &'a mut K { + let index = self.index(); + &mut self.entries[index].key + } + + /// Gets a reference to the entry's value in the map. + pub fn get(&self) -> &V { + &self.entries[self.index()].value + } + + /// Gets a mutable reference to the entry's value in the map. + /// + /// If you need a reference which may outlive the destruction of the + /// [`RawEntryMut`] value, see [`into_mut`][Self::into_mut]. + pub fn get_mut(&mut self) -> &mut V { + let index = self.index(); + &mut self.entries[index].value + } + + /// Converts into a mutable reference to the entry's value in the map, + /// with a lifetime bound to the map itself. + pub fn into_mut(self) -> &'a mut V { + let index = self.index(); + &mut self.entries[index].value + } + + /// Gets a reference to the entry's key and value in the map. + pub fn get_key_value(&self) -> (&K, &V) { + self.entries[self.index()].refs() + } + + /// Gets a reference to the entry's key and value in the map. + pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { + let index = self.index(); + self.entries[index].muts() + } + + /// Converts into a mutable reference to the entry's key and value in the map, + /// with a lifetime bound to the map itself. + pub fn into_key_value_mut(self) -> (&'a mut K, &'a mut V) { + let index = self.index(); + self.entries[index].muts() + } + + /// Sets the value of the entry, and returns the entry's old value. + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Sets the key of the entry, and returns the entry's old key. + pub fn insert_key(&mut self, key: K) -> K { + mem::replace(self.key_mut(), key) + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// **NOTE:** This is equivalent to [`.swap_remove()`][Self::swap_remove], replacing this + /// entry's position with the last element, and it is deprecated in favor of calling that + /// explicitly. If you need to preserve the relative order of the keys in the map, use + /// [`.shift_remove()`][Self::shift_remove] instead. + #[deprecated(note = "`remove` disrupts the map order -- \ + use `swap_remove` or `shift_remove` for explicit behavior.")] + pub fn remove(self) -> V { + self.swap_remove() + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove(self) -> V { + self.swap_remove_entry().1 + } + + /// Remove the key, value pair stored in the map for this entry, and return the value. + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove(self) -> V { + self.shift_remove_entry().1 + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// **NOTE:** This is equivalent to [`.swap_remove_entry()`][Self::swap_remove_entry], + /// replacing this entry's position with the last element, and it is deprecated in favor of + /// calling that explicitly. If you need to preserve the relative order of the keys in the map, + /// use [`.shift_remove_entry()`][Self::shift_remove_entry] instead. + #[deprecated(note = "`remove_entry` disrupts the map order -- \ + use `swap_remove_entry` or `shift_remove_entry` for explicit behavior.")] + pub fn remove_entry(self) -> (K, V) { + self.swap_remove_entry() + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::swap_remove`][crate::Vec::swap_remove], the pair is removed by swapping it with + /// the last element of the map and popping it off. + /// **This perturbs the position of what used to be the last element!** + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_entry(self) -> (K, V) { + let (index, entry) = self.index.remove(); + RefMut::new(entry.into_table(), self.entries).swap_remove_finish(index) + } + + /// Remove and return the key, value pair stored in the map for this entry + /// + /// Like [`Vec::remove`][crate::Vec::remove], the pair is removed by shifting all of the + /// elements that follow it, preserving their relative order. + /// **This perturbs the index of all of those elements!** + /// + /// Computes in **O(n)** time (average). + pub fn shift_remove_entry(self) -> (K, V) { + let (index, entry) = self.index.remove(); + RefMut::new(entry.into_table(), self.entries).shift_remove_finish(index) + } + + /// Moves the position of the entry to a new index + /// by shifting all other entries in-between. + /// + /// This is equivalent to [`IndexMap::move_index`] + /// coming `from` the current [`.index()`][Self::index]. + /// + /// * If `self.index() < to`, the other pairs will shift down while the targeted pair moves up. + /// * If `self.index() > to`, the other pairs will shift up while the targeted pair moves down. + /// + /// ***Panics*** if `to` is out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn move_index(self, to: usize) { + let index = self.index(); + self.into_ref_mut().move_index(index, to); + } + + /// Swaps the position of entry with another. + /// + /// This is equivalent to [`IndexMap::swap_indices`] + /// with the current [`.index()`][Self::index] as one of the two being swapped. + /// + /// ***Panics*** if the `other` index is out of bounds. + /// + /// Computes in **O(1)** time (average). + pub fn swap_indices(self, other: usize) { + let index = self.index(); + self.into_ref_mut().swap_indices(index, other); + } +} + +/// A view into a vacant raw entry in an [`IndexMap`]. +/// It is part of the [`RawEntryMut`] enum. +pub struct RawVacantEntryMut<'a, K, V, S> { + map: RefMut<'a, K, V>, + hash_builder: &'a S, +} + +impl fmt::Debug for RawVacantEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawVacantEntryMut").finish_non_exhaustive() + } +} + +impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { + /// Return the index where a key-value pair may be inserted. + pub fn index(&self) -> usize { + self.map.indices.len() + } + + /// Inserts the given key and value into the map, + /// and returns mutable references to them. + pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + let mut h = self.hash_builder.build_hasher(); + key.hash(&mut h); + self.insert_hashed_nocheck(h.finish(), key, value) + } + + /// Inserts the given key and value into the map with the provided hash, + /// and returns mutable references to them. + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) { + let hash = HashValue(hash as usize); + self.map.insert_unique(hash, key, value).into_muts() + } + + /// Inserts the given key and value into the map at the given index, + /// shifting others to the right, and returns mutable references to them. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn shift_insert(self, index: usize, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + let mut h = self.hash_builder.build_hasher(); + key.hash(&mut h); + self.shift_insert_hashed_nocheck(index, h.finish(), key, value) + } + + /// Inserts the given key and value into the map with the provided hash + /// at the given index, and returns mutable references to them. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(n)** time (average). + pub fn shift_insert_hashed_nocheck( + mut self, + index: usize, + hash: u64, + key: K, + value: V, + ) -> (&'a mut K, &'a mut V) { + let hash = HashValue(hash as usize); + self.map.shift_insert_unique(index, hash, key, value); + self.map.entries[index].muts() + } +} + +mod private { + pub trait Sealed {} + + impl Sealed for super::IndexMap {} +} diff --git a/src/map/iter.rs b/src/map/iter.rs new file mode 100644 index 00000000..83d2e3f7 --- /dev/null +++ b/src/map/iter.rs @@ -0,0 +1,776 @@ +use super::core::IndexMapCore; +use super::{Bucket, Entries, IndexMap, Slice}; + +use alloc::vec::{self, Vec}; +use core::fmt; +use core::hash::{BuildHasher, Hash}; +use core::iter::FusedIterator; +use core::ops::{Index, RangeBounds}; +use core::slice; + +impl<'a, K, V, S> IntoIterator for &'a IndexMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a mut IndexMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl IntoIterator for IndexMap { + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_entries()) + } +} + +/// An iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::iter`] method. +/// See its documentation for more. +pub struct Iter<'a, K, V> { + iter: slice::Iter<'a, Bucket>, +} + +impl<'a, K, V> Iter<'a, K, V> { + pub(super) fn new(entries: &'a [Bucket]) -> Self { + Self { + iter: entries.iter(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + iterator_methods!(Bucket::refs); +} + +impl DoubleEndedIterator for Iter<'_, K, V> { + double_ended_iterator_methods!(Bucket::refs); +} + +impl ExactSizeIterator for Iter<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Iter<'_, K, V> {} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +impl Clone for Iter<'_, K, V> { + fn clone(&self) -> Self { + Iter { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for Iter<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +impl Default for Iter<'_, K, V> { + fn default() -> Self { + Self { iter: [].iter() } + } +} + +/// A mutable iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::iter_mut`] method. +/// See its documentation for more. +pub struct IterMut<'a, K, V> { + iter: slice::IterMut<'a, Bucket>, +} + +impl<'a, K, V> IterMut<'a, K, V> { + pub(super) fn new(entries: &'a mut [Bucket]) -> Self { + Self { + iter: entries.iter_mut(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } + + /// Returns a mutable slice of the remaining entries in the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. + pub fn into_slice(self) -> &'a mut Slice { + Slice::from_mut_slice(self.iter.into_slice()) + } +} + +impl<'a, K, V> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + iterator_methods!(Bucket::ref_mut); +} + +impl DoubleEndedIterator for IterMut<'_, K, V> { + double_ended_iterator_methods!(Bucket::ref_mut); +} + +impl ExactSizeIterator for IterMut<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IterMut<'_, K, V> {} + +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() + } +} + +impl Default for IterMut<'_, K, V> { + fn default() -> Self { + Self { + iter: [].iter_mut(), + } + } +} + +/// A mutable iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`MutableKeys::iter_mut2`][super::MutableKeys::iter_mut2] method. +/// See its documentation for more. +pub struct IterMut2<'a, K, V> { + iter: slice::IterMut<'a, Bucket>, +} + +impl<'a, K, V> IterMut2<'a, K, V> { + pub(super) fn new(entries: &'a mut [Bucket]) -> Self { + Self { + iter: entries.iter_mut(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } + + /// Returns a mutable slice of the remaining entries in the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. + pub fn into_slice(self) -> &'a mut Slice { + Slice::from_mut_slice(self.iter.into_slice()) + } +} + +impl<'a, K, V> Iterator for IterMut2<'a, K, V> { + type Item = (&'a mut K, &'a mut V); + + iterator_methods!(Bucket::muts); +} + +impl DoubleEndedIterator for IterMut2<'_, K, V> { + double_ended_iterator_methods!(Bucket::muts); +} + +impl ExactSizeIterator for IterMut2<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IterMut2<'_, K, V> {} + +impl fmt::Debug for IterMut2<'_, 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() + } +} + +impl Default for IterMut2<'_, K, V> { + fn default() -> Self { + Self { + iter: [].iter_mut(), + } + } +} + +/// An owning iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::into_iter`] method +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +#[derive(Clone)] +pub struct IntoIter { + iter: vec::IntoIter>, +} + +impl IntoIter { + pub(super) fn new(entries: Vec>) -> Self { + Self { + iter: entries.into_iter(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } + + /// Returns a mutable slice of the remaining entries in the iterator. + pub fn as_mut_slice(&mut self) -> &mut Slice { + Slice::from_mut_slice(self.iter.as_mut_slice()) + } +} + +impl Iterator for IntoIter { + type Item = (K, V); + + iterator_methods!(Bucket::key_value); +} + +impl DoubleEndedIterator for IntoIter { + double_ended_iterator_methods!(Bucket::key_value); +} + +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IntoIter {} + +impl fmt::Debug for IntoIter { + 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() + } +} + +impl Default for IntoIter { + fn default() -> Self { + Self { + iter: Vec::new().into_iter(), + } + } +} + +/// A draining iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::drain`] method. +/// See its documentation for more. +pub struct Drain<'a, K, V> { + iter: vec::Drain<'a, Bucket>, +} + +impl<'a, K, V> Drain<'a, K, V> { + pub(super) fn new(iter: vec::Drain<'a, Bucket>) -> Self { + Self { iter } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl Iterator for Drain<'_, K, V> { + type Item = (K, V); + + iterator_methods!(Bucket::key_value); +} + +impl DoubleEndedIterator for Drain<'_, K, V> { + double_ended_iterator_methods!(Bucket::key_value); +} + +impl ExactSizeIterator for Drain<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Drain<'_, K, V> {} + +impl fmt::Debug for Drain<'_, 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 iterator over the keys of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::keys`] method. +/// See its documentation for more. +pub struct Keys<'a, K, V> { + iter: slice::Iter<'a, Bucket>, +} + +impl<'a, K, V> Keys<'a, K, V> { + pub(super) fn new(entries: &'a [Bucket]) -> Self { + Self { + iter: entries.iter(), + } + } +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + iterator_methods!(Bucket::key_ref); +} + +impl DoubleEndedIterator for Keys<'_, K, V> { + double_ended_iterator_methods!(Bucket::key_ref); +} + +impl ExactSizeIterator for Keys<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Keys<'_, K, V> {} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +impl Clone for Keys<'_, K, V> { + fn clone(&self) -> Self { + Keys { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for Keys<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +impl Default for Keys<'_, K, V> { + fn default() -> Self { + Self { iter: [].iter() } + } +} + +/// Access [`IndexMap`] keys at indexed positions. +/// +/// While [`Index for IndexMap`][values] accesses a map's values, +/// indexing through [`IndexMap::keys`] offers an alternative to access a map's +/// keys instead. +/// +/// [values]: IndexMap#impl-Index-for-IndexMap +/// +/// Since `Keys` is also an iterator, consuming items from the iterator will +/// offset the effective indexes. Similarly, if `Keys` is obtained from +/// [`Slice::keys`], indexes will be interpreted relative to the position of +/// that slice. +/// +/// # Examples +/// +/// ``` +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// for word in "Lorem ipsum dolor sit amet".split_whitespace() { +/// map.insert(word.to_lowercase(), word.to_uppercase()); +/// } +/// +/// assert_eq!(map[0], "LOREM"); +/// assert_eq!(map.keys()[0], "lorem"); +/// assert_eq!(map[1], "IPSUM"); +/// assert_eq!(map.keys()[1], "ipsum"); +/// +/// map.reverse(); +/// assert_eq!(map.keys()[0], "amet"); +/// assert_eq!(map.keys()[1], "sit"); +/// +/// map.sort_keys(); +/// assert_eq!(map.keys()[0], "amet"); +/// assert_eq!(map.keys()[1], "dolor"); +/// +/// // Advancing the iterator will offset the indexing +/// let mut keys = map.keys(); +/// assert_eq!(keys[0], "amet"); +/// assert_eq!(keys.next().map(|s| &**s), Some("amet")); +/// assert_eq!(keys[0], "dolor"); +/// assert_eq!(keys[1], "ipsum"); +/// +/// // Slices may have an offset as well +/// let slice = &map[2..]; +/// assert_eq!(slice[0], "IPSUM"); +/// assert_eq!(slice.keys()[0], "ipsum"); +/// ``` +/// +/// ```should_panic +/// use indexmap::IndexMap; +/// +/// let mut map = IndexMap::new(); +/// map.insert("foo", 1); +/// println!("{:?}", map.keys()[10]); // panics! +/// ``` +impl<'a, K, V> Index for Keys<'a, K, V> { + type Output = K; + + /// Returns a reference to the key at the supplied `index`. + /// + /// ***Panics*** if `index` is out of bounds. + fn index(&self, index: usize) -> &K { + &self.iter.as_slice()[index].key + } +} + +/// An owning iterator over the keys of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::into_keys`] method. +/// See its documentation for more. +pub struct IntoKeys { + iter: vec::IntoIter>, +} + +impl IntoKeys { + pub(super) fn new(entries: Vec>) -> Self { + Self { + iter: entries.into_iter(), + } + } +} + +impl Iterator for IntoKeys { + type Item = K; + + iterator_methods!(Bucket::key); +} + +impl DoubleEndedIterator for IntoKeys { + double_ended_iterator_methods!(Bucket::key); +} + +impl ExactSizeIterator for IntoKeys { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IntoKeys {} + +impl fmt::Debug for IntoKeys { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::key_ref); + f.debug_list().entries(iter).finish() + } +} + +impl Default for IntoKeys { + fn default() -> Self { + Self { + iter: Vec::new().into_iter(), + } + } +} + +/// An iterator over the values of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::values`] method. +/// See its documentation for more. +pub struct Values<'a, K, V> { + iter: slice::Iter<'a, Bucket>, +} + +impl<'a, K, V> Values<'a, K, V> { + pub(super) fn new(entries: &'a [Bucket]) -> Self { + Self { + iter: entries.iter(), + } + } +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + iterator_methods!(Bucket::value_ref); +} + +impl DoubleEndedIterator for Values<'_, K, V> { + double_ended_iterator_methods!(Bucket::value_ref); +} + +impl ExactSizeIterator for Values<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Values<'_, K, V> {} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +impl Clone for Values<'_, K, V> { + fn clone(&self) -> Self { + Values { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for Values<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +impl Default for Values<'_, K, V> { + fn default() -> Self { + Self { iter: [].iter() } + } +} + +/// A mutable iterator over the values of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::values_mut`] method. +/// See its documentation for more. +pub struct ValuesMut<'a, K, V> { + iter: slice::IterMut<'a, Bucket>, +} + +impl<'a, K, V> ValuesMut<'a, K, V> { + pub(super) fn new(entries: &'a mut [Bucket]) -> Self { + Self { + iter: entries.iter_mut(), + } + } +} + +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + iterator_methods!(Bucket::value_mut); +} + +impl DoubleEndedIterator for ValuesMut<'_, K, V> { + double_ended_iterator_methods!(Bucket::value_mut); +} + +impl ExactSizeIterator for ValuesMut<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for ValuesMut<'_, K, V> {} + +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() + } +} + +impl Default for ValuesMut<'_, K, V> { + fn default() -> Self { + Self { + iter: [].iter_mut(), + } + } +} + +/// An owning iterator over the values of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::into_values`] method. +/// See its documentation for more. +pub struct IntoValues { + iter: vec::IntoIter>, +} + +impl IntoValues { + pub(super) fn new(entries: Vec>) -> Self { + Self { + iter: entries.into_iter(), + } + } +} + +impl Iterator for IntoValues { + type Item = V; + + iterator_methods!(Bucket::value); +} + +impl DoubleEndedIterator for IntoValues { + double_ended_iterator_methods!(Bucket::value); +} + +impl ExactSizeIterator for IntoValues { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IntoValues {} + +impl fmt::Debug for IntoValues { + 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() + } +} + +impl Default for IntoValues { + fn default() -> Self { + Self { + iter: Vec::new().into_iter(), + } + } +} + +/// A splicing iterator for `IndexMap`. +/// +/// This `struct` is created by [`IndexMap::splice()`]. +/// See its documentation for more. +pub struct Splice<'a, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + map: &'a mut IndexMap, + tail: IndexMapCore, + drain: vec::IntoIter>, + replace_with: I, +} + +impl<'a, I, K, V, S> Splice<'a, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + #[track_caller] + pub(super) fn new(map: &'a mut IndexMap, range: R, replace_with: I) -> Self + where + R: RangeBounds, + { + let (tail, drain) = map.core.split_splice(range); + Self { + map, + tail, + drain, + replace_with, + } + } +} + +impl Drop for Splice<'_, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + fn drop(&mut self) { + // Finish draining unconsumed items. We don't strictly *have* to do this + // manually, since we already split it into separate memory, but it will + // match the drop order of `vec::Splice` items this way. + let _ = self.drain.nth(usize::MAX); + + // Now insert all the new items. If a key matches an existing entry, it + // keeps the original position and only replaces the value, like `insert`. + while let Some((key, value)) = self.replace_with.next() { + // Since the tail is disjoint, we can try to update it first, + // or else insert (update or append) the primary map. + let hash = self.map.hash(&key); + if let Some(i) = self.tail.get_index_of(hash, &key) { + self.tail.as_entries_mut()[i].value = value; + } else { + self.map.core.insert_full(hash, key, value); + } + } + + // Finally, re-append the tail + self.map.core.append_unchecked(&mut self.tail); + } +} + +impl Iterator for Splice<'_, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + type Item = (K, V); + + fn next(&mut self) -> Option { + self.drain.next().map(Bucket::key_value) + } + + fn size_hint(&self) -> (usize, Option) { + self.drain.size_hint() + } +} + +impl DoubleEndedIterator for Splice<'_, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + fn next_back(&mut self) -> Option { + self.drain.next_back().map(Bucket::key_value) + } +} + +impl ExactSizeIterator for Splice<'_, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ + fn len(&self) -> usize { + self.drain.len() + } +} + +impl FusedIterator for Splice<'_, I, K, V, S> +where + I: Iterator, + K: Hash + Eq, + S: BuildHasher, +{ +} + +impl<'a, I, K, V, S> fmt::Debug for Splice<'a, I, K, V, S> +where + I: fmt::Debug + Iterator, + K: fmt::Debug + Hash + Eq, + V: fmt::Debug, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Follow `vec::Splice` in only printing the drain and replacement + f.debug_struct("Splice") + .field("drain", &self.drain) + .field("replace_with", &self.replace_with) + .finish() + } +} diff --git a/src/map/mutable.rs b/src/map/mutable.rs new file mode 100644 index 00000000..e429c8be --- /dev/null +++ b/src/map/mutable.rs @@ -0,0 +1,166 @@ +use core::hash::{BuildHasher, Hash}; + +use super::{ + Bucket, Entries, Entry, Equivalent, IndexMap, IndexedEntry, IterMut2, OccupiedEntry, + VacantEntry, +}; + +/// Opt-in mutable access to [`IndexMap`] keys. +/// +/// These methods expose `&mut K`, mutable references to the key as it is stored +/// in the map. +/// You are allowed to modify the keys in the map **if the modification +/// does not change the key’s hash and equality**. +/// +/// If keys are modified erroneously, you can no longer look them up. +/// This is sound (memory safe) but a logical error hazard (just like +/// implementing `PartialEq`, `Eq`, or `Hash` incorrectly would be). +/// +/// `use` this trait to enable its methods for `IndexMap`. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait MutableKeys: private::Sealed { + type Key; + type Value; + + /// Return item index, mutable reference to key and value + /// + /// Computes in **O(1)** time (average). + fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut Self::Key, &mut Self::Value)> + where + Q: ?Sized + Hash + Equivalent; + + /// Return mutable reference to key and value at an index. + /// + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + fn get_index_mut2(&mut self, index: usize) -> Option<(&mut Self::Key, &mut Self::Value)>; + + /// Return an iterator over the key-value pairs of the map, in their order + fn iter_mut2(&mut self) -> IterMut2<'_, Self::Key, Self::Value>; + + /// Scan through each key-value pair in the map and keep those where the + /// closure `keep` returns `true`. + /// + /// The elements are visited in order, and remaining elements keep their + /// order. + /// + /// Computes in **O(n)** time (average). + fn retain2(&mut self, keep: F) + where + F: FnMut(&mut Self::Key, &mut Self::Value) -> bool; +} + +/// Opt-in mutable access to [`IndexMap`] keys. +/// +/// See [`MutableKeys`] for more information. +impl MutableKeys for IndexMap +where + S: BuildHasher, +{ + type Key = K; + type Value = V; + + fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut K, &mut V)> + where + Q: ?Sized + Hash + Equivalent, + { + if let Some(i) = self.get_index_of(key) { + let entry = &mut self.as_entries_mut()[i]; + Some((i, &mut entry.key, &mut entry.value)) + } else { + None + } + } + + fn get_index_mut2(&mut self, index: usize) -> Option<(&mut K, &mut V)> { + self.as_entries_mut().get_mut(index).map(Bucket::muts) + } + + fn iter_mut2(&mut self) -> IterMut2<'_, Self::Key, Self::Value> { + IterMut2::new(self.as_entries_mut()) + } + + fn retain2(&mut self, keep: F) + where + F: FnMut(&mut K, &mut V) -> bool, + { + self.core.retain_in_order(keep); + } +} + +/// Opt-in mutable access to [`Entry`] keys. +/// +/// These methods expose `&mut K`, mutable references to the key as it is stored +/// in the map. +/// You are allowed to modify the keys in the map **if the modification +/// does not change the key’s hash and equality**. +/// +/// If keys are modified erroneously, you can no longer look them up. +/// This is sound (memory safe) but a logical error hazard (just like +/// implementing `PartialEq`, `Eq`, or `Hash` incorrectly would be). +/// +/// `use` this trait to enable its methods for `Entry`. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait MutableEntryKey: private::Sealed { + type Key; + + /// Gets a mutable reference to the entry's key, either within the map if occupied, + /// or else the new key that was used to find the entry. + fn key_mut(&mut self) -> &mut Self::Key; +} + +/// Opt-in mutable access to [`Entry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for Entry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + match self { + Entry::Occupied(e) => e.key_mut(), + Entry::Vacant(e) => e.key_mut(), + } + } +} + +/// Opt-in mutable access to [`OccupiedEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for OccupiedEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + +/// Opt-in mutable access to [`VacantEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for VacantEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + +/// Opt-in mutable access to [`IndexedEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for IndexedEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + +mod private { + pub trait Sealed {} + + impl Sealed for super::IndexMap {} + impl Sealed for super::Entry<'_, K, V> {} + impl Sealed for super::OccupiedEntry<'_, K, V> {} + impl Sealed for super::VacantEntry<'_, K, V> {} + impl Sealed for super::IndexedEntry<'_, K, V> {} +} diff --git a/src/serde_seq.rs b/src/map/serde_seq.rs similarity index 64% rename from src/serde_seq.rs rename to src/map/serde_seq.rs index d326a02e..602ae7dc 100644 --- a/src/serde_seq.rs +++ b/src/map/serde_seq.rs @@ -1,4 +1,4 @@ -//! Functions to serialize and deserialize an `IndexMap` as an ordered sequence. +//! Functions to serialize and deserialize an [`IndexMap`] as an ordered sequence. //! //! The default `serde` implementation serializes `IndexMap` as a normal map, //! but there is no guarantee that serialization formats will preserve the order @@ -12,13 +12,11 @@ //! # use serde_derive::{Deserialize, Serialize}; //! #[derive(Deserialize, Serialize)] //! struct Data { -//! #[serde(with = "indexmap::serde_seq")] +//! #[serde(with = "indexmap::map::serde_seq")] //! map: IndexMap, //! // ... //! } //! ``` -//! -//! Requires crate feature `"serde"` or `"serde-1"` use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use serde::ser::{Serialize, Serializer}; @@ -27,29 +25,59 @@ use core::fmt::{self, Formatter}; use core::hash::{BuildHasher, Hash}; use core::marker::PhantomData; +use crate::map::Slice as MapSlice; +use crate::serde::cautious_capacity; +use crate::set::Slice as SetSlice; use crate::IndexMap; -/// Serializes an `IndexMap` as an ordered sequence. +/// Serializes a [`map::Slice`][MapSlice] as an ordered sequence. /// -/// This function may be used in a field attribute for deriving `Serialize`: +/// This behaves like [`crate::map::serde_seq`] for `IndexMap`, serializing a sequence +/// of `(key, value)` pairs, rather than as a map that might not preserve order. +impl Serialize for MapSlice +where + K: Serialize, + V: Serialize, +{ + fn serialize(&self, serializer: T) -> Result + where + T: Serializer, + { + serializer.collect_seq(self) + } +} + +/// Serializes a [`set::Slice`][SetSlice] as an ordered sequence. +impl Serialize for SetSlice +where + T: Serialize, +{ + fn serialize(&self, serializer: Se) -> Result + where + Se: Serializer, + { + serializer.collect_seq(self) + } +} + +/// Serializes an [`IndexMap`] as an ordered sequence. +/// +/// This function may be used in a field attribute for deriving [`Serialize`]: /// /// ``` /// # use indexmap::IndexMap; /// # use serde_derive::Serialize; /// #[derive(Serialize)] /// struct Data { -/// #[serde(serialize_with = "indexmap::serde_seq::serialize")] +/// #[serde(serialize_with = "indexmap::map::serde_seq::serialize")] /// map: IndexMap, /// // ... /// } /// ``` -/// -/// Requires crate feature `"serde"` or `"serde-1"` pub fn serialize(map: &IndexMap, serializer: T) -> Result where - K: Serialize + Hash + Eq, + K: Serialize, V: Serialize, - S: BuildHasher, T: Serializer, { serializer.collect_seq(map) @@ -74,7 +102,7 @@ where where A: SeqAccess<'de>, { - let capacity = seq.size_hint().unwrap_or(0); + let capacity = cautious_capacity::(seq.size_hint()); let mut map = IndexMap::with_capacity_and_hasher(capacity, S::default()); while let Some((key, value)) = seq.next_element()? { @@ -85,22 +113,20 @@ where } } -/// Deserializes an `IndexMap` from an ordered sequence. +/// Deserializes an [`IndexMap`] from an ordered sequence. /// -/// This function may be used in a field attribute for deriving `Deserialize`: +/// This function may be used in a field attribute for deriving [`Deserialize`]: /// /// ``` /// # use indexmap::IndexMap; /// # use serde_derive::Deserialize; /// #[derive(Deserialize)] /// struct Data { -/// #[serde(deserialize_with = "indexmap::serde_seq::deserialize")] +/// #[serde(deserialize_with = "indexmap::map::serde_seq::deserialize")] /// map: IndexMap, /// // ... /// } /// ``` -/// -/// Requires crate feature `"serde"` or `"serde-1"` pub fn deserialize<'de, D, K, V, S>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, diff --git a/src/map/slice.rs b/src/map/slice.rs new file mode 100644 index 00000000..94795b70 --- /dev/null +++ b/src/map/slice.rs @@ -0,0 +1,539 @@ +use super::{ + Bucket, Entries, IndexMap, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, + ValuesMut, +}; +use crate::util::try_simplify_range; + +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::ops::{self, Bound, Index, IndexMut, RangeBounds}; + +/// A dynamically-sized slice of key-value pairs in an [`IndexMap`]. +/// +/// This supports indexed operations much like a `[(K, V)]` slice, +/// but not any hashed operations on the map keys. +/// +/// Unlike `IndexMap`, `Slice` does consider the order for [`PartialEq`] +/// and [`Eq`], and it also implements [`PartialOrd`], [`Ord`], and [`Hash`]. +#[repr(transparent)] +pub struct Slice { + pub(crate) entries: [Bucket], +} + +// SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, +// and reference lifetimes are bound together in function signatures. +#[allow(unsafe_code)] +impl Slice { + pub(super) const fn from_slice(entries: &[Bucket]) -> &Self { + unsafe { &*(entries as *const [Bucket] as *const Self) } + } + + pub(super) fn from_mut_slice(entries: &mut [Bucket]) -> &mut Self { + unsafe { &mut *(entries as *mut [Bucket] as *mut Self) } + } + + pub(super) fn from_boxed(entries: Box<[Bucket]>) -> Box { + unsafe { Box::from_raw(Box::into_raw(entries) as *mut Self) } + } + + fn into_boxed(self: Box) -> Box<[Bucket]> { + unsafe { Box::from_raw(Box::into_raw(self) as *mut [Bucket]) } + } +} + +impl Slice { + pub(crate) fn into_entries(self: Box) -> Vec> { + self.into_boxed().into_vec() + } + + /// Returns an empty slice. + pub const fn new<'a>() -> &'a Self { + Self::from_slice(&[]) + } + + /// Returns an empty mutable slice. + pub fn new_mut<'a>() -> &'a mut Self { + Self::from_mut_slice(&mut []) + } + + /// Return the number of key-value pairs in the map slice. + #[inline] + pub const fn len(&self) -> usize { + self.entries.len() + } + + /// Returns true if the map slice contains no elements. + #[inline] + pub const fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Get a key-value pair by index. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + self.entries.get(index).map(Bucket::refs) + } + + /// Get a key-value pair by index, with mutable access to the value. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_index_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { + self.entries.get_mut(index).map(Bucket::ref_mut) + } + + /// Returns a slice of key-value pairs in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_range>(&self, range: R) -> Option<&Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get(range).map(Slice::from_slice) + } + + /// Returns a mutable slice of key-value pairs in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_range_mut>(&mut self, range: R) -> Option<&mut Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get_mut(range).map(Slice::from_mut_slice) + } + + /// Get the first key-value pair. + pub fn first(&self) -> Option<(&K, &V)> { + self.entries.first().map(Bucket::refs) + } + + /// Get the first key-value pair, with mutable access to the value. + pub fn first_mut(&mut self) -> Option<(&K, &mut V)> { + self.entries.first_mut().map(Bucket::ref_mut) + } + + /// Get the last key-value pair. + pub fn last(&self) -> Option<(&K, &V)> { + self.entries.last().map(Bucket::refs) + } + + /// Get the last key-value pair, with mutable access to the value. + pub fn last_mut(&mut self) -> Option<(&K, &mut V)> { + self.entries.last_mut().map(Bucket::ref_mut) + } + + /// Divides one slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at(&self, index: usize) -> (&Self, &Self) { + let (first, second) = self.entries.split_at(index); + (Self::from_slice(first), Self::from_slice(second)) + } + + /// Divides one mutable slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at_mut(&mut self, index: usize) -> (&mut Self, &mut Self) { + let (first, second) = self.entries.split_at_mut(index); + (Self::from_mut_slice(first), Self::from_mut_slice(second)) + } + + /// Returns the first key-value pair and the rest of the slice, + /// or `None` if it is empty. + pub fn split_first(&self) -> Option<((&K, &V), &Self)> { + if let [first, rest @ ..] = &self.entries { + Some((first.refs(), Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the first key-value pair and the rest of the slice, + /// with mutable access to the value, or `None` if it is empty. + pub fn split_first_mut(&mut self) -> Option<((&K, &mut V), &mut Self)> { + if let [first, rest @ ..] = &mut self.entries { + Some((first.ref_mut(), Self::from_mut_slice(rest))) + } else { + None + } + } + + /// Returns the last key-value pair and the rest of the slice, + /// or `None` if it is empty. + pub fn split_last(&self) -> Option<((&K, &V), &Self)> { + if let [rest @ .., last] = &self.entries { + Some((last.refs(), Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the last key-value pair and the rest of the slice, + /// with mutable access to the value, or `None` if it is empty. + pub fn split_last_mut(&mut self) -> Option<((&K, &mut V), &mut Self)> { + if let [rest @ .., last] = &mut self.entries { + Some((last.ref_mut(), Self::from_mut_slice(rest))) + } else { + None + } + } + + /// Return an iterator over the key-value pairs of the map slice. + pub fn iter(&self) -> Iter<'_, K, V> { + Iter::new(&self.entries) + } + + /// Return an iterator over the key-value pairs of the map slice. + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + IterMut::new(&mut self.entries) + } + + /// Return an iterator over the keys of the map slice. + pub fn keys(&self) -> Keys<'_, K, V> { + Keys::new(&self.entries) + } + + /// Return an owning iterator over the keys of the map slice. + pub fn into_keys(self: Box) -> IntoKeys { + IntoKeys::new(self.into_entries()) + } + + /// Return an iterator over the values of the map slice. + pub fn values(&self) -> Values<'_, K, V> { + Values::new(&self.entries) + } + + /// Return an iterator over mutable references to the the values of the map slice. + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + ValuesMut::new(&mut self.entries) + } + + /// Return an owning iterator over the values of the map slice. + pub fn into_values(self: Box) -> IntoValues { + IntoValues::new(self.into_entries()) + } + + /// Search over a sorted map for a key. + /// + /// Returns the position where that key is present, or the position where it can be inserted to + /// maintain the sort. See [`slice::binary_search`] for more details. + /// + /// Computes in **O(log(n))** time, which is notably less scalable than looking the key up in + /// the map this is a slice from using [`IndexMap::get_index_of`], but this can also position + /// missing keys. + pub fn binary_search_keys(&self, x: &K) -> Result + where + K: Ord, + { + self.binary_search_by(|p, _| p.cmp(x)) + } + + /// Search over a sorted map with a comparator function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result + where + F: FnMut(&'a K, &'a V) -> Ordering, + { + self.entries.binary_search_by(move |a| f(&a.key, &a.value)) + } + + /// Search over a sorted map with an extraction function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by_key`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result + where + F: FnMut(&'a K, &'a V) -> B, + B: Ord, + { + self.binary_search_by(|k, v| f(k, v).cmp(b)) + } + + /// Returns the index of the partition point of a sorted map according to the given predicate + /// (the index of the first element of the second partition). + /// + /// See [`slice::partition_point`] for more details. + /// + /// Computes in **O(log(n))** time. + #[must_use] + pub fn partition_point

(&self, mut pred: P) -> usize + where + P: FnMut(&K, &V) -> bool, + { + self.entries + .partition_point(move |a| pred(&a.key, &a.value)) + } +} + +impl<'a, K, V> IntoIterator for &'a Slice { + type IntoIter = Iter<'a, K, V>; + type Item = (&'a K, &'a V); + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, K, V> IntoIterator for &'a mut Slice { + type IntoIter = IterMut<'a, K, V>; + type Item = (&'a K, &'a mut V); + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl IntoIterator for Box> { + type IntoIter = IntoIter; + type Item = (K, V); + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_entries()) + } +} + +impl Default for &'_ Slice { + fn default() -> Self { + Slice::from_slice(&[]) + } +} + +impl Default for &'_ mut Slice { + fn default() -> Self { + Slice::from_mut_slice(&mut []) + } +} + +impl Default for Box> { + fn default() -> Self { + Slice::from_boxed(Box::default()) + } +} + +impl Clone for Box> { + fn clone(&self) -> Self { + Slice::from_boxed(self.entries.to_vec().into_boxed_slice()) + } +} + +impl From<&Slice> for Box> { + fn from(slice: &Slice) -> Self { + Slice::from_boxed(Box::from(&slice.entries)) + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +impl PartialEq for Slice { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } +} + +impl Eq for Slice {} + +impl PartialOrd for Slice { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +impl Ord for Slice { + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + self.len().hash(state); + for (key, value) in self { + key.hash(state); + value.hash(state); + } + } +} + +impl Index for Slice { + type Output = V; + + fn index(&self, index: usize) -> &V { + &self.entries[index].value + } +} + +impl IndexMut for Slice { + fn index_mut(&mut self, index: usize) -> &mut V { + &mut self.entries[index].value + } +} + +// We can't have `impl> Index` because that conflicts +// both upstream with `Index` and downstream with `Index<&Q>`. +// Instead, we repeat the implementations for all the core range types. +macro_rules! impl_index { + ($($range:ty),*) => {$( + impl Index<$range> for IndexMap { + type Output = Slice; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.as_entries()[range]) + } + } + + impl IndexMut<$range> for IndexMap { + fn index_mut(&mut self, range: $range) -> &mut Self::Output { + Slice::from_mut_slice(&mut self.as_entries_mut()[range]) + } + } + + impl Index<$range> for Slice { + type Output = Slice; + + fn index(&self, range: $range) -> &Self { + Self::from_slice(&self.entries[range]) + } + } + + impl IndexMut<$range> for Slice { + fn index_mut(&mut self, range: $range) -> &mut Self { + Self::from_mut_slice(&mut self.entries[range]) + } + } + )*} +} +impl_index!( + ops::Range, + ops::RangeFrom, + ops::RangeFull, + ops::RangeInclusive, + ops::RangeTo, + ops::RangeToInclusive, + (Bound, Bound) +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slice_index() { + fn check( + vec_slice: &[(i32, i32)], + map_slice: &Slice, + sub_slice: &Slice, + ) { + assert_eq!(map_slice as *const _, sub_slice as *const _); + itertools::assert_equal( + vec_slice.iter().copied(), + map_slice.iter().map(|(&k, &v)| (k, v)), + ); + itertools::assert_equal(vec_slice.iter().map(|(k, _)| k), map_slice.keys()); + itertools::assert_equal(vec_slice.iter().map(|(_, v)| v), map_slice.values()); + } + + let vec: Vec<(i32, i32)> = (0..10).map(|i| (i, i * i)).collect(); + let map: IndexMap = vec.iter().cloned().collect(); + let slice = map.as_slice(); + + // RangeFull + check(&vec[..], &map[..], &slice[..]); + + for i in 0usize..10 { + // Index + assert_eq!(vec[i].1, map[i]); + assert_eq!(vec[i].1, slice[i]); + assert_eq!(map[&(i as i32)], map[i]); + assert_eq!(map[&(i as i32)], slice[i]); + + // RangeFrom + check(&vec[i..], &map[i..], &slice[i..]); + + // RangeTo + check(&vec[..i], &map[..i], &slice[..i]); + + // RangeToInclusive + check(&vec[..=i], &map[..=i], &slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check(&vec[i + 1..], &map[bounds], &slice[bounds]); + + for j in i..=10 { + // Range + check(&vec[i..j], &map[i..j], &slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check(&vec[i..=j], &map[i..=j], &slice[i..=j]); + } + } + } + + #[test] + fn slice_index_mut() { + fn check_mut( + vec_slice: &[(i32, i32)], + map_slice: &mut Slice, + sub_slice: &mut Slice, + ) { + assert_eq!(map_slice, sub_slice); + itertools::assert_equal( + vec_slice.iter().copied(), + map_slice.iter_mut().map(|(&k, &mut v)| (k, v)), + ); + itertools::assert_equal( + vec_slice.iter().map(|&(_, v)| v), + map_slice.values_mut().map(|&mut v| v), + ); + } + + let vec: Vec<(i32, i32)> = (0..10).map(|i| (i, i * i)).collect(); + let mut map: IndexMap = vec.iter().cloned().collect(); + let mut map2 = map.clone(); + let slice = map2.as_mut_slice(); + + // RangeFull + check_mut(&vec[..], &mut map[..], &mut slice[..]); + + for i in 0usize..10 { + // IndexMut + assert_eq!(&mut map[i], &mut slice[i]); + + // RangeFrom + check_mut(&vec[i..], &mut map[i..], &mut slice[i..]); + + // RangeTo + check_mut(&vec[..i], &mut map[..i], &mut slice[..i]); + + // RangeToInclusive + check_mut(&vec[..=i], &mut map[..=i], &mut slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check_mut(&vec[i + 1..], &mut map[bounds], &mut slice[bounds]); + + for j in i..=10 { + // Range + check_mut(&vec[i..j], &mut map[i..j], &mut slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check_mut(&vec[i..=j], &mut map[i..=j], &mut slice[i..=j]); + } + } + } +} diff --git a/src/map/tests.rs b/src/map/tests.rs new file mode 100644 index 00000000..9de9db1b --- /dev/null +++ b/src/map/tests.rs @@ -0,0 +1,830 @@ +use super::*; +use std::string::String; + +#[test] +fn it_works() { + let mut map = IndexMap::new(); + assert_eq!(map.is_empty(), true); + map.insert(1, ()); + map.insert(1, ()); + assert_eq!(map.len(), 1); + assert!(map.get(&1).is_some()); + assert_eq!(map.is_empty(), false); +} + +#[test] +fn new() { + let map = IndexMap::::new(); + println!("{:?}", map); + assert_eq!(map.capacity(), 0); + assert_eq!(map.len(), 0); + assert_eq!(map.is_empty(), true); +} + +#[test] +fn insert() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5]; + let not_present = [1, 3, 6, 9, 10]; + let mut map = IndexMap::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(map.len(), i); + map.insert(elt, elt); + assert_eq!(map.len(), i + 1); + assert_eq!(map.get(&elt), Some(&elt)); + assert_eq!(map[&elt], elt); + } + println!("{:?}", map); + + for &elt in ¬_present { + assert!(map.get(&elt).is_none()); + } +} + +#[test] +fn insert_full() { + let insert = vec![9, 2, 7, 1, 4, 6, 13]; + let present = vec![1, 6, 2]; + let mut map = IndexMap::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(map.len(), i); + let (index, existing) = map.insert_full(elt, elt); + assert_eq!(existing, None); + assert_eq!(Some(index), map.get_full(&elt).map(|x| x.0)); + assert_eq!(map.len(), i + 1); + } + + let len = map.len(); + for &elt in &present { + let (index, existing) = map.insert_full(elt, elt); + assert_eq!(existing, Some(elt)); + assert_eq!(Some(index), map.get_full(&elt).map(|x| x.0)); + assert_eq!(map.len(), len); + } +} + +#[test] +fn insert_2() { + let mut map = IndexMap::with_capacity(16); + + let mut keys = vec![]; + keys.extend(0..16); + keys.extend(if cfg!(miri) { 32..64 } else { 128..267 }); + + for &i in &keys { + let old_map = map.clone(); + map.insert(i, ()); + for key in old_map.keys() { + if map.get(key).is_none() { + println!("old_map: {:?}", old_map); + println!("map: {:?}", map); + panic!("did not find {} in map", key); + } + } + } + + for &i in &keys { + assert!(map.get(&i).is_some(), "did not find {}", i); + } +} + +#[test] +fn insert_order() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut map = IndexMap::new(); + + for &elt in &insert { + map.insert(elt, ()); + } + + assert_eq!(map.keys().count(), map.len()); + assert_eq!(map.keys().count(), insert.len()); + for (a, b) in insert.iter().zip(map.keys()) { + assert_eq!(a, b); + } + for (i, k) in (0..insert.len()).zip(map.keys()) { + assert_eq!(map.get_index(i).unwrap().0, k); + } +} + +#[test] +fn shift_insert() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut map = IndexMap::new(); + + for &elt in &insert { + map.shift_insert(0, elt, ()); + } + + assert_eq!(map.keys().count(), map.len()); + assert_eq!(map.keys().count(), insert.len()); + for (a, b) in insert.iter().rev().zip(map.keys()) { + assert_eq!(a, b); + } + for (i, k) in (0..insert.len()).zip(map.keys()) { + assert_eq!(map.get_index(i).unwrap().0, k); + } + + // "insert" that moves an existing entry + map.shift_insert(0, insert[0], ()); + assert_eq!(map.keys().count(), insert.len()); + assert_eq!(insert[0], map.keys()[0]); + for (a, b) in insert[1..].iter().rev().zip(map.keys().skip(1)) { + assert_eq!(a, b); + } +} + +#[test] +fn insert_sorted_bad() { + let mut map = IndexMap::new(); + map.insert(10, ()); + for i in 0..10 { + map.insert(i, ()); + } + + // The binary search will want to insert this at the end (index == len()), + // but that's only possible for *new* inserts. It should still be handled + // without panicking though, and in this case it's simple enough that we + // know the exact result. (But don't read this as an API guarantee!) + assert_eq!(map.first(), Some((&10, &()))); + map.insert_sorted(10, ()); + assert_eq!(map.last(), Some((&10, &()))); + assert!(map.keys().copied().eq(0..=10)); + + // Other out-of-order entries can also "insert" to a binary-searched + // position, moving in either direction. + map.move_index(5, 0); + map.move_index(6, 10); + assert_eq!(map.first(), Some((&5, &()))); + assert_eq!(map.last(), Some((&6, &()))); + map.insert_sorted(5, ()); // moves back up + map.insert_sorted(6, ()); // moves back down + assert!(map.keys().copied().eq(0..=10)); +} + +#[test] +fn grow() { + let insert = [0, 4, 2, 12, 8, 7, 11]; + let not_present = [1, 3, 6, 9, 10]; + let mut map = IndexMap::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(map.len(), i); + map.insert(elt, elt); + assert_eq!(map.len(), i + 1); + assert_eq!(map.get(&elt), Some(&elt)); + assert_eq!(map[&elt], elt); + } + + println!("{:?}", map); + for &elt in &insert { + map.insert(elt * 10, elt); + } + for &elt in &insert { + map.insert(elt * 100, elt); + } + for (i, &elt) in insert.iter().cycle().enumerate().take(100) { + map.insert(elt * 100 + i as i32, elt); + } + println!("{:?}", map); + for &elt in ¬_present { + assert!(map.get(&elt).is_none()); + } +} + +#[test] +fn reserve() { + let mut map = IndexMap::::new(); + assert_eq!(map.capacity(), 0); + map.reserve(100); + let capacity = map.capacity(); + assert!(capacity >= 100); + for i in 0..capacity { + assert_eq!(map.len(), i); + map.insert(i, i * i); + assert_eq!(map.len(), i + 1); + assert_eq!(map.capacity(), capacity); + assert_eq!(map.get(&i), Some(&(i * i))); + } + map.insert(capacity, std::usize::MAX); + assert_eq!(map.len(), capacity + 1); + assert!(map.capacity() > capacity); + assert_eq!(map.get(&capacity), Some(&std::usize::MAX)); +} + +#[test] +fn try_reserve() { + let mut map = IndexMap::::new(); + assert_eq!(map.capacity(), 0); + assert_eq!(map.try_reserve(100), Ok(())); + assert!(map.capacity() >= 100); + assert!(map.try_reserve(usize::MAX).is_err()); +} + +#[test] +fn shrink_to_fit() { + let mut map = IndexMap::::new(); + assert_eq!(map.capacity(), 0); + for i in 0..100 { + assert_eq!(map.len(), i); + map.insert(i, i * i); + assert_eq!(map.len(), i + 1); + assert!(map.capacity() >= i + 1); + assert_eq!(map.get(&i), Some(&(i * i))); + map.shrink_to_fit(); + assert_eq!(map.len(), i + 1); + assert_eq!(map.capacity(), i + 1); + assert_eq!(map.get(&i), Some(&(i * i))); + } +} + +#[test] +fn remove() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut map = IndexMap::new(); + + for &elt in &insert { + map.insert(elt, elt); + } + + assert_eq!(map.keys().count(), map.len()); + assert_eq!(map.keys().count(), insert.len()); + for (a, b) in insert.iter().zip(map.keys()) { + assert_eq!(a, b); + } + + let remove_fail = [99, 77]; + let remove = [4, 12, 8, 7]; + + for &key in &remove_fail { + assert!(map.swap_remove_full(&key).is_none()); + } + println!("{:?}", map); + for &key in &remove { + //println!("{:?}", map); + let index = map.get_full(&key).unwrap().0; + assert_eq!(map.swap_remove_full(&key), Some((index, key, key))); + } + println!("{:?}", map); + + for key in &insert { + assert_eq!(map.get(key).is_some(), !remove.contains(key)); + } + assert_eq!(map.len(), insert.len() - remove.len()); + assert_eq!(map.keys().count(), insert.len() - remove.len()); +} + +#[test] +fn remove_to_empty() { + let mut map = indexmap! { 0 => 0, 4 => 4, 5 => 5 }; + map.swap_remove(&5).unwrap(); + map.swap_remove(&4).unwrap(); + map.swap_remove(&0).unwrap(); + assert!(map.is_empty()); +} + +#[test] +fn swap_remove_index() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut map = IndexMap::new(); + + for &elt in &insert { + map.insert(elt, elt * 2); + } + + let mut vector = insert.to_vec(); + let remove_sequence = &[3, 3, 10, 4, 5, 4, 3, 0, 1]; + + // check that the same swap remove sequence on vec and map + // have the same result. + for &rm in remove_sequence { + let out_vec = vector.swap_remove(rm); + let (out_map, _) = map.swap_remove_index(rm).unwrap(); + assert_eq!(out_vec, out_map); + } + assert_eq!(vector.len(), map.len()); + for (a, b) in vector.iter().zip(map.keys()) { + assert_eq!(a, b); + } +} + +#[test] +fn partial_eq_and_eq() { + let mut map_a = IndexMap::new(); + map_a.insert(1, "1"); + map_a.insert(2, "2"); + let mut map_b = map_a.clone(); + assert_eq!(map_a, map_b); + map_b.swap_remove(&1); + assert_ne!(map_a, map_b); + + let map_c: IndexMap<_, String> = map_b.into_iter().map(|(k, v)| (k, v.into())).collect(); + assert_ne!(map_a, map_c); + assert_ne!(map_c, map_a); +} + +#[test] +fn extend() { + let mut map = IndexMap::new(); + map.extend(vec![(&1, &2), (&3, &4)]); + map.extend(vec![(5, 6)]); + assert_eq!( + map.into_iter().collect::>(), + vec![(1, 2), (3, 4), (5, 6)] + ); +} + +#[test] +fn entry() { + let mut map = IndexMap::new(); + + map.insert(1, "1"); + map.insert(2, "2"); + { + let e = map.entry(3); + assert_eq!(e.index(), 2); + let e = e.or_insert("3"); + assert_eq!(e, &"3"); + } + + let e = map.entry(2); + assert_eq!(e.index(), 1); + assert_eq!(e.key(), &2); + match e { + Entry::Occupied(ref e) => assert_eq!(e.get(), &"2"), + Entry::Vacant(_) => panic!(), + } + assert_eq!(e.or_insert("4"), &"2"); +} + +#[test] +fn entry_and_modify() { + let mut map = IndexMap::new(); + + map.insert(1, "1"); + map.entry(1).and_modify(|x| *x = "2"); + assert_eq!(Some(&"2"), map.get(&1)); + + map.entry(2).and_modify(|x| *x = "doesn't exist"); + assert_eq!(None, map.get(&2)); +} + +#[test] +fn entry_or_default() { + let mut map = IndexMap::new(); + + #[derive(Debug, PartialEq)] + enum TestEnum { + DefaultValue, + NonDefaultValue, + } + + impl Default for TestEnum { + fn default() -> Self { + TestEnum::DefaultValue + } + } + + map.insert(1, TestEnum::NonDefaultValue); + assert_eq!(&mut TestEnum::NonDefaultValue, map.entry(1).or_default()); + + assert_eq!(&mut TestEnum::DefaultValue, map.entry(2).or_default()); +} + +#[test] +fn occupied_entry_key() { + // These keys match hash and equality, but their addresses are distinct. + let (k1, k2) = (&mut 1, &mut 1); + let k1_ptr = k1 as *const i32; + let k2_ptr = k2 as *const i32; + assert_ne!(k1_ptr, k2_ptr); + + let mut map = IndexMap::new(); + map.insert(k1, "value"); + match map.entry(k2) { + Entry::Occupied(ref e) => { + // `OccupiedEntry::key` should reference the key in the map, + // not the key that was used to find the entry. + let ptr = *e.key() as *const i32; + assert_eq!(ptr, k1_ptr); + assert_ne!(ptr, k2_ptr); + } + Entry::Vacant(_) => panic!(), + } +} + +#[test] +fn get_index_entry() { + let mut map = IndexMap::new(); + + assert!(map.get_index_entry(0).is_none()); + assert!(map.first_entry().is_none()); + assert!(map.last_entry().is_none()); + + map.insert(0, "0"); + map.insert(1, "1"); + map.insert(2, "2"); + map.insert(3, "3"); + + assert!(map.get_index_entry(4).is_none()); + + { + let e = map.get_index_entry(1).unwrap(); + assert_eq!(*e.key(), 1); + assert_eq!(*e.get(), "1"); + assert_eq!(e.swap_remove(), "1"); + } + + { + let mut e = map.get_index_entry(1).unwrap(); + assert_eq!(*e.key(), 3); + assert_eq!(*e.get(), "3"); + assert_eq!(e.insert("4"), "3"); + } + + assert_eq!(*map.get(&3).unwrap(), "4"); + + { + let e = map.first_entry().unwrap(); + assert_eq!(*e.key(), 0); + assert_eq!(*e.get(), "0"); + } + + { + let e = map.last_entry().unwrap(); + assert_eq!(*e.key(), 2); + assert_eq!(*e.get(), "2"); + } +} + +#[test] +fn from_entries() { + let mut map = IndexMap::from([(1, "1"), (2, "2"), (3, "3")]); + + { + let e = match map.entry(1) { + Entry::Occupied(e) => IndexedEntry::from(e), + Entry::Vacant(_) => panic!(), + }; + assert_eq!(e.index(), 0); + assert_eq!(*e.key(), 1); + assert_eq!(*e.get(), "1"); + } + + { + let e = match map.get_index_entry(1) { + Some(e) => OccupiedEntry::from(e), + None => panic!(), + }; + assert_eq!(e.index(), 1); + assert_eq!(*e.key(), 2); + assert_eq!(*e.get(), "2"); + } +} + +#[test] +fn keys() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: IndexMap<_, _> = vec.into_iter().collect(); + let keys: Vec<_> = map.keys().copied().collect(); + assert_eq!(keys.len(), 3); + assert!(keys.contains(&1)); + assert!(keys.contains(&2)); + assert!(keys.contains(&3)); +} + +#[test] +fn into_keys() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: IndexMap<_, _> = vec.into_iter().collect(); + let keys: Vec = map.into_keys().collect(); + assert_eq!(keys.len(), 3); + assert!(keys.contains(&1)); + assert!(keys.contains(&2)); + assert!(keys.contains(&3)); +} + +#[test] +fn values() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: IndexMap<_, _> = vec.into_iter().collect(); + let values: Vec<_> = map.values().copied().collect(); + assert_eq!(values.len(), 3); + assert!(values.contains(&'a')); + assert!(values.contains(&'b')); + assert!(values.contains(&'c')); +} + +#[test] +fn values_mut() { + let vec = vec![(1, 1), (2, 2), (3, 3)]; + let mut map: IndexMap<_, _> = vec.into_iter().collect(); + for value in map.values_mut() { + *value *= 2 + } + let values: Vec<_> = map.values().copied().collect(); + assert_eq!(values.len(), 3); + assert!(values.contains(&2)); + assert!(values.contains(&4)); + assert!(values.contains(&6)); +} + +#[test] +fn into_values() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: IndexMap<_, _> = vec.into_iter().collect(); + let values: Vec = map.into_values().collect(); + assert_eq!(values.len(), 3); + assert!(values.contains(&'a')); + assert!(values.contains(&'b')); + assert!(values.contains(&'c')); +} + +#[test] +fn drain_range() { + // Test the various heuristics of `erase_indices` + for range in [ + 0..0, // nothing erased + 10..90, // reinsert the few kept (..10 and 90..) + 80..90, // update the few to adjust (80..) + 20..30, // sweep everything + ] { + let mut vec = Vec::from_iter(0..100); + let mut map: IndexMap = (0..100).map(|i| (i, ())).collect(); + drop(vec.drain(range.clone())); + drop(map.drain(range)); + assert!(vec.iter().eq(map.keys())); + for (i, x) in vec.iter().enumerate() { + assert_eq!(map.get_index_of(x), Some(i)); + } + } +} + +#[test] +#[cfg(feature = "std")] +fn from_array() { + let map = IndexMap::from([(1, 2), (3, 4)]); + let mut expected = IndexMap::new(); + expected.insert(1, 2); + expected.insert(3, 4); + + assert_eq!(map, expected) +} + +#[test] +fn iter_default() { + struct K; + struct V; + fn assert_default() + where + T: Default + Iterator, + { + assert!(T::default().next().is_none()); + } + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); + assert_default::>(); +} + +#[test] +fn test_binary_search_by() { + // adapted from std's test for binary_search + let b: IndexMap<_, i32> = [] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&5)), Err(0)); + + let b: IndexMap<_, i32> = [4] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&3)), Err(0)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&4)), Ok(0)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&5)), Err(1)); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&5)), Err(3)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&6)), Ok(3)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&7)), Err(4)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&8)), Ok(4)); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&9)), Err(6)); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 7, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&6)), Ok(3)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&5)), Err(3)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&8)), Ok(5)); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&7)), Err(5)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&0)), Err(0)); + + let b: IndexMap<_, i32> = [1, 3, 3, 3, 7] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&0)), Err(0)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&1)), Ok(0)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&2)), Err(1)); + assert!(match b.binary_search_by(|_, x| x.cmp(&3)) { + Ok(1..=3) => true, + _ => false, + }); + assert!(match b.binary_search_by(|_, x| x.cmp(&3)) { + Ok(1..=3) => true, + _ => false, + }); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&4)), Err(4)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&5)), Err(4)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&6)), Err(4)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&7)), Ok(4)); + assert_eq!(b.binary_search_by(|_, x| x.cmp(&8)), Err(5)); +} + +#[test] +fn test_binary_search_by_key() { + // adapted from std's test for binary_search + let b: IndexMap<_, i32> = [] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&5, |_, &x| x), Err(0)); + + let b: IndexMap<_, i32> = [4] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&3, |_, &x| x), Err(0)); + assert_eq!(b.binary_search_by_key(&4, |_, &x| x), Ok(0)); + assert_eq!(b.binary_search_by_key(&5, |_, &x| x), Err(1)); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&5, |_, &x| x), Err(3)); + assert_eq!(b.binary_search_by_key(&6, |_, &x| x), Ok(3)); + assert_eq!(b.binary_search_by_key(&7, |_, &x| x), Err(4)); + assert_eq!(b.binary_search_by_key(&8, |_, &x| x), Ok(4)); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&9, |_, &x| x), Err(6)); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 7, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&6, |_, &x| x), Ok(3)); + assert_eq!(b.binary_search_by_key(&5, |_, &x| x), Err(3)); + assert_eq!(b.binary_search_by_key(&8, |_, &x| x), Ok(5)); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&7, |_, &x| x), Err(5)); + assert_eq!(b.binary_search_by_key(&0, |_, &x| x), Err(0)); + + let b: IndexMap<_, i32> = [1, 3, 3, 3, 7] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.binary_search_by_key(&0, |_, &x| x), Err(0)); + assert_eq!(b.binary_search_by_key(&1, |_, &x| x), Ok(0)); + assert_eq!(b.binary_search_by_key(&2, |_, &x| x), Err(1)); + assert!(match b.binary_search_by_key(&3, |_, &x| x) { + Ok(1..=3) => true, + _ => false, + }); + assert!(match b.binary_search_by_key(&3, |_, &x| x) { + Ok(1..=3) => true, + _ => false, + }); + assert_eq!(b.binary_search_by_key(&4, |_, &x| x), Err(4)); + assert_eq!(b.binary_search_by_key(&5, |_, &x| x), Err(4)); + assert_eq!(b.binary_search_by_key(&6, |_, &x| x), Err(4)); + assert_eq!(b.binary_search_by_key(&7, |_, &x| x), Ok(4)); + assert_eq!(b.binary_search_by_key(&8, |_, &x| x), Err(5)); +} + +#[test] +fn test_partition_point() { + // adapted from std's test for partition_point + let b: IndexMap<_, i32> = [] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 5), 0); + + let b: IndexMap<_, i32> = [4] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 3), 0); + assert_eq!(b.partition_point(|_, &x| x < 4), 0); + assert_eq!(b.partition_point(|_, &x| x < 5), 1); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 5), 3); + assert_eq!(b.partition_point(|_, &x| x < 6), 3); + assert_eq!(b.partition_point(|_, &x| x < 7), 4); + assert_eq!(b.partition_point(|_, &x| x < 8), 4); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 9), 6); + + let b: IndexMap<_, i32> = [1, 2, 4, 6, 7, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 6), 3); + assert_eq!(b.partition_point(|_, &x| x < 5), 3); + assert_eq!(b.partition_point(|_, &x| x < 8), 5); + + let b: IndexMap<_, i32> = [1, 2, 4, 5, 6, 8, 9] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 7), 5); + assert_eq!(b.partition_point(|_, &x| x < 0), 0); + + let b: IndexMap<_, i32> = [1, 3, 3, 3, 7] + .into_iter() + .enumerate() + .map(|(i, x)| (i + 100, x)) + .collect(); + assert_eq!(b.partition_point(|_, &x| x < 0), 0); + assert_eq!(b.partition_point(|_, &x| x < 1), 0); + assert_eq!(b.partition_point(|_, &x| x < 2), 1); + assert_eq!(b.partition_point(|_, &x| x < 3), 1); + assert_eq!(b.partition_point(|_, &x| x < 4), 4); + assert_eq!(b.partition_point(|_, &x| x < 5), 4); + assert_eq!(b.partition_point(|_, &x| x < 6), 4); + assert_eq!(b.partition_point(|_, &x| x < 7), 4); + assert_eq!(b.partition_point(|_, &x| x < 8), 5); +} + +macro_rules! move_index_oob { + ($test:ident, $from:expr, $to:expr) => { + #[test] + #[should_panic(expected = "index out of bounds")] + fn $test() { + let mut map: IndexMap = (0..10).map(|k| (k, ())).collect(); + map.move_index($from, $to); + } + }; +} +move_index_oob!(test_move_index_out_of_bounds_0_10, 0, 10); +move_index_oob!(test_move_index_out_of_bounds_0_max, 0, usize::MAX); +move_index_oob!(test_move_index_out_of_bounds_10_0, 10, 0); +move_index_oob!(test_move_index_out_of_bounds_max_0, usize::MAX, 0); diff --git a/src/mutable_keys.rs b/src/mutable_keys.rs deleted file mode 100644 index 0688441e..00000000 --- a/src/mutable_keys.rs +++ /dev/null @@ -1,75 +0,0 @@ -use core::hash::{BuildHasher, Hash}; - -use super::{Equivalent, IndexMap}; - -pub struct PrivateMarker {} - -/// Opt-in mutable access to keys. -/// -/// These methods expose `&mut K`, mutable references to the key as it is stored -/// in the map. -/// You are allowed to modify the keys in the hashmap **if the modifcation -/// does not change the key’s hash and equality**. -/// -/// If keys are modified erronously, you can no longer look them up. -/// This is sound (memory safe) but a logical error hazard (just like -/// implementing PartialEq, Eq, or Hash incorrectly would be). -/// -/// `use` this trait to enable its methods for `IndexMap`. -pub trait MutableKeys { - type Key; - type Value; - - /// Return item index, mutable reference to key and value - fn get_full_mut2( - &mut self, - key: &Q, - ) -> Option<(usize, &mut Self::Key, &mut Self::Value)> - where - Q: Hash + Equivalent; - - /// Scan through each key-value pair in the map and keep those where the - /// closure `keep` returns `true`. - /// - /// The elements are visited in order, and remaining elements keep their - /// order. - /// - /// Computes in **O(n)** time (average). - fn retain2(&mut self, keep: F) - where - F: FnMut(&mut Self::Key, &mut Self::Value) -> bool; - - /// This method is not useful in itself – it is there to “seal” the trait - /// for external implementation, so that we can add methods without - /// causing breaking changes. - fn __private_marker(&self) -> PrivateMarker; -} - -/// Opt-in mutable access to keys. -/// -/// See [`MutableKeys`](trait.MutableKeys.html) for more information. -impl MutableKeys for IndexMap -where - K: Eq + Hash, - S: BuildHasher, -{ - type Key = K; - type Value = V; - fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut K, &mut V)> - where - Q: Hash + Equivalent, - { - self.get_full_mut2_impl(key) - } - - fn retain2(&mut self, keep: F) - where - F: FnMut(&mut K, &mut V) -> bool, - { - self.retain_mut(keep) - } - - fn __private_marker(&self) -> PrivateMarker { - PrivateMarker {} - } -} diff --git a/src/rayon/map.rs b/src/rayon/map.rs index ed4c74e4..8236cf70 100644 --- a/src/rayon/map.rs +++ b/src/rayon/map.rs @@ -1,24 +1,24 @@ -//! Parallel iterator types for `IndexMap` with [rayon](https://docs.rs/rayon/1.0/rayon). +//! Parallel iterator types for [`IndexMap`] with [`rayon`][::rayon]. //! //! You will rarely need to interact with this module directly unless you need to name one of the //! iterator types. -//! -//! Requires crate feature `"rayon"` use super::collect; use rayon::iter::plumbing::{Consumer, ProducerCallback, UnindexedConsumer}; use rayon::prelude::*; use crate::vec::Vec; +use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; +use core::ops::RangeBounds; +use crate::map::Slice; use crate::Bucket; use crate::Entries; use crate::IndexMap; -/// Requires crate feature `"rayon"`. impl IntoParallelIterator for IndexMap where K: Send, @@ -34,13 +34,25 @@ where } } -/// A parallel owning iterator over the entries of a `IndexMap`. -/// -/// This `struct` is created by the [`into_par_iter`] method on [`IndexMap`] -/// (provided by rayon's `IntoParallelIterator` trait). See its documentation for more. +impl IntoParallelIterator for Box> +where + K: Send, + V: Send, +{ + type Item = (K, V); + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } +} + +/// A parallel owning iterator over the entries of an [`IndexMap`]. /// -/// [`into_par_iter`]: ../struct.IndexMap.html#method.into_par_iter -/// [`IndexMap`]: ../struct.IndexMap.html +/// This `struct` is created by the [`IndexMap::into_par_iter`] method +/// (provided by rayon's [`IntoParallelIterator`] trait). See its documentation for more. pub struct IntoParIter { entries: Vec>, } @@ -62,7 +74,6 @@ impl IndexedParallelIterator for IntoParIter { indexed_parallel_iterator_methods!(Bucket::key_value); } -/// Requires crate feature `"rayon"`. impl<'a, K, V, S> IntoParallelIterator for &'a IndexMap where K: Sync, @@ -78,13 +89,27 @@ where } } -/// A parallel iterator over the entries of a `IndexMap`. +impl<'a, K, V> IntoParallelIterator for &'a Slice +where + K: Sync, + V: Sync, +{ + type Item = (&'a K, &'a V); + type Iter = ParIter<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: &self.entries, + } + } +} + +/// A parallel iterator over the entries of an [`IndexMap`]. /// -/// This `struct` is created by the [`par_iter`] method on [`IndexMap`] -/// (provided by rayon's `IntoParallelRefIterator` trait). See its documentation for more. +/// This `struct` is created by the [`IndexMap::par_iter`] method +/// (provided by rayon's [`IntoParallelRefIterator`] trait). See its documentation for more. /// -/// [`par_iter`]: ../struct.IndexMap.html#method.par_iter -/// [`IndexMap`]: ../struct.IndexMap.html +/// [`IndexMap::par_iter`]: ../struct.IndexMap.html#method.par_iter pub struct ParIter<'a, K, V> { entries: &'a [Bucket], } @@ -112,7 +137,6 @@ impl IndexedParallelIterator for ParIter<'_, K, V> { indexed_parallel_iterator_methods!(Bucket::refs); } -/// Requires crate feature `"rayon"`. impl<'a, K, V, S> IntoParallelIterator for &'a mut IndexMap where K: Sync + Send, @@ -128,17 +152,38 @@ where } } -/// A parallel mutable iterator over the entries of a `IndexMap`. +impl<'a, K, V> IntoParallelIterator for &'a mut Slice +where + K: Sync + Send, + V: Send, +{ + type Item = (&'a K, &'a mut V); + type Iter = ParIterMut<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIterMut { + entries: &mut self.entries, + } + } +} + +/// A parallel mutable iterator over the entries of an [`IndexMap`]. /// -/// This `struct` is created by the [`par_iter_mut`] method on [`IndexMap`] -/// (provided by rayon's `IntoParallelRefMutIterator` trait). See its documentation for more. +/// This `struct` is created by the [`IndexMap::par_iter_mut`] method +/// (provided by rayon's [`IntoParallelRefMutIterator`] trait). See its documentation for more. /// -/// [`par_iter_mut`]: ../struct.IndexMap.html#method.par_iter_mut -/// [`IndexMap`]: ../struct.IndexMap.html +/// [`IndexMap::par_iter_mut`]: ../struct.IndexMap.html#method.par_iter_mut pub struct ParIterMut<'a, K, V> { entries: &'a mut [Bucket], } +impl fmt::Debug for ParIterMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.entries.iter().map(Bucket::refs); + f.debug_list().entries(iter).finish() + } +} + impl<'a, K: Sync + Send, V: Send> ParallelIterator for ParIterMut<'a, K, V> { type Item = (&'a K, &'a mut V); @@ -149,6 +194,41 @@ impl IndexedParallelIterator for ParIterMut<'_, K, V> { indexed_parallel_iterator_methods!(Bucket::ref_mut); } +impl<'a, K, V, S> ParallelDrainRange for &'a mut IndexMap +where + K: Send, + V: Send, +{ + type Item = (K, V); + type Iter = ParDrain<'a, K, V>; + + fn par_drain>(self, range: R) -> Self::Iter { + ParDrain { + entries: self.core.par_drain(range), + } + } +} + +/// A parallel draining iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`IndexMap::par_drain`] method +/// (provided by rayon's [`ParallelDrainRange`] trait). See its documentation for more. +/// +/// [`IndexMap::par_drain`]: ../struct.IndexMap.html#method.par_drain +pub struct ParDrain<'a, K: Send, V: Send> { + entries: rayon::vec::Drain<'a, Bucket>, +} + +impl ParallelIterator for ParDrain<'_, K, V> { + type Item = (K, V); + + parallel_iterator_methods!(Bucket::key_value); +} + +impl IndexedParallelIterator for ParDrain<'_, K, V> { + indexed_parallel_iterator_methods!(Bucket::key_value); +} + /// Parallel iterator methods and other parallel methods. /// /// The following methods **require crate feature `"rayon"`**. @@ -180,6 +260,37 @@ where } } +/// Parallel iterator methods and other parallel methods. +/// +/// The following methods **require crate feature `"rayon"`**. +/// +/// See also the `IntoParallelIterator` implementations. +impl Slice +where + K: Sync, + V: Sync, +{ + /// Return a parallel iterator over the keys of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_keys(&self) -> ParKeys<'_, K, V> { + ParKeys { + entries: &self.entries, + } + } + + /// Return a parallel iterator over the values of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_values(&self) -> ParValues<'_, K, V> { + ParValues { + entries: &self.entries, + } + } +} + impl IndexMap where K: Hash + Eq + Sync, @@ -201,13 +312,10 @@ where } } -/// A parallel iterator over the keys of a `IndexMap`. -/// -/// This `struct` is created by the [`par_keys`] method on [`IndexMap`]. See its -/// documentation for more. +/// A parallel iterator over the keys of an [`IndexMap`]. /// -/// [`par_keys`]: ../struct.IndexMap.html#method.par_keys -/// [`IndexMap`]: ../struct.IndexMap.html +/// This `struct` is created by the [`IndexMap::par_keys`] method. +/// See its documentation for more. pub struct ParKeys<'a, K, V> { entries: &'a [Bucket], } @@ -235,13 +343,10 @@ impl IndexedParallelIterator for ParKeys<'_, K, V> { indexed_parallel_iterator_methods!(Bucket::key_ref); } -/// A parallel iterator over the values of a `IndexMap`. -/// -/// This `struct` is created by the [`par_values`] method on [`IndexMap`]. See its -/// documentation for more. +/// A parallel iterator over the values of an [`IndexMap`]. /// -/// [`par_values`]: ../struct.IndexMap.html#method.par_values -/// [`IndexMap`]: ../struct.IndexMap.html +/// This `struct` is created by the [`IndexMap::par_values`] method. +/// See its documentation for more. pub struct ParValues<'a, K, V> { entries: &'a [Bucket], } @@ -269,13 +374,12 @@ impl IndexedParallelIterator for ParValues<'_, K, V> { indexed_parallel_iterator_methods!(Bucket::value_ref); } -/// Requires crate feature `"rayon"`. impl IndexMap where K: Send, V: Send, { - /// Return a parallel iterator over mutable references to the the values of the map + /// Return a parallel iterator over mutable references to the values of the map /// /// While parallel iterators can process items in any order, their relative order /// in the map is still preserved for operations like `reduce` and `collect`. @@ -286,11 +390,26 @@ where } } +impl Slice +where + K: Send, + V: Send, +{ + /// Return a parallel iterator over mutable references to the the values of the map slice. + /// + /// While parallel iterators can process items in any order, their relative order + /// in the slice is still preserved for operations like `reduce` and `collect`. + pub fn par_values_mut(&mut self) -> ParValuesMut<'_, K, V> { + ParValuesMut { + entries: &mut self.entries, + } + } +} + impl IndexMap where - K: Hash + Eq + Send, + K: Send, V: Send, - S: BuildHasher, { /// Sort the map’s key-value pairs in parallel, by the default ordering of the keys. pub fn par_sort_keys(&mut self) @@ -303,7 +422,7 @@ where } /// Sort the map’s key-value pairs in place and in parallel, using the comparison - /// function `compare`. + /// function `cmp`. /// /// The comparison function receives two key and value pairs to compare (you /// can sort by keys or values or their combination as needed). @@ -316,7 +435,7 @@ where }); } - /// Sort the key-value pairs of the map in parallel and return a by value parallel + /// Sort the key-value pairs of the map in parallel and return a by-value parallel /// iterator of the key-value pairs with the result. pub fn par_sorted_by(self, cmp: F) -> IntoParIter where @@ -326,19 +445,70 @@ where entries.par_sort_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); IntoParIter { entries } } + + /// Sort the map's key-value pairs in parallel, by the default ordering of the keys. + pub fn par_sort_unstable_keys(&mut self) + where + K: Ord, + { + self.with_entries(|entries| { + entries.par_sort_unstable_by(|a, b| K::cmp(&a.key, &b.key)); + }); + } + + /// Sort the map's key-value pairs in place and in parallel, using the comparison + /// function `cmp`. + /// + /// The comparison function receives two key and value pairs to compare (you + /// can sort by keys or values or their combination as needed). + pub fn par_sort_unstable_by(&mut self, cmp: F) + where + F: Fn(&K, &V, &K, &V) -> Ordering + Sync, + { + self.with_entries(|entries| { + entries.par_sort_unstable_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); + }); + } + + /// Sort the key-value pairs of the map in parallel and return a by-value parallel + /// iterator of the key-value pairs with the result. + pub fn par_sorted_unstable_by(self, cmp: F) -> IntoParIter + where + F: Fn(&K, &V, &K, &V) -> Ordering + Sync, + { + let mut entries = self.into_entries(); + entries.par_sort_unstable_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); + IntoParIter { entries } + } + + /// Sort the map’s key-value pairs in place and in parallel, using a sort-key extraction + /// function. + pub fn par_sort_by_cached_key(&mut self, sort_key: F) + where + T: Ord + Send, + F: Fn(&K, &V) -> T + Sync, + { + self.with_entries(move |entries| { + entries.par_sort_by_cached_key(move |a| sort_key(&a.key, &a.value)); + }); + } } -/// A parallel mutable iterator over the values of a `IndexMap`. -/// -/// This `struct` is created by the [`par_values_mut`] method on [`IndexMap`]. See its -/// documentation for more. +/// A parallel mutable iterator over the values of an [`IndexMap`]. /// -/// [`par_values_mut`]: ../struct.IndexMap.html#method.par_values_mut -/// [`IndexMap`]: ../struct.IndexMap.html +/// This `struct` is created by the [`IndexMap::par_values_mut`] method. +/// See its documentation for more. pub struct ParValuesMut<'a, K, V> { entries: &'a mut [Bucket], } +impl fmt::Debug for ParValuesMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.entries.iter().map(Bucket::value_ref); + f.debug_list().entries(iter).finish() + } +} + impl<'a, K: Send, V: Send> ParallelIterator for ParValuesMut<'a, K, V> { type Item = &'a mut V; @@ -349,7 +519,6 @@ impl IndexedParallelIterator for ParValuesMut<'_, K, V> { indexed_parallel_iterator_methods!(Bucket::value_mut); } -/// Requires crate feature `"rayon"`. impl FromParallelIterator<(K, V)> for IndexMap where K: Eq + Hash + Send, @@ -370,7 +539,6 @@ where } } -/// Requires crate feature `"rayon"`. impl ParallelExtend<(K, V)> for IndexMap where K: Eq + Hash + Send, @@ -387,7 +555,6 @@ where } } -/// Requires crate feature `"rayon"`. impl<'a, K: 'a, V: 'a, S> ParallelExtend<(&'a K, &'a V)> for IndexMap where K: Copy + Eq + Hash + Send + Sync, @@ -464,7 +631,7 @@ mod tests { fn keys() { let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; let map: IndexMap<_, _> = vec.into_par_iter().collect(); - let keys: Vec<_> = map.par_keys().cloned().collect(); + let keys: Vec<_> = map.par_keys().copied().collect(); assert_eq!(keys.len(), 3); assert!(keys.contains(&1)); assert!(keys.contains(&2)); @@ -475,7 +642,7 @@ mod tests { fn values() { let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; let map: IndexMap<_, _> = vec.into_par_iter().collect(); - let values: Vec<_> = map.par_values().cloned().collect(); + let values: Vec<_> = map.par_values().copied().collect(); assert_eq!(values.len(), 3); assert!(values.contains(&'a')); assert!(values.contains(&'b')); @@ -487,7 +654,7 @@ mod tests { let vec = vec![(1, 1), (2, 2), (3, 3)]; let mut map: IndexMap<_, _> = vec.into_par_iter().collect(); map.par_values_mut().for_each(|value| *value *= 2); - let values: Vec<_> = map.par_values().cloned().collect(); + let values: Vec<_> = map.par_values().copied().collect(); assert_eq!(values.len(), 3); assert!(values.contains(&2)); assert!(values.contains(&4)); diff --git a/src/rayon/mod.rs b/src/rayon/mod.rs index 57c810be..84ce02db 100644 --- a/src/rayon/mod.rs +++ b/src/rayon/mod.rs @@ -1,83 +1,16 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "rayon")))] + use rayon::prelude::*; -#[cfg(not(has_std))] use alloc::collections::LinkedList; -#[cfg(has_std)] -use std::collections::LinkedList; - use crate::vec::Vec; -// generate `ParallelIterator` methods by just forwarding to the underlying -// self.entries and mapping its elements. -macro_rules! parallel_iterator_methods { - // $map_elt is the mapping function from the underlying iterator's element - ($map_elt:expr) => { - fn drive_unindexed(self, consumer: C) -> C::Result - where - C: UnindexedConsumer, - { - self.entries - .into_par_iter() - .map($map_elt) - .drive_unindexed(consumer) - } - - // NB: This allows indexed collection, e.g. directly into a `Vec`, but the - // underlying iterator must really be indexed. We should remove this if we - // start having tombstones that must be filtered out. - fn opt_len(&self) -> Option { - Some(self.entries.len()) - } - }; -} - -// generate `IndexedParallelIterator` methods by just forwarding to the underlying -// self.entries and mapping its elements. -macro_rules! indexed_parallel_iterator_methods { - // $map_elt is the mapping function from the underlying iterator's element - ($map_elt:expr) => { - fn drive(self, consumer: C) -> C::Result - where - C: Consumer, - { - self.entries.into_par_iter().map($map_elt).drive(consumer) - } - - fn len(&self) -> usize { - self.entries.len() - } - - fn with_producer(self, callback: CB) -> CB::Output - where - CB: ProducerCallback, - { - self.entries - .into_par_iter() - .map($map_elt) - .with_producer(callback) - } - }; -} - pub mod map; pub mod set; // This form of intermediate collection is also how Rayon collects `HashMap`. // Note that the order will also be preserved! fn collect(iter: I) -> LinkedList> { - iter.into_par_iter() - .fold(Vec::new, |mut vec, elem| { - vec.push(elem); - vec - }) - .map(|vec| { - let mut list = LinkedList::new(); - list.push_back(vec); - list - }) - .reduce(LinkedList::new, |mut list1, mut list2| { - list1.append(&mut list2); - list1 - }) + iter.into_par_iter().collect_vec_list() } diff --git a/src/rayon/set.rs b/src/rayon/set.rs index f6e08d48..3904234b 100644 --- a/src/rayon/set.rs +++ b/src/rayon/set.rs @@ -1,25 +1,25 @@ -//! Parallel iterator types for `IndexSet` with [rayon](https://docs.rs/rayon/1.0/rayon). +//! Parallel iterator types for [`IndexSet`] with [rayon][::rayon]. //! //! You will rarely need to interact with this module directly unless you need to name one of the //! iterator types. -//! -//! Requires crate feature `"rayon"`. use super::collect; use rayon::iter::plumbing::{Consumer, ProducerCallback, UnindexedConsumer}; use rayon::prelude::*; use crate::vec::Vec; +use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; +use core::ops::RangeBounds; +use crate::set::Slice; use crate::Entries; use crate::IndexSet; type Bucket = crate::Bucket; -/// Requires crate feature `"rayon"`. impl IntoParallelIterator for IndexSet where T: Send, @@ -34,13 +34,24 @@ where } } -/// A parallel owning iterator over the items of a `IndexSet`. -/// -/// This `struct` is created by the [`into_par_iter`] method on [`IndexSet`] -/// (provided by rayon's `IntoParallelIterator` trait). See its documentation for more. +impl IntoParallelIterator for Box> +where + T: Send, +{ + type Item = T; + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } +} + +/// A parallel owning iterator over the items of an [`IndexSet`]. /// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`into_par_iter`]: ../struct.IndexSet.html#method.into_par_iter +/// This `struct` is created by the [`IndexSet::into_par_iter`] method +/// (provided by rayon's [`IntoParallelIterator`] trait). See its documentation for more. pub struct IntoParIter { entries: Vec>, } @@ -62,7 +73,6 @@ impl IndexedParallelIterator for IntoParIter { indexed_parallel_iterator_methods!(Bucket::key); } -/// Requires crate feature `"rayon"`. impl<'a, T, S> IntoParallelIterator for &'a IndexSet where T: Sync, @@ -77,13 +87,26 @@ where } } -/// A parallel iterator over the items of a `IndexSet`. +impl<'a, T> IntoParallelIterator for &'a Slice +where + T: Sync, +{ + type Item = &'a T; + type Iter = ParIter<'a, T>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: &self.entries, + } + } +} + +/// A parallel iterator over the items of an [`IndexSet`]. /// -/// This `struct` is created by the [`par_iter`] method on [`IndexSet`] -/// (provided by rayon's `IntoParallelRefIterator` trait). See its documentation for more. +/// This `struct` is created by the [`IndexSet::par_iter`] method +/// (provided by rayon's [`IntoParallelRefIterator`] trait). See its documentation for more. /// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`par_iter`]: ../struct.IndexSet.html#method.par_iter +/// [`IndexSet::par_iter`]: ../struct.IndexSet.html#method.par_iter pub struct ParIter<'a, T> { entries: &'a [Bucket], } @@ -111,6 +134,40 @@ impl IndexedParallelIterator for ParIter<'_, T> { indexed_parallel_iterator_methods!(Bucket::key_ref); } +impl<'a, T, S> ParallelDrainRange for &'a mut IndexSet +where + T: Send, +{ + type Item = T; + type Iter = ParDrain<'a, T>; + + fn par_drain>(self, range: R) -> Self::Iter { + ParDrain { + entries: self.map.core.par_drain(range), + } + } +} + +/// A parallel draining iterator over the items of an [`IndexSet`]. +/// +/// This `struct` is created by the [`IndexSet::par_drain`] method +/// (provided by rayon's [`ParallelDrainRange`] trait). See its documentation for more. +/// +/// [`IndexSet::par_drain`]: ../struct.IndexSet.html#method.par_drain +pub struct ParDrain<'a, T: Send> { + entries: rayon::vec::Drain<'a, Bucket>, +} + +impl ParallelIterator for ParDrain<'_, T> { + type Item = T; + + parallel_iterator_methods!(Bucket::key); +} + +impl IndexedParallelIterator for ParDrain<'_, T> { + indexed_parallel_iterator_methods!(Bucket::key); +} + /// Parallel iterator methods and other parallel methods. /// /// The following methods **require crate feature `"rayon"`**. @@ -232,13 +289,10 @@ where } } -/// A parallel iterator producing elements in the difference of `IndexSet`s. +/// A parallel iterator producing elements in the difference of [`IndexSet`]s. /// -/// This `struct` is created by the [`par_difference`] method on [`IndexSet`]. +/// This `struct` is created by the [`IndexSet::par_difference`] method. /// See its documentation for more. -/// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`par_difference`]: ../struct.IndexSet.html#method.par_difference pub struct ParDifference<'a, T, S1, S2> { set1: &'a IndexSet, set2: &'a IndexSet, @@ -258,7 +312,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() } } @@ -283,13 +337,10 @@ where } } -/// A parallel iterator producing elements in the intersection of `IndexSet`s. +/// A parallel iterator producing elements in the intersection of [`IndexSet`]s. /// -/// This `struct` is created by the [`par_intersection`] method on [`IndexSet`]. +/// This `struct` is created by the [`IndexSet::par_intersection`] method. /// See its documentation for more. -/// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`par_intersection`]: ../struct.IndexSet.html#method.par_intersection pub struct ParIntersection<'a, T, S1, S2> { set1: &'a IndexSet, set2: &'a IndexSet, @@ -309,7 +360,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() } } @@ -334,13 +385,10 @@ where } } -/// A parallel iterator producing elements in the symmetric difference of `IndexSet`s. +/// A parallel iterator producing elements in the symmetric difference of [`IndexSet`]s. /// -/// This `struct` is created by the [`par_symmetric_difference`] method on -/// [`IndexSet`]. See its documentation for more. -/// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`par_symmetric_difference`]: ../struct.IndexSet.html#method.par_symmetric_difference +/// This `struct` is created by the [`IndexSet::par_symmetric_difference`] method. +/// See its documentation for more. pub struct ParSymmetricDifference<'a, T, S1, S2> { set1: &'a IndexSet, set2: &'a IndexSet, @@ -360,7 +408,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() } } @@ -385,13 +433,10 @@ where } } -/// A parallel iterator producing elements in the union of `IndexSet`s. +/// A parallel iterator producing elements in the union of [`IndexSet`]s. /// -/// This `struct` is created by the [`par_union`] method on [`IndexSet`]. +/// This `struct` is created by the [`IndexSet::par_union`] method. /// See its documentation for more. -/// -/// [`IndexSet`]: ../struct.IndexSet.html -/// [`par_union`]: ../struct.IndexSet.html#method.par_union pub struct ParUnion<'a, T, S1, S2> { set1: &'a IndexSet, set2: &'a IndexSet, @@ -410,7 +455,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() } } @@ -439,8 +484,7 @@ where /// The following methods **require crate feature `"rayon"`**. impl IndexSet where - T: Hash + Eq + Send, - S: BuildHasher + Send, + T: Send, { /// Sort the set’s values in parallel by their default ordering. pub fn par_sort(&mut self) @@ -452,7 +496,7 @@ where }); } - /// Sort the set’s values in place and in parallel, using the comparison function `compare`. + /// Sort the set’s values in place and in parallel, using the comparison function `cmp`. pub fn par_sort_by(&mut self, cmp: F) where F: Fn(&T, &T) -> Ordering + Sync, @@ -462,7 +506,7 @@ where }); } - /// Sort the values of the set in parallel and return a by value parallel iterator of + /// Sort the values of the set in parallel and return a by-value parallel iterator of /// the values with the result. pub fn par_sorted_by(self, cmp: F) -> IntoParIter where @@ -472,9 +516,50 @@ where entries.par_sort_by(move |a, b| cmp(&a.key, &b.key)); IntoParIter { entries } } + + /// Sort the set's values in parallel by their default ordering. + pub fn par_sort_unstable(&mut self) + where + T: Ord, + { + self.with_entries(|entries| { + entries.par_sort_unstable_by(|a, b| T::cmp(&a.key, &b.key)); + }); + } + + /// Sort the set’s values in place and in parallel, using the comparison function `cmp`. + pub fn par_sort_unstable_by(&mut self, cmp: F) + where + F: Fn(&T, &T) -> Ordering + Sync, + { + self.with_entries(|entries| { + entries.par_sort_unstable_by(move |a, b| cmp(&a.key, &b.key)); + }); + } + + /// Sort the values of the set in parallel and return a by-value parallel iterator of + /// the values with the result. + pub fn par_sorted_unstable_by(self, cmp: F) -> IntoParIter + where + F: Fn(&T, &T) -> Ordering + Sync, + { + let mut entries = self.into_entries(); + entries.par_sort_unstable_by(move |a, b| cmp(&a.key, &b.key)); + IntoParIter { entries } + } + + /// Sort the set’s values in place and in parallel, using a key extraction function. + pub fn par_sort_by_cached_key(&mut self, sort_key: F) + where + K: Ord + Send, + F: Fn(&T) -> K + Sync, + { + self.with_entries(move |entries| { + entries.par_sort_by_cached_key(move |a| sort_key(&a.key)); + }); + } } -/// Requires crate feature `"rayon"`. impl FromParallelIterator for IndexSet where T: Eq + Hash + Send, @@ -494,7 +579,6 @@ where } } -/// Requires crate feature `"rayon"`. impl ParallelExtend for IndexSet where T: Eq + Hash + Send, @@ -510,7 +594,6 @@ where } } -/// Requires crate feature `"rayon"`. impl<'a, T: 'a, S> ParallelExtend<&'a T> for IndexSet where T: Copy + Eq + Hash + Send + Sync, @@ -622,7 +705,7 @@ mod tests { I1: ParallelIterator, I2: Iterator, { - let v1: Vec<_> = iter1.cloned().collect(); + let v1: Vec<_> = iter1.copied().collect(); let v2: Vec<_> = iter2.collect(); assert_eq!(v1, v2); } diff --git a/src/rustc.rs b/src/rustc.rs new file mode 100644 index 00000000..b843858b --- /dev/null +++ b/src/rustc.rs @@ -0,0 +1,158 @@ +//! Minimal support for `rustc-rayon`, not intended for general use. + +use crate::vec::Vec; +use crate::{Bucket, Entries, IndexMap, IndexSet}; + +use rustc_rayon::iter::plumbing::{Consumer, ProducerCallback, UnindexedConsumer}; +use rustc_rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; + +mod map { + use super::*; + + impl IntoParallelIterator for IndexMap + where + K: Send, + V: Send, + { + type Item = (K, V); + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } + } + + pub struct IntoParIter { + entries: Vec>, + } + + impl ParallelIterator for IntoParIter { + type Item = (K, V); + + parallel_iterator_methods!(Bucket::key_value); + } + + impl IndexedParallelIterator for IntoParIter { + indexed_parallel_iterator_methods!(Bucket::key_value); + } + + impl<'a, K, V, S> IntoParallelIterator for &'a IndexMap + where + K: Sync, + V: Sync, + { + type Item = (&'a K, &'a V); + type Iter = ParIter<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: self.as_entries(), + } + } + } + + pub struct ParIter<'a, K, V> { + entries: &'a [Bucket], + } + + impl<'a, K: Sync, V: Sync> ParallelIterator for ParIter<'a, K, V> { + type Item = (&'a K, &'a V); + + parallel_iterator_methods!(Bucket::refs); + } + + impl IndexedParallelIterator for ParIter<'_, K, V> { + indexed_parallel_iterator_methods!(Bucket::refs); + } + + impl<'a, K, V, S> IntoParallelIterator for &'a mut IndexMap + where + K: Sync + Send, + V: Send, + { + type Item = (&'a K, &'a mut V); + type Iter = ParIterMut<'a, K, V>; + + fn into_par_iter(self) -> Self::Iter { + ParIterMut { + entries: self.as_entries_mut(), + } + } + } + + pub struct ParIterMut<'a, K, V> { + entries: &'a mut [Bucket], + } + + impl<'a, K: Sync + Send, V: Send> ParallelIterator for ParIterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + parallel_iterator_methods!(Bucket::ref_mut); + } + + impl IndexedParallelIterator for ParIterMut<'_, K, V> { + indexed_parallel_iterator_methods!(Bucket::ref_mut); + } +} + +mod set { + use super::*; + + impl IntoParallelIterator for IndexSet + where + T: Send, + { + type Item = T; + type Iter = IntoParIter; + + fn into_par_iter(self) -> Self::Iter { + IntoParIter { + entries: self.into_entries(), + } + } + } + + pub struct IntoParIter { + entries: Vec>, + } + + impl ParallelIterator for IntoParIter { + type Item = T; + + parallel_iterator_methods!(Bucket::key); + } + + impl IndexedParallelIterator for IntoParIter { + indexed_parallel_iterator_methods!(Bucket::key); + } + + impl<'a, T, S> IntoParallelIterator for &'a IndexSet + where + T: Sync, + { + type Item = &'a T; + type Iter = ParIter<'a, T>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { + entries: self.as_entries(), + } + } + } + + pub struct ParIter<'a, T> { + entries: &'a [Bucket], + } + + impl<'a, T: Sync> ParallelIterator for ParIter<'a, T> { + type Item = &'a T; + + parallel_iterator_methods!(Bucket::key_ref); + } + + impl IndexedParallelIterator for ParIter<'_, T> { + indexed_parallel_iterator_methods!(Bucket::key_ref); + } +} diff --git a/src/serde.rs b/src/serde.rs index c6dd6d5e..25546d53 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,3 +1,5 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "serde")))] + use serde::de::value::{MapDeserializer, SeqDeserializer}; use serde::de::{ Deserialize, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor, @@ -7,15 +9,30 @@ use serde::ser::{Serialize, Serializer}; use core::fmt::{self, Formatter}; use core::hash::{BuildHasher, Hash}; use core::marker::PhantomData; +use core::{cmp, mem}; + +use crate::{Bucket, IndexMap, IndexSet}; + +/// Limit our preallocated capacity from a deserializer `size_hint()`. +/// +/// We do account for the `Bucket` overhead from its saved `hash` field, but we don't count the +/// `RawTable` allocation or the fact that its raw capacity will be rounded up to a power of two. +/// The "max" is an arbitrary choice anyway, not something that needs precise adherence. +/// +/// This is based on the internal `serde::de::size_hint::cautious(hint)` function. +pub(crate) fn cautious_capacity(hint: Option) -> usize { + const MAX_PREALLOC_BYTES: usize = 1024 * 1024; + + cmp::min( + hint.unwrap_or(0), + MAX_PREALLOC_BYTES / mem::size_of::>(), + ) +} -use crate::IndexMap; - -/// Requires crate feature `"serde"` or `"serde-1"` impl Serialize for IndexMap where - K: Serialize + Hash + Eq, + K: Serialize, V: Serialize, - S: BuildHasher, { fn serialize(&self, serializer: T) -> Result where @@ -43,8 +60,8 @@ where where A: MapAccess<'de>, { - let mut values = - IndexMap::with_capacity_and_hasher(map.size_hint().unwrap_or(0), S::default()); + let capacity = cautious_capacity::(map.size_hint()); + let mut values = IndexMap::with_capacity_and_hasher(capacity, S::default()); while let Some((key, value)) = map.next_entry()? { values.insert(key, value); @@ -54,7 +71,6 @@ where } } -/// Requires crate feature `"serde"` or `"serde-1"` impl<'de, K, V, S> Deserialize<'de> for IndexMap where K: Deserialize<'de> + Eq + Hash, @@ -83,13 +99,9 @@ where } } -use crate::IndexSet; - -/// Requires crate feature `"serde"` or `"serde-1"` impl Serialize for IndexSet where - T: Serialize + Hash + Eq, - S: BuildHasher, + T: Serialize, { fn serialize(&self, serializer: Se) -> Result where @@ -116,8 +128,8 @@ where where A: SeqAccess<'de>, { - let mut values = - IndexSet::with_capacity_and_hasher(seq.size_hint().unwrap_or(0), S::default()); + let capacity = cautious_capacity::(seq.size_hint()); + let mut values = IndexSet::with_capacity_and_hasher(capacity, S::default()); while let Some(value) = seq.next_element()? { values.insert(value); @@ -127,7 +139,6 @@ where } } -/// Requires crate feature `"serde"` or `"serde-1"` impl<'de, T, S> Deserialize<'de> for IndexSet where T: Deserialize<'de> + Eq + Hash, diff --git a/src/set.rs b/src/set.rs index 29fb64f0..1be248eb 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1,18 +1,32 @@ -//! A hash set implemented using `IndexMap` +//! A hash set implemented using [`IndexMap`] + +mod iter; +mod mutable; +mod slice; + +#[cfg(test)] +mod tests; + +pub use self::iter::{ + Difference, Drain, Intersection, IntoIter, Iter, Splice, SymmetricDifference, Union, +}; +pub use self::mutable::MutableValues; +pub use self::slice::Slice; #[cfg(feature = "rayon")] pub use crate::rayon::set as rayon; +use crate::TryReserveError; -#[cfg(has_std)] +#[cfg(feature = "std")] use std::collections::hash_map::RandomState; -use crate::vec::{self, Vec}; +use crate::util::try_simplify_range; +use alloc::boxed::Box; +use alloc::vec::Vec; use core::cmp::Ordering; use core::fmt; use core::hash::{BuildHasher, Hash}; -use core::iter::{Chain, FromIterator}; use core::ops::{BitAnd, BitOr, BitXor, Index, RangeBounds, Sub}; -use core::slice; use super::{Entries, Equivalent, IndexMap}; @@ -21,8 +35,9 @@ type Bucket = super::Bucket; /// A hash set where the iteration order of the values is independent of their /// hash values. /// -/// The interface is closely compatible with the standard `HashSet`, but also -/// has additional features. +/// The interface is closely compatible with the standard +/// [`HashSet`][std::collections::HashSet], +/// but also has additional features. /// /// # Order /// @@ -33,11 +48,12 @@ type Bucket = super::Bucket; /// already present. /// /// All iterators traverse the set *in order*. Set operation iterators like -/// `union` produce a concatenated order, as do their matching "bitwise" +/// [`IndexSet::union`] produce a concatenated order, as do their matching "bitwise" /// operators. See their documentation for specifics. /// /// The insertion order is preserved, with **notable exceptions** like the -/// `.remove()` or `.swap_remove()` methods. Methods such as `.sort_by()` of +/// [`.remove()`][Self::remove] or [`.swap_remove()`][Self::swap_remove] methods. +/// Methods such as [`.sort_by()`][Self::sort_by] of /// course result in a new order, depending on the sorting order. /// /// # Indices @@ -46,6 +62,11 @@ type Bucket = super::Bucket; /// `0..self.len()`. For example, the method `.get_full` looks up the index for /// a value, and the method `.get_index` looks up the value by index. /// +/// # Complexity +/// +/// Internally, `IndexSet` just holds an [`IndexMap`](IndexMap). Thus the complexity +/// of the two are the same for most methods. +/// /// # Examples /// /// ``` @@ -59,13 +80,13 @@ type Bucket = super::Bucket; /// assert!(letters.contains(&'u')); /// assert!(!letters.contains(&'y')); /// ``` -#[cfg(has_std)] +#[cfg(feature = "std")] pub struct IndexSet { - map: IndexMap, + pub(crate) map: IndexMap, } -#[cfg(not(has_std))] +#[cfg(not(feature = "std"))] pub struct IndexSet { - map: IndexMap, + pub(crate) map: IndexMap, } impl Clone for IndexSet @@ -114,17 +135,20 @@ impl fmt::Debug for IndexSet where T: fmt::Debug, { + #[cfg(not(feature = "test_debug"))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if cfg!(not(feature = "test_debug")) { - f.debug_set().entries(self.iter()).finish() - } else { - // Let the inner `IndexMap` print all of its details - f.debug_struct("IndexSet").field("map", &self.map).finish() - } + f.debug_set().entries(self.iter()).finish() + } + + #[cfg(feature = "test_debug")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Let the inner `IndexMap` print all of its details + f.debug_struct("IndexSet").field("map", &self.map).finish() } } -#[cfg(has_std)] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl IndexSet { /// Create a new set. (Does not allocate.) pub fn new() -> Self { @@ -155,13 +179,21 @@ impl IndexSet { } } - /// Create a new set with `hash_builder` - pub fn with_hasher(hash_builder: S) -> Self { + /// Create a new set with `hash_builder`. + /// + /// This function is `const`, so it + /// can be called in `static` contexts. + pub const fn with_hasher(hash_builder: S) -> Self { IndexSet { map: IndexMap::with_hasher(hash_builder), } } + /// Return the number of elements the set can hold without reallocating. + /// + /// This number is a lower bound; the set might be able to hold more, + /// but is guaranteed to be able to hold at least this many. + /// /// Computes in **O(1)** time. pub fn capacity(&self) -> usize { self.map.capacity() @@ -188,9 +220,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::new(self.as_entries()) } /// Remove all elements in the set, while preserving its capacity. @@ -210,7 +240,7 @@ impl IndexSet { /// Clears the `IndexSet` in the given index range, returning those values /// as a drain iterator. /// - /// The range may be any type that implements `RangeBounds`, + /// The range may be any type that implements [`RangeBounds`], /// including all of the `std::ops::Range*` types, or even a tuple pair of /// `Bound` start and end values. To drain the set entirely, use `RangeFull` /// like `set.drain(..)`. @@ -220,13 +250,12 @@ impl IndexSet { /// /// ***Panics*** if the starting point is greater than the end point or if /// the end point is greater than the length of the set. + #[track_caller] pub fn drain(&mut self, range: R) -> Drain<'_, T> where R: RangeBounds, { - Drain { - iter: self.map.drain(range).iter, - } + Drain::new(self.map.core.drain(range)) } /// Splits the collection into two at the given index. @@ -236,6 +265,7 @@ impl IndexSet { /// the elements `[0, at)` with its previous capacity unchanged. /// /// ***Panics*** if `at > len`. + #[track_caller] pub fn split_off(&mut self, at: usize) -> Self where S: Clone, @@ -244,13 +274,7 @@ impl IndexSet { map: self.map.split_off(at), } } -} -impl IndexSet -where - T: Hash + Eq, - S: BuildHasher, -{ /// Reserve capacity for `additional` more values. /// /// Computes in **O(n)** time. @@ -258,6 +282,37 @@ where self.map.reserve(additional); } + /// Reserve capacity for `additional` more values, without over-allocating. + /// + /// Unlike `reserve`, this does not deliberately over-allocate the entry capacity to avoid + /// frequent re-allocations. However, the underlying data structures may still have internal + /// capacity requirements, and the allocator itself may give more space than requested, so this + /// cannot be relied upon to be precisely minimal. + /// + /// Computes in **O(n)** time. + pub fn reserve_exact(&mut self, additional: usize) { + self.map.reserve_exact(additional); + } + + /// Try to reserve capacity for `additional` more values. + /// + /// Computes in **O(n)** time. + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.map.try_reserve(additional) + } + + /// Try to reserve capacity for `additional` more values, without over-allocating. + /// + /// Unlike `try_reserve`, this does not deliberately over-allocate the entry capacity to avoid + /// frequent re-allocations. However, the underlying data structures may still have internal + /// capacity requirements, and the allocator itself may give more space than requested, so this + /// cannot be relied upon to be precisely minimal. + /// + /// Computes in **O(n)** time. + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.map.try_reserve_exact(additional) + } + /// Shrink the capacity of the set as much as possible. /// /// Computes in **O(n)** time. @@ -265,6 +320,19 @@ 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); + } +} + +impl IndexSet +where + T: Hash + Eq, + S: BuildHasher, +{ /// Insert the value into the set. /// /// If an equivalent item already exists in the set, it returns @@ -287,15 +355,161 @@ where /// /// Computes in **O(1)** time (amortized average). pub fn insert_full(&mut self, value: T) -> (usize, bool) { - use super::map::Entry::*; - - match self.map.entry(value) { - Occupied(e) => (e.index(), false), - Vacant(e) => { - let index = e.index(); - e.insert(()); - (index, true) - } + let (index, existing) = self.map.insert_full(value, ()); + (index, existing.is_none()) + } + + /// Insert the value into the set at its ordered position among sorted values. + /// + /// This is equivalent to finding the position with + /// [`binary_search`][Self::binary_search], and if needed calling + /// [`insert_before`][Self::insert_before] for a new value. + /// + /// If the sorted item is found in the set, it returns the index of that + /// existing item and `false`, without any change. Otherwise, it inserts the + /// new item and returns its sorted index and `true`. + /// + /// If the existing items are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the value + /// is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). Instead of repeating calls to + /// `insert_sorted`, it may be faster to call batched [`insert`][Self::insert] + /// or [`extend`][Self::extend] and only call [`sort`][Self::sort] or + /// [`sort_unstable`][Self::sort_unstable] once. + pub fn insert_sorted(&mut self, value: T) -> (usize, bool) + where + T: Ord, + { + let (index, existing) = self.map.insert_sorted(value, ()); + (index, existing.is_none()) + } + + /// Insert the value into the set before the value at the given index, or at the end. + /// + /// If an equivalent item already exists in the set, it returns `false` leaving the + /// original value in the set, but moved to the new position. The returned index + /// will either be the given index or one less, depending on how the value moved. + /// (See [`shift_insert`](Self::shift_insert) for different behavior here.) + /// + /// Otherwise, it inserts the new value exactly at the given index and returns `true`. + /// + /// ***Panics*** if `index` is out of bounds. + /// Valid indices are `0..=set.len()` (inclusive). + /// + /// Computes in **O(n)** time (average). + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexSet; + /// let mut set: IndexSet = ('a'..='z').collect(); + /// + /// // The new value '*' goes exactly at the given index. + /// assert_eq!(set.get_index_of(&'*'), None); + /// assert_eq!(set.insert_before(10, '*'), (10, true)); + /// assert_eq!(set.get_index_of(&'*'), Some(10)); + /// + /// // Moving the value 'a' up will shift others down, so this moves *before* 10 to index 9. + /// assert_eq!(set.insert_before(10, 'a'), (9, false)); + /// assert_eq!(set.get_index_of(&'a'), Some(9)); + /// assert_eq!(set.get_index_of(&'*'), Some(10)); + /// + /// // Moving the value 'z' down will shift others up, so this moves to exactly 10. + /// assert_eq!(set.insert_before(10, 'z'), (10, false)); + /// assert_eq!(set.get_index_of(&'z'), Some(10)); + /// assert_eq!(set.get_index_of(&'*'), Some(11)); + /// + /// // Moving or inserting before the endpoint is also valid. + /// assert_eq!(set.len(), 27); + /// assert_eq!(set.insert_before(set.len(), '*'), (26, false)); + /// assert_eq!(set.get_index_of(&'*'), Some(26)); + /// assert_eq!(set.insert_before(set.len(), '+'), (27, true)); + /// assert_eq!(set.get_index_of(&'+'), Some(27)); + /// assert_eq!(set.len(), 28); + /// ``` + #[track_caller] + pub fn insert_before(&mut self, index: usize, value: T) -> (usize, bool) { + let (index, existing) = self.map.insert_before(index, value, ()); + (index, existing.is_none()) + } + + /// Insert the value into the set at the given index. + /// + /// If an equivalent item already exists in the set, it returns `false` leaving + /// the original value in the set, but moved to the given index. + /// Note that existing values **cannot** be moved to `index == set.len()`! + /// (See [`insert_before`](Self::insert_before) for different behavior here.) + /// + /// Otherwise, it inserts the new value at the given index and returns `true`. + /// + /// ***Panics*** if `index` is out of bounds. + /// Valid indices are `0..set.len()` (exclusive) when moving an existing value, or + /// `0..=set.len()` (inclusive) when inserting a new value. + /// + /// Computes in **O(n)** time (average). + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexSet; + /// let mut set: IndexSet = ('a'..='z').collect(); + /// + /// // The new value '*' goes exactly at the given index. + /// assert_eq!(set.get_index_of(&'*'), None); + /// assert_eq!(set.shift_insert(10, '*'), true); + /// assert_eq!(set.get_index_of(&'*'), Some(10)); + /// + /// // Moving the value 'a' up to 10 will shift others down, including the '*' that was at 10. + /// assert_eq!(set.shift_insert(10, 'a'), false); + /// assert_eq!(set.get_index_of(&'a'), Some(10)); + /// assert_eq!(set.get_index_of(&'*'), Some(9)); + /// + /// // Moving the value 'z' down to 9 will shift others up, including the '*' that was at 9. + /// assert_eq!(set.shift_insert(9, 'z'), false); + /// assert_eq!(set.get_index_of(&'z'), Some(9)); + /// assert_eq!(set.get_index_of(&'*'), Some(10)); + /// + /// // Existing values can move to len-1 at most, but new values can insert at the endpoint. + /// assert_eq!(set.len(), 27); + /// assert_eq!(set.shift_insert(set.len() - 1, '*'), false); + /// assert_eq!(set.get_index_of(&'*'), Some(26)); + /// assert_eq!(set.shift_insert(set.len(), '+'), true); + /// assert_eq!(set.get_index_of(&'+'), Some(27)); + /// assert_eq!(set.len(), 28); + /// ``` + /// + /// ```should_panic + /// use indexmap::IndexSet; + /// let mut set: IndexSet = ('a'..='z').collect(); + /// + /// // This is an invalid index for moving an existing value! + /// set.shift_insert(set.len(), 'a'); + /// ``` + #[track_caller] + pub fn shift_insert(&mut self, index: usize, value: T) -> bool { + self.map.shift_insert(index, value, ()).is_none() + } + + /// Adds a value to the set, replacing the existing value, if any, that is + /// equal to the given one, without altering its insertion order. Returns + /// the replaced value. + /// + /// Computes in **O(1)** time (average). + pub fn replace(&mut self, value: T) -> Option { + self.replace_full(value).1 + } + + /// Adds a value to the set, replacing the existing value, if any, that is + /// equal to the given one, without altering its insertion order. Returns + /// the index of the item and its replaced value. + /// + /// Computes in **O(1)** time (average). + pub fn replace_full(&mut self, value: T) -> (usize, Option) { + let hash = self.map.hash(&value); + match self.map.core.replace_full(hash, value, ()) { + (i, Some((replaced, ()))) => (i, Some(replaced)), + (i, None) => (i, None), } } @@ -306,10 +520,7 @@ where where S2: BuildHasher, { - Difference { - iter: self.iter(), - other, - } + Difference::new(self, other) } /// Return an iterator over the values that are in `self` or `other`, @@ -324,9 +535,7 @@ where where S2: BuildHasher, { - SymmetricDifference { - iter: self.difference(other).chain(other.difference(self)), - } + SymmetricDifference::new(self, other) } /// Return an iterator over the values that are in both `self` and `other`. @@ -336,10 +545,7 @@ where where S2: BuildHasher, { - Intersection { - iter: self.iter(), - other, - } + Intersection::new(self, other) } /// Return an iterator over all values that are in `self` or `other`. @@ -350,17 +556,88 @@ where where S2: BuildHasher, { - Union { - iter: self.iter().chain(other.difference(self)), - } + Union::new(self, other) } + /// Creates a splicing iterator that replaces the specified range in the set + /// with the given `replace_with` iterator and yields the removed items. + /// `replace_with` does not need to be the same length as `range`. + /// + /// The `range` is removed even if the iterator is not consumed until the + /// end. It is unspecified how many elements are removed from the set if the + /// `Splice` value is leaked. + /// + /// The input iterator `replace_with` is only consumed when the `Splice` + /// value is dropped. If a value from the iterator matches an existing entry + /// in the set (outside of `range`), then the original will be unchanged. + /// Otherwise, the new value will be inserted in the replaced `range`. + /// + /// ***Panics*** if the starting point is greater than the end point or if + /// the end point is greater than the length of the set. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexSet; + /// + /// let mut set = IndexSet::from([0, 1, 2, 3, 4]); + /// let new = [5, 4, 3, 2, 1]; + /// let removed: Vec<_> = set.splice(2..4, new).collect(); + /// + /// // 1 and 4 kept their positions, while 5, 3, and 2 were newly inserted. + /// assert!(set.into_iter().eq([0, 1, 5, 3, 2, 4])); + /// assert_eq!(removed, &[2, 3]); + /// ``` + #[track_caller] + pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter, T, S> + where + R: RangeBounds, + I: IntoIterator, + { + Splice::new(self, range, replace_with.into_iter()) + } + + /// Moves all values from `other` into `self`, leaving `other` empty. + /// + /// This is equivalent to calling [`insert`][Self::insert] for each value + /// from `other` in order, which means that values that already exist + /// in `self` are unchanged in their current position. + /// + /// See also [`union`][Self::union] to iterate the combined values by + /// reference, without modifying `self` or `other`. + /// + /// # Examples + /// + /// ``` + /// use indexmap::IndexSet; + /// + /// let mut a = IndexSet::from([3, 2, 1]); + /// let mut b = IndexSet::from([3, 4, 5]); + /// let old_capacity = b.capacity(); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// assert_eq!(b.capacity(), old_capacity); + /// + /// assert!(a.iter().eq(&[3, 2, 1, 4, 5])); + /// ``` + pub fn append(&mut self, other: &mut IndexSet) { + self.map.append(&mut other.map); + } +} + +impl IndexSet +where + S: BuildHasher, +{ /// Return `true` if an equivalent to `value` exists in the set. /// /// Computes in **O(1)** time (average). - pub fn contains(&self, value: &Q) -> bool + pub fn contains(&self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.contains_key(value) } @@ -369,86 +646,74 @@ where /// else `None`. /// /// Computes in **O(1)** time (average). - pub fn get(&self, value: &Q) -> Option<&T> + pub fn get(&self, value: &Q) -> Option<&T> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_key_value(value).map(|(x, &())| x) } /// Return item index and value - pub fn get_full(&self, value: &Q) -> Option<(usize, &T)> + pub fn get_full(&self, value: &Q) -> Option<(usize, &T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_full(value).map(|(i, x, &())| (i, x)) } /// Return item index, if it exists in the set - pub fn get_index_of(&self, value: &Q) -> Option + /// + /// Computes in **O(1)** time (average). + pub fn get_index_of(&self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_index_of(value) } - /// Adds a value to the set, replacing the existing value, if any, that is - /// equal to the given one. Returns the replaced value. - /// - /// Computes in **O(1)** time (average). - pub fn replace(&mut self, value: T) -> Option { - use super::map::Entry::*; - - match self.map.entry(value) { - Vacant(e) => { - e.insert(()); - None - } - Occupied(e) => Some(e.replace_key()), - } - } - /// Remove the value from the set, and return `true` if it was present. /// - /// **NOTE:** This is equivalent to `.swap_remove(value)`, if you want - /// to preserve the order of the values in the set, use `.shift_remove(value)`. - /// - /// Computes in **O(1)** time (average). - pub fn remove(&mut self, value: &Q) -> bool + /// **NOTE:** This is equivalent to [`.swap_remove(value)`][Self::swap_remove], replacing this + /// value's position with the last element, and it is deprecated in favor of calling that + /// explicitly. If you need to preserve the relative order of the values in the set, use + /// [`.shift_remove(value)`][Self::shift_remove] instead. + #[deprecated(note = "`remove` disrupts the set order -- \ + use `swap_remove` or `shift_remove` for explicit behavior.")] + pub fn remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove(value) } /// Remove the value from the set, and return `true` if it was present. /// - /// Like `Vec::swap_remove`, the value is removed by swapping it with the + /// Like [`Vec::swap_remove`], the value is removed by swapping it with the /// last element of the set and popping it off. **This perturbs - /// the postion of what used to be the last element!** + /// the position of what used to be the last element!** /// /// Return `false` if `value` was not in the set. /// /// Computes in **O(1)** time (average). - pub fn swap_remove(&mut self, value: &Q) -> bool + pub fn swap_remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove(value).is_some() } /// Remove the value from the set, and return `true` if it was present. /// - /// Like `Vec::remove`, the value is removed by shifting all of the + /// Like [`Vec::remove`], the value is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `false` if `value` was not in the set. /// /// Computes in **O(n)** time (average). - pub fn shift_remove(&mut self, value: &Q) -> bool + pub fn shift_remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove(value).is_some() } @@ -456,14 +721,15 @@ where /// Removes and returns the value in the set, if any, that is equal to the /// given one. /// - /// **NOTE:** This is equivalent to `.swap_take(value)`, if you need to - /// preserve the order of the values in the set, use `.shift_take(value)` - /// instead. - /// - /// Computes in **O(1)** time (average). - pub fn take(&mut self, value: &Q) -> Option + /// **NOTE:** This is equivalent to [`.swap_take(value)`][Self::swap_take], replacing this + /// value's position with the last element, and it is deprecated in favor of calling that + /// explicitly. If you need to preserve the relative order of the values in the set, use + /// [`.shift_take(value)`][Self::shift_take] instead. + #[deprecated(note = "`take` disrupts the set order -- \ + use `swap_take` or `shift_take` for explicit behavior.")] + pub fn take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_take(value) } @@ -471,16 +737,16 @@ where /// Removes and returns the value in the set, if any, that is equal to the /// given one. /// - /// Like `Vec::swap_remove`, the value is removed by swapping it with the + /// Like [`Vec::swap_remove`], the value is removed by swapping it with the /// last element of the set and popping it off. **This perturbs - /// the postion of what used to be the last element!** + /// the position of what used to be the last element!** /// /// Return `None` if `value` was not in the set. /// /// Computes in **O(1)** time (average). - pub fn swap_take(&mut self, value: &Q) -> Option + pub fn swap_take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove_entry(value).map(|(x, ())| x) } @@ -488,51 +754,56 @@ where /// Removes and returns the value in the set, if any, that is equal to the /// given one. /// - /// Like `Vec::remove`, the value is removed by shifting all of the + /// Like [`Vec::remove`], the value is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `None` if `value` was not in the set. /// /// Computes in **O(n)** time (average). - pub fn shift_take(&mut self, value: &Q) -> Option + pub fn shift_take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove_entry(value).map(|(x, ())| x) } /// Remove the value from the set return it and the index it had. /// - /// Like `Vec::swap_remove`, the value is removed by swapping it with the + /// Like [`Vec::swap_remove`], the value is removed by swapping it with the /// last element of the set and popping it off. **This perturbs - /// the postion of what used to be the last element!** + /// the position of what used to be the last element!** /// /// Return `None` if `value` was not in the set. - pub fn swap_remove_full(&mut self, value: &Q) -> Option<(usize, T)> + pub fn swap_remove_full(&mut self, value: &Q) -> Option<(usize, T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove_full(value).map(|(i, x, ())| (i, x)) } /// Remove the value from the set return it and the index it had. /// - /// Like `Vec::remove`, the value is removed by shifting all of the + /// Like [`Vec::remove`], the value is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// /// Return `None` if `value` was not in the set. - pub fn shift_remove_full(&mut self, value: &Q) -> Option<(usize, T)> + pub fn shift_remove_full(&mut self, value: &Q) -> Option<(usize, T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove_full(value).map(|(i, x, ())| (i, x)) } +} +impl IndexSet { /// Remove the last value /// + /// This preserves the order of the remaining elements. + /// /// Computes in **O(1)** time (average). + #[doc(alias = "pop_last")] // like `BTreeSet` pub fn pop(&mut self) -> Option { self.map.pop().map(|(x, ())| x) } @@ -553,7 +824,11 @@ where /// Sort the set’s values by their default ordering. /// - /// See `sort_by` for details. + /// This is a stable sort -- but equivalent values should not normally coexist in + /// a set at all, so [`sort_unstable`][Self::sort_unstable] is preferred + /// because it is generally faster and doesn't allocate auxiliary memory. + /// + /// See [`sort_by`](Self::sort_by) for details. pub fn sort(&mut self) where T: Ord, @@ -561,17 +836,17 @@ where self.map.sort_keys() } - /// Sort the set’s values in place using the comparison function `compare`. + /// Sort the set’s values in place using the comparison function `cmp`. /// /// Computes in **O(n log n)** time and **O(n)** space. The sort is stable. - pub fn sort_by(&mut self, mut compare: F) + pub fn sort_by(&mut self, mut cmp: F) where F: FnMut(&T, &T) -> Ordering, { - self.map.sort_by(move |a, _, b, _| compare(a, b)); + self.map.sort_by(move |a, _, b, _| cmp(a, b)); } - /// Sort the values of the set and return a by value iterator of + /// Sort the values of the set and return a by-value iterator of /// the values with the result. /// /// The sort is stable. @@ -579,9 +854,115 @@ where where F: FnMut(&T, &T) -> Ordering, { - IntoIter { - iter: self.map.sorted_by(move |a, &(), b, &()| cmp(a, b)).iter, - } + let mut entries = self.into_entries(); + entries.sort_by(move |a, b| cmp(&a.key, &b.key)); + IntoIter::new(entries) + } + + /// Sort the set's values by their default ordering. + /// + /// See [`sort_unstable_by`](Self::sort_unstable_by) for details. + pub fn sort_unstable(&mut self) + where + T: Ord, + { + self.map.sort_unstable_keys() + } + + /// Sort the set's values in place using the comparison function `cmp`. + /// + /// Computes in **O(n log n)** time. The sort is unstable. + pub fn sort_unstable_by(&mut self, mut cmp: F) + where + F: FnMut(&T, &T) -> Ordering, + { + self.map.sort_unstable_by(move |a, _, b, _| cmp(a, b)) + } + + /// Sort the values of the set and return a by-value iterator of + /// the values with the result. + pub fn sorted_unstable_by(self, mut cmp: F) -> IntoIter + 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::new(entries) + } + + /// Sort the set’s values in place using a key extraction function. + /// + /// During sorting, the function is called at most once per entry, by using temporary storage + /// to remember the results of its evaluation. The order of calls to the function is + /// unspecified and may change between versions of `indexmap` or the standard library. + /// + /// Computes in **O(m n + n log n + c)** time () and **O(n)** space, where the function is + /// **O(m)**, *n* is the length of the map, and *c* the capacity. The sort is stable. + pub fn sort_by_cached_key(&mut self, mut sort_key: F) + where + K: Ord, + F: FnMut(&T) -> K, + { + self.with_entries(move |entries| { + entries.sort_by_cached_key(move |a| sort_key(&a.key)); + }); + } + + /// Search over a sorted set for a value. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search`] for more details. + /// + /// Computes in **O(log(n))** time, which is notably less scalable than looking the value up + /// using [`get_index_of`][IndexSet::get_index_of], but this can also position missing values. + pub fn binary_search(&self, x: &T) -> Result + where + T: Ord, + { + self.as_slice().binary_search(x) + } + + /// Search over a sorted set with a comparator function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result + where + F: FnMut(&'a T) -> Ordering, + { + self.as_slice().binary_search_by(f) + } + + /// Search over a sorted set with an extraction function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by_key`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result + where + F: FnMut(&'a T) -> B, + B: Ord, + { + self.as_slice().binary_search_by_key(b, f) + } + + /// Returns the index of the partition point of a sorted set according to the given predicate + /// (the index of the first element of the second partition). + /// + /// See [`slice::partition_point`] for more details. + /// + /// Computes in **O(log(n))** time. + #[must_use] + pub fn partition_point

(&self, pred: P) -> usize + where + P: FnMut(&T) -> bool, + { + self.as_slice().partition_point(pred) } /// Reverses the order of the set’s values in place. @@ -590,18 +971,41 @@ where pub fn reverse(&mut self) { self.map.reverse() } -} -impl IndexSet { + /// Returns a slice of all the values in the set. + /// + /// Computes in **O(1)** time. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.as_entries()) + } + + /// Converts into a boxed slice of all the values in the set. + /// + /// Note that this will drop the inner hash table and any excess capacity. + pub fn into_boxed_slice(self) -> Box> { + Slice::from_boxed(self.into_entries().into_boxed_slice()) + } + /// Get a value by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// /// Computes in **O(1)** time. pub fn get_index(&self, index: usize) -> Option<&T> { self.as_entries().get(index).map(Bucket::key_ref) } + /// Returns a slice of values in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + pub fn get_range>(&self, range: R) -> Option<&Slice> { + let entries = self.as_entries(); + let range = try_simplify_range(range, entries.len())?; + entries.get(range).map(Slice::from_slice) + } + /// Get the first value /// /// Computes in **O(1)** time. @@ -618,11 +1022,11 @@ impl IndexSet { /// Remove the value by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// - /// Like `Vec::swap_remove`, the value is removed by swapping it with the + /// Like [`Vec::swap_remove`], the value is removed by swapping it with the /// last element of the set and popping it off. **This perturbs - /// the postion of what used to be the last element!** + /// the position of what used to be the last element!** /// /// Computes in **O(1)** time (average). pub fn swap_remove_index(&mut self, index: usize) -> Option { @@ -631,9 +1035,9 @@ impl IndexSet { /// Remove the value by index /// - /// Valid indices are *0 <= index < self.len()* + /// Valid indices are `0 <= index < self.len()`. /// - /// Like `Vec::remove`, the value is removed by shifting all of the + /// Like [`Vec::remove`], the value is removed by shifting all of the /// elements that follow it, preserving their relative order. /// **This perturbs the index of all of those elements!** /// @@ -642,15 +1046,32 @@ 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). + #[track_caller] + 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. + /// + /// Computes in **O(1)** time (average). + #[track_caller] pub fn swap_indices(&mut self, a: usize, b: usize) { self.map.swap_indices(a, b) } } -/// Access `IndexSet` values at indexed positions. +/// Access [`IndexSet`] values at indexed positions. /// /// # Examples /// @@ -685,128 +1106,12 @@ impl Index for IndexSet { /// /// ***Panics*** if `index` is out of bounds. fn index(&self, index: usize) -> &T { - self.get_index(index) - .expect("IndexSet: index out of bounds") - } -} - -/// An owning iterator over the items of a `IndexSet`. -/// -/// This `struct` is created by the [`into_iter`] method on [`IndexSet`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`into_iter`]: struct.IndexSet.html#method.into_iter -pub struct IntoIter { - iter: vec::IntoIter>, -} - -impl Iterator for IntoIter { - type Item = T; - - iterator_methods!(Bucket::key); -} - -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::key) - } -} - -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let iter = self.iter.as_slice().iter().map(Bucket::key_ref); - f.debug_list().entries(iter).finish() - } -} - -/// An iterator over the items of a `IndexSet`. -/// -/// This `struct` is created by the [`iter`] method on [`IndexSet`]. -/// See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`iter`]: struct.IndexSet.html#method.iter -pub struct Iter<'a, T> { - iter: slice::Iter<'a, Bucket>, -} - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - iterator_methods!(Bucket::key_ref); -} - -impl DoubleEndedIterator for Iter<'_, T> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Bucket::key_ref) - } -} - -impl ExactSizeIterator for Iter<'_, T> { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl Clone for Iter<'_, T> { - fn clone(&self) -> Self { - Iter { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for Iter<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A draining iterator over the items of a `IndexSet`. -/// -/// This `struct` is created by the [`drain`] method on [`IndexSet`]. -/// See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`drain`]: struct.IndexSet.html#method.drain -pub struct Drain<'a, T> { - iter: vec::Drain<'a, Bucket>, -} - -impl Iterator for Drain<'_, T> { - type Item = T; - - iterator_methods!(Bucket::key); -} - -impl DoubleEndedIterator for Drain<'_, T> { - double_ended_iterator_methods!(Bucket::key); -} - -impl<'a, T, S> IntoIterator for &'a IndexSet { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl IntoIterator for IndexSet { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> Self::IntoIter { - IntoIter { - iter: self.map.into_iter().iter, - } + self.get_index(index).unwrap_or_else(|| { + panic!( + "index out of bounds: the len is {len} but the index is {index}", + len = self.len() + ); + }) } } @@ -823,6 +1128,26 @@ where } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<[T; N]> for IndexSet +where + T: Eq + Hash, +{ + /// # Examples + /// + /// ``` + /// use indexmap::IndexSet; + /// + /// let set1 = IndexSet::from([1, 2, 3, 4]); + /// let set2: IndexSet<_> = [1, 2, 3, 4].into(); + /// assert_eq!(set1, set2); + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + impl Extend for IndexSet where T: Hash + Eq, @@ -840,7 +1165,7 @@ where S: BuildHasher, { fn extend>(&mut self, iterable: I) { - let iter = iterable.into_iter().cloned(); // FIXME: use `copied` in Rust 1.36 + let iter = iterable.into_iter().copied(); self.extend(iter); } } @@ -849,7 +1174,7 @@ impl Default for IndexSet where S: Default, { - /// Return an empty `IndexSet` + /// Return an empty [`IndexSet`] fn default() -> Self { IndexSet { map: IndexMap::default(), @@ -909,267 +1234,6 @@ where } } -/// A lazy iterator producing elements in the difference of `IndexSet`s. -/// -/// This `struct` is created by the [`difference`] method on [`IndexSet`]. -/// See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`difference`]: struct.IndexSet.html#method.difference -pub struct Difference<'a, T, S> { - iter: Iter<'a, T>, - other: &'a IndexSet, -} - -impl<'a, T, S> Iterator for Difference<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - while let Some(item) = self.iter.next() { - if !self.other.contains(item) { - return Some(item); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - (0, self.iter.size_hint().1) - } -} - -impl DoubleEndedIterator for Difference<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - fn next_back(&mut self) -> Option { - while let Some(item) = self.iter.next_back() { - if !self.other.contains(item) { - return Some(item); - } - } - None - } -} - -impl Clone for Difference<'_, T, S> { - fn clone(&self) -> Self { - Difference { - iter: self.iter.clone(), - ..*self - } - } -} - -impl fmt::Debug for Difference<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A lazy iterator producing elements in the intersection of `IndexSet`s. -/// -/// This `struct` is created by the [`intersection`] method on [`IndexSet`]. -/// See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`intersection`]: struct.IndexSet.html#method.intersection -pub struct Intersection<'a, T, S> { - iter: Iter<'a, T>, - other: &'a IndexSet, -} - -impl<'a, T, S> Iterator for Intersection<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - while let Some(item) = self.iter.next() { - if self.other.contains(item) { - return Some(item); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - (0, self.iter.size_hint().1) - } -} - -impl DoubleEndedIterator for Intersection<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - fn next_back(&mut self) -> Option { - while let Some(item) = self.iter.next_back() { - if self.other.contains(item) { - return Some(item); - } - } - None - } -} - -impl Clone for Intersection<'_, T, S> { - fn clone(&self) -> Self { - Intersection { - iter: self.iter.clone(), - ..*self - } - } -} - -impl fmt::Debug for Intersection<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A lazy iterator producing elements in the symmetric difference of `IndexSet`s. -/// -/// This `struct` is created by the [`symmetric_difference`] method on -/// [`IndexSet`]. See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`symmetric_difference`]: struct.IndexSet.html#method.symmetric_difference -pub struct SymmetricDifference<'a, T, S1, S2> { - iter: Chain, Difference<'a, T, S1>>, -} - -impl<'a, T, S1, S2> Iterator for SymmetricDifference<'a, T, S1, S2> -where - T: Eq + Hash, - S1: BuildHasher, - S2: BuildHasher, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn fold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, f) - } -} - -impl DoubleEndedIterator for SymmetricDifference<'_, T, S1, S2> -where - T: Eq + Hash, - S1: BuildHasher, - S2: BuildHasher, -{ - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -impl Clone for SymmetricDifference<'_, T, S1, S2> { - fn clone(&self) -> Self { - SymmetricDifference { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for SymmetricDifference<'_, T, S1, S2> -where - T: fmt::Debug + Eq + Hash, - S1: BuildHasher, - S2: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A lazy iterator producing elements in the union of `IndexSet`s. -/// -/// This `struct` is created by the [`union`] method on [`IndexSet`]. -/// See its documentation for more. -/// -/// [`IndexSet`]: struct.IndexSet.html -/// [`union`]: struct.IndexSet.html#method.union -pub struct Union<'a, T, S> { - iter: Chain, Difference<'a, T, S>>, -} - -impl<'a, T, S> Iterator for Union<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn fold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, f) - } -} - -impl DoubleEndedIterator for Union<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -impl Clone for Union<'_, T, S> { - fn clone(&self) -> Self { - Union { - iter: self.iter.clone(), - } - } -} - -impl fmt::Debug for Union<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - impl BitAnd<&IndexSet> for &IndexSet where T: Eq + Hash + Clone, @@ -1235,417 +1299,3 @@ where self.difference(other).cloned().collect() } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::util::enumerate; - use std::string::String; - - #[test] - fn it_works() { - let mut set = IndexSet::new(); - assert_eq!(set.is_empty(), true); - set.insert(1); - set.insert(1); - assert_eq!(set.len(), 1); - assert!(set.get(&1).is_some()); - assert_eq!(set.is_empty(), false); - } - - #[test] - fn new() { - let set = IndexSet::::new(); - println!("{:?}", set); - assert_eq!(set.capacity(), 0); - assert_eq!(set.len(), 0); - assert_eq!(set.is_empty(), true); - } - - #[test] - fn insert() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5]; - let not_present = [1, 3, 6, 9, 10]; - let mut set = IndexSet::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(set.len(), i); - set.insert(elt); - assert_eq!(set.len(), i + 1); - assert_eq!(set.get(&elt), Some(&elt)); - } - println!("{:?}", set); - - for &elt in ¬_present { - assert!(set.get(&elt).is_none()); - } - } - - #[test] - fn insert_full() { - let insert = vec![9, 2, 7, 1, 4, 6, 13]; - let present = vec![1, 6, 2]; - let mut set = IndexSet::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(set.len(), i); - let (index, success) = set.insert_full(elt); - assert!(success); - assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); - assert_eq!(set.len(), i + 1); - } - - let len = set.len(); - for &elt in &present { - let (index, success) = set.insert_full(elt); - assert!(!success); - assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); - assert_eq!(set.len(), len); - } - } - - #[test] - fn insert_2() { - let mut set = IndexSet::with_capacity(16); - - let mut values = vec![]; - values.extend(0..16); - values.extend(128..267); - - for &i in &values { - let old_set = set.clone(); - set.insert(i); - for value in old_set.iter() { - if set.get(value).is_none() { - println!("old_set: {:?}", old_set); - println!("set: {:?}", set); - panic!("did not find {} in set", value); - } - } - } - - for &i in &values { - assert!(set.get(&i).is_some(), "did not find {}", i); - } - } - - #[test] - fn insert_dup() { - let mut elements = vec![0, 2, 4, 6, 8]; - let mut set: IndexSet = elements.drain(..).collect(); - { - let (i, v) = set.get_full(&0).unwrap(); - assert_eq!(set.len(), 5); - assert_eq!(i, 0); - assert_eq!(*v, 0); - } - { - let inserted = set.insert(0); - let (i, v) = set.get_full(&0).unwrap(); - assert_eq!(set.len(), 5); - assert_eq!(inserted, false); - assert_eq!(i, 0); - assert_eq!(*v, 0); - } - } - - #[test] - fn insert_order() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut set = IndexSet::new(); - - for &elt in &insert { - set.insert(elt); - } - - assert_eq!(set.iter().count(), set.len()); - assert_eq!(set.iter().count(), insert.len()); - for (a, b) in insert.iter().zip(set.iter()) { - assert_eq!(a, b); - } - for (i, v) in (0..insert.len()).zip(set.iter()) { - assert_eq!(set.get_index(i).unwrap(), v); - } - } - - #[test] - fn grow() { - let insert = [0, 4, 2, 12, 8, 7, 11]; - let not_present = [1, 3, 6, 9, 10]; - let mut set = IndexSet::with_capacity(insert.len()); - - for (i, &elt) in enumerate(&insert) { - assert_eq!(set.len(), i); - set.insert(elt); - assert_eq!(set.len(), i + 1); - assert_eq!(set.get(&elt), Some(&elt)); - } - - println!("{:?}", set); - for &elt in &insert { - set.insert(elt * 10); - } - for &elt in &insert { - set.insert(elt * 100); - } - for (i, &elt) in insert.iter().cycle().enumerate().take(100) { - set.insert(elt * 100 + i as i32); - } - println!("{:?}", set); - for &elt in ¬_present { - assert!(set.get(&elt).is_none()); - } - } - - #[test] - fn reserve() { - let mut set = IndexSet::::new(); - assert_eq!(set.capacity(), 0); - set.reserve(100); - let capacity = set.capacity(); - assert!(capacity >= 100); - for i in 0..capacity { - assert_eq!(set.len(), i); - set.insert(i); - assert_eq!(set.len(), i + 1); - assert_eq!(set.capacity(), capacity); - assert_eq!(set.get(&i), Some(&i)); - } - set.insert(capacity); - assert_eq!(set.len(), capacity + 1); - assert!(set.capacity() > capacity); - assert_eq!(set.get(&capacity), Some(&capacity)); - } - - #[test] - fn shrink_to_fit() { - let mut set = IndexSet::::new(); - assert_eq!(set.capacity(), 0); - for i in 0..100 { - assert_eq!(set.len(), i); - set.insert(i); - assert_eq!(set.len(), i + 1); - assert!(set.capacity() >= i + 1); - assert_eq!(set.get(&i), Some(&i)); - set.shrink_to_fit(); - assert_eq!(set.len(), i + 1); - assert_eq!(set.capacity(), i + 1); - assert_eq!(set.get(&i), Some(&i)); - } - } - - #[test] - fn remove() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut set = IndexSet::new(); - - for &elt in &insert { - set.insert(elt); - } - - assert_eq!(set.iter().count(), set.len()); - assert_eq!(set.iter().count(), insert.len()); - for (a, b) in insert.iter().zip(set.iter()) { - assert_eq!(a, b); - } - - let remove_fail = [99, 77]; - let remove = [4, 12, 8, 7]; - - for &value in &remove_fail { - assert!(set.swap_remove_full(&value).is_none()); - } - println!("{:?}", set); - for &value in &remove { - //println!("{:?}", set); - let index = set.get_full(&value).unwrap().0; - assert_eq!(set.swap_remove_full(&value), Some((index, value))); - } - println!("{:?}", set); - - for value in &insert { - assert_eq!(set.get(value).is_some(), !remove.contains(value)); - } - assert_eq!(set.len(), insert.len() - remove.len()); - assert_eq!(set.iter().count(), insert.len() - remove.len()); - } - - #[test] - fn swap_remove_index() { - let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; - let mut set = IndexSet::new(); - - for &elt in &insert { - set.insert(elt); - } - - let mut vector = insert.to_vec(); - let remove_sequence = &[3, 3, 10, 4, 5, 4, 3, 0, 1]; - - // check that the same swap remove sequence on vec and set - // have the same result. - for &rm in remove_sequence { - let out_vec = vector.swap_remove(rm); - let out_set = set.swap_remove_index(rm).unwrap(); - assert_eq!(out_vec, out_set); - } - assert_eq!(vector.len(), set.len()); - for (a, b) in vector.iter().zip(set.iter()) { - assert_eq!(a, b); - } - } - - #[test] - fn partial_eq_and_eq() { - let mut set_a = IndexSet::new(); - set_a.insert(1); - set_a.insert(2); - let mut set_b = set_a.clone(); - assert_eq!(set_a, set_b); - set_b.swap_remove(&1); - assert_ne!(set_a, set_b); - - let set_c: IndexSet<_> = set_b.into_iter().collect(); - assert_ne!(set_a, set_c); - assert_ne!(set_c, set_a); - } - - #[test] - fn extend() { - let mut set = IndexSet::new(); - set.extend(vec![&1, &2, &3, &4]); - set.extend(vec![5, 6]); - assert_eq!(set.into_iter().collect::>(), vec![1, 2, 3, 4, 5, 6]); - } - - #[test] - fn comparisons() { - let set_a: IndexSet<_> = (0..3).collect(); - let set_b: IndexSet<_> = (3..6).collect(); - let set_c: IndexSet<_> = (0..6).collect(); - let set_d: IndexSet<_> = (3..9).collect(); - - assert!(!set_a.is_disjoint(&set_a)); - assert!(set_a.is_subset(&set_a)); - assert!(set_a.is_superset(&set_a)); - - assert!(set_a.is_disjoint(&set_b)); - assert!(set_b.is_disjoint(&set_a)); - assert!(!set_a.is_subset(&set_b)); - assert!(!set_b.is_subset(&set_a)); - assert!(!set_a.is_superset(&set_b)); - assert!(!set_b.is_superset(&set_a)); - - assert!(!set_a.is_disjoint(&set_c)); - assert!(!set_c.is_disjoint(&set_a)); - assert!(set_a.is_subset(&set_c)); - assert!(!set_c.is_subset(&set_a)); - assert!(!set_a.is_superset(&set_c)); - assert!(set_c.is_superset(&set_a)); - - assert!(!set_c.is_disjoint(&set_d)); - assert!(!set_d.is_disjoint(&set_c)); - assert!(!set_c.is_subset(&set_d)); - assert!(!set_d.is_subset(&set_c)); - assert!(!set_c.is_superset(&set_d)); - assert!(!set_d.is_superset(&set_c)); - } - - #[test] - fn iter_comparisons() { - use std::iter::empty; - - fn check<'a, I1, I2>(iter1: I1, iter2: I2) - where - I1: Iterator, - I2: Iterator, - { - assert!(iter1.cloned().eq(iter2)); - } - - let set_a: IndexSet<_> = (0..3).collect(); - let set_b: IndexSet<_> = (3..6).collect(); - let set_c: IndexSet<_> = (0..6).collect(); - let set_d: IndexSet<_> = (3..9).rev().collect(); - - check(set_a.difference(&set_a), empty()); - check(set_a.symmetric_difference(&set_a), empty()); - check(set_a.intersection(&set_a), 0..3); - check(set_a.union(&set_a), 0..3); - - check(set_a.difference(&set_b), 0..3); - check(set_b.difference(&set_a), 3..6); - check(set_a.symmetric_difference(&set_b), 0..6); - check(set_b.symmetric_difference(&set_a), (3..6).chain(0..3)); - check(set_a.intersection(&set_b), empty()); - check(set_b.intersection(&set_a), empty()); - check(set_a.union(&set_b), 0..6); - check(set_b.union(&set_a), (3..6).chain(0..3)); - - check(set_a.difference(&set_c), empty()); - check(set_c.difference(&set_a), 3..6); - check(set_a.symmetric_difference(&set_c), 3..6); - check(set_c.symmetric_difference(&set_a), 3..6); - check(set_a.intersection(&set_c), 0..3); - check(set_c.intersection(&set_a), 0..3); - check(set_a.union(&set_c), 0..6); - check(set_c.union(&set_a), 0..6); - - check(set_c.difference(&set_d), 0..3); - check(set_d.difference(&set_c), (6..9).rev()); - check( - set_c.symmetric_difference(&set_d), - (0..3).chain((6..9).rev()), - ); - check(set_d.symmetric_difference(&set_c), (6..9).rev().chain(0..3)); - check(set_c.intersection(&set_d), 3..6); - check(set_d.intersection(&set_c), (3..6).rev()); - check(set_c.union(&set_d), (0..6).chain((6..9).rev())); - check(set_d.union(&set_c), (3..9).rev().chain(0..3)); - } - - #[test] - fn ops() { - let empty = IndexSet::::new(); - let set_a: IndexSet<_> = (0..3).collect(); - let set_b: IndexSet<_> = (3..6).collect(); - let set_c: IndexSet<_> = (0..6).collect(); - let set_d: IndexSet<_> = (3..9).rev().collect(); - - // FIXME: #[allow(clippy::eq_op)] in Rust 1.31 - #[cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints, eq_op))] - { - assert_eq!(&set_a & &set_a, set_a); - assert_eq!(&set_a | &set_a, set_a); - assert_eq!(&set_a ^ &set_a, empty); - assert_eq!(&set_a - &set_a, empty); - } - - assert_eq!(&set_a & &set_b, empty); - assert_eq!(&set_b & &set_a, empty); - assert_eq!(&set_a | &set_b, set_c); - assert_eq!(&set_b | &set_a, set_c); - assert_eq!(&set_a ^ &set_b, set_c); - assert_eq!(&set_b ^ &set_a, set_c); - assert_eq!(&set_a - &set_b, set_a); - assert_eq!(&set_b - &set_a, set_b); - - assert_eq!(&set_a & &set_c, set_a); - assert_eq!(&set_c & &set_a, set_a); - assert_eq!(&set_a | &set_c, set_c); - assert_eq!(&set_c | &set_a, set_c); - assert_eq!(&set_a ^ &set_c, set_b); - assert_eq!(&set_c ^ &set_a, set_b); - assert_eq!(&set_a - &set_c, empty); - assert_eq!(&set_c - &set_a, set_b); - - assert_eq!(&set_c & &set_d, set_b); - assert_eq!(&set_d & &set_c, set_b); - assert_eq!(&set_c | &set_d, &set_a | &set_d); - assert_eq!(&set_d | &set_c, &set_a | &set_d); - assert_eq!(&set_c ^ &set_d, &set_a | &(&set_d - &set_b)); - assert_eq!(&set_d ^ &set_c, &set_a | &(&set_d - &set_b)); - assert_eq!(&set_c - &set_d, set_a); - assert_eq!(&set_d - &set_c, &set_d - &set_b); - } -} diff --git a/src/set/iter.rs b/src/set/iter.rs new file mode 100644 index 00000000..5b1c8c90 --- /dev/null +++ b/src/set/iter.rs @@ -0,0 +1,628 @@ +use super::{Bucket, Entries, IndexSet, Slice}; + +use alloc::vec::{self, Vec}; +use core::fmt; +use core::hash::{BuildHasher, Hash}; +use core::iter::{Chain, FusedIterator}; +use core::ops::RangeBounds; +use core::slice::Iter as SliceIter; + +impl<'a, T, S> IntoIterator for &'a IndexSet { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl IntoIterator for IndexSet { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_entries()) + } +} + +/// An iterator over the items of an [`IndexSet`]. +/// +/// This `struct` is created by the [`IndexSet::iter`] method. +/// See its documentation for more. +pub struct Iter<'a, T> { + iter: SliceIter<'a, Bucket>, +} + +impl<'a, T> Iter<'a, T> { + pub(super) fn new(entries: &'a [Bucket]) -> Self { + Self { + iter: entries.iter(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &'a Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + iterator_methods!(Bucket::key_ref); +} + +impl DoubleEndedIterator for Iter<'_, T> { + double_ended_iterator_methods!(Bucket::key_ref); +} + +impl ExactSizeIterator for Iter<'_, T> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Iter<'_, T> {} + +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +impl Default for Iter<'_, T> { + fn default() -> Self { + Self { iter: [].iter() } + } +} + +/// An owning iterator over the items of an [`IndexSet`]. +/// +/// This `struct` is created by the [`IndexSet::into_iter`] method +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +#[derive(Clone)] +pub struct IntoIter { + iter: vec::IntoIter>, +} + +impl IntoIter { + pub(super) fn new(entries: Vec>) -> Self { + Self { + iter: entries.into_iter(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl Iterator for IntoIter { + type Item = T; + + iterator_methods!(Bucket::key); +} + +impl DoubleEndedIterator for IntoIter { + double_ended_iterator_methods!(Bucket::key); +} + +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IntoIter {} + +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::key_ref); + f.debug_list().entries(iter).finish() + } +} + +impl Default for IntoIter { + fn default() -> Self { + Self { + iter: Vec::new().into_iter(), + } + } +} + +/// A draining iterator over the items of an [`IndexSet`]. +/// +/// This `struct` is created by the [`IndexSet::drain`] method. +/// See its documentation for more. +pub struct Drain<'a, T> { + iter: vec::Drain<'a, Bucket>, +} + +impl<'a, T> Drain<'a, T> { + pub(super) fn new(iter: vec::Drain<'a, Bucket>) -> Self { + Self { iter } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } +} + +impl Iterator for Drain<'_, T> { + type Item = T; + + iterator_methods!(Bucket::key); +} + +impl DoubleEndedIterator for Drain<'_, T> { + double_ended_iterator_methods!(Bucket::key); +} + +impl ExactSizeIterator for Drain<'_, T> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Drain<'_, T> {} + +impl fmt::Debug for Drain<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::key_ref); + f.debug_list().entries(iter).finish() + } +} + +/// A lazy iterator producing elements in the difference of [`IndexSet`]s. +/// +/// This `struct` is created by the [`IndexSet::difference`] method. +/// See its documentation for more. +pub struct Difference<'a, T, S> { + iter: Iter<'a, T>, + other: &'a IndexSet, +} + +impl<'a, T, S> Difference<'a, T, S> { + pub(super) fn new(set: &'a IndexSet, other: &'a IndexSet) -> Self { + Self { + iter: set.iter(), + other, + } + } +} + +impl<'a, T, S> Iterator for Difference<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + while let Some(item) = self.iter.next() { + if !self.other.contains(item) { + return Some(item); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, self.iter.size_hint().1) + } +} + +impl DoubleEndedIterator for Difference<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + fn next_back(&mut self) -> Option { + while let Some(item) = self.iter.next_back() { + if !self.other.contains(item) { + return Some(item); + } + } + None + } +} + +impl FusedIterator for Difference<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +impl Clone for Difference<'_, T, S> { + fn clone(&self) -> Self { + Difference { + iter: self.iter.clone(), + ..*self + } + } +} + +impl fmt::Debug for Difference<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A lazy iterator producing elements in the intersection of [`IndexSet`]s. +/// +/// This `struct` is created by the [`IndexSet::intersection`] method. +/// See its documentation for more. +pub struct Intersection<'a, T, S> { + iter: Iter<'a, T>, + other: &'a IndexSet, +} + +impl<'a, T, S> Intersection<'a, T, S> { + pub(super) fn new(set: &'a IndexSet, other: &'a IndexSet) -> Self { + Self { + iter: set.iter(), + other, + } + } +} + +impl<'a, T, S> Iterator for Intersection<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + while let Some(item) = self.iter.next() { + if self.other.contains(item) { + return Some(item); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, self.iter.size_hint().1) + } +} + +impl DoubleEndedIterator for Intersection<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + fn next_back(&mut self) -> Option { + while let Some(item) = self.iter.next_back() { + if self.other.contains(item) { + return Some(item); + } + } + None + } +} + +impl FusedIterator for Intersection<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +impl Clone for Intersection<'_, T, S> { + fn clone(&self) -> Self { + Intersection { + iter: self.iter.clone(), + ..*self + } + } +} + +impl fmt::Debug for Intersection<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A lazy iterator producing elements in the symmetric difference of [`IndexSet`]s. +/// +/// This `struct` is created by the [`IndexSet::symmetric_difference`] method. +/// See its documentation for more. +pub struct SymmetricDifference<'a, T, S1, S2> { + iter: Chain, Difference<'a, T, S1>>, +} + +impl<'a, T, S1, S2> SymmetricDifference<'a, T, S1, S2> +where + T: Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, +{ + pub(super) fn new(set1: &'a IndexSet, set2: &'a IndexSet) -> Self { + let diff1 = set1.difference(set2); + let diff2 = set2.difference(set1); + Self { + iter: diff1.chain(diff2), + } + } +} + +impl<'a, T, S1, S2> Iterator for SymmetricDifference<'a, T, S1, S2> +where + T: Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } +} + +impl DoubleEndedIterator for SymmetricDifference<'_, T, S1, S2> +where + T: Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, +{ + fn next_back(&mut self) -> Option { + self.iter.next_back() + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.rfold(init, f) + } +} + +impl FusedIterator for SymmetricDifference<'_, T, S1, S2> +where + T: Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, +{ +} + +impl Clone for SymmetricDifference<'_, T, S1, S2> { + fn clone(&self) -> Self { + SymmetricDifference { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for SymmetricDifference<'_, T, S1, S2> +where + T: fmt::Debug + Eq + Hash, + S1: BuildHasher, + S2: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A lazy iterator producing elements in the union of [`IndexSet`]s. +/// +/// This `struct` is created by the [`IndexSet::union`] method. +/// See its documentation for more. +pub struct Union<'a, T, S> { + iter: Chain, Difference<'a, T, S>>, +} + +impl<'a, T, S> Union<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + pub(super) fn new(set1: &'a IndexSet, set2: &'a IndexSet) -> Self + where + S2: BuildHasher, + { + Self { + iter: set1.iter().chain(set2.difference(set1)), + } + } +} + +impl<'a, T, S> Iterator for Union<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } +} + +impl DoubleEndedIterator for Union<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + fn next_back(&mut self) -> Option { + self.iter.next_back() + } + + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.rfold(init, f) + } +} + +impl FusedIterator for Union<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +impl Clone for Union<'_, T, S> { + fn clone(&self) -> Self { + Union { + iter: self.iter.clone(), + } + } +} + +impl fmt::Debug for Union<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A splicing iterator for `IndexSet`. +/// +/// This `struct` is created by [`IndexSet::splice()`]. +/// See its documentation for more. +pub struct Splice<'a, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ + iter: crate::map::Splice<'a, UnitValue, T, (), S>, +} + +impl<'a, I, T, S> Splice<'a, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ + #[track_caller] + pub(super) fn new(set: &'a mut IndexSet, range: R, replace_with: I) -> Self + where + R: RangeBounds, + { + Self { + iter: set.map.splice(range, UnitValue(replace_with)), + } + } +} + +impl Iterator for Splice<'_, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ + type Item = T; + + fn next(&mut self) -> Option { + Some(self.iter.next()?.0) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for Splice<'_, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ + fn next_back(&mut self) -> Option { + Some(self.iter.next_back()?.0) + } +} + +impl ExactSizeIterator for Splice<'_, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for Splice<'_, I, T, S> +where + I: Iterator, + T: Hash + Eq, + S: BuildHasher, +{ +} + +struct UnitValue(I); + +impl Iterator for UnitValue { + type Item = (I::Item, ()); + + fn next(&mut self) -> Option { + self.0.next().map(|x| (x, ())) + } +} + +impl<'a, I, T, S> fmt::Debug for Splice<'a, I, T, S> +where + I: fmt::Debug + Iterator, + T: fmt::Debug + Hash + Eq, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.iter, f) + } +} + +impl fmt::Debug for UnitValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} diff --git a/src/set/mutable.rs b/src/set/mutable.rs new file mode 100644 index 00000000..21615f34 --- /dev/null +++ b/src/set/mutable.rs @@ -0,0 +1,86 @@ +use core::hash::{BuildHasher, Hash}; + +use super::{Equivalent, IndexSet}; +use crate::map::MutableKeys; + +/// Opt-in mutable access to [`IndexSet`] values. +/// +/// These methods expose `&mut T`, mutable references to the value as it is stored +/// in the set. +/// You are allowed to modify the values in the set **if the modification +/// does not change the value’s hash and equality**. +/// +/// If values are modified erroneously, you can no longer look them up. +/// This is sound (memory safe) but a logical error hazard (just like +/// implementing `PartialEq`, `Eq`, or `Hash` incorrectly would be). +/// +/// `use` this trait to enable its methods for `IndexSet`. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait MutableValues: private::Sealed { + type Value; + + /// Return item index and mutable reference to the value + /// + /// Computes in **O(1)** time (average). + fn get_full_mut2(&mut self, value: &Q) -> Option<(usize, &mut Self::Value)> + where + Q: ?Sized + Hash + Equivalent; + + /// Return mutable reference to the value at an index. + /// + /// Valid indices are `0 <= index < self.len()`. + /// + /// Computes in **O(1)** time. + fn get_index_mut2(&mut self, index: usize) -> Option<&mut Self::Value>; + + /// Scan through each value in the set and keep those where the + /// closure `keep` returns `true`. + /// + /// The values are visited in order, and remaining values keep their order. + /// + /// Computes in **O(n)** time (average). + fn retain2(&mut self, keep: F) + where + F: FnMut(&mut Self::Value) -> bool; +} + +/// Opt-in mutable access to [`IndexSet`] values. +/// +/// See [`MutableValues`] for more information. +impl MutableValues for IndexSet +where + S: BuildHasher, +{ + type Value = T; + + fn get_full_mut2(&mut self, value: &Q) -> Option<(usize, &mut T)> + where + Q: ?Sized + Hash + Equivalent, + { + match self.map.get_full_mut2(value) { + Some((index, value, ())) => Some((index, value)), + None => None, + } + } + + fn get_index_mut2(&mut self, index: usize) -> Option<&mut T> { + match self.map.get_index_mut2(index) { + Some((value, ())) => Some(value), + None => None, + } + } + + fn retain2(&mut self, mut keep: F) + where + F: FnMut(&mut T) -> bool, + { + self.map.retain2(move |value, ()| keep(value)); + } +} + +mod private { + pub trait Sealed {} + + impl Sealed for super::IndexSet {} +} diff --git a/src/set/slice.rs b/src/set/slice.rs new file mode 100644 index 00000000..f980e974 --- /dev/null +++ b/src/set/slice.rs @@ -0,0 +1,340 @@ +use super::{Bucket, Entries, IndexSet, IntoIter, Iter}; +use crate::util::try_simplify_range; + +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::ops::{self, Bound, Index, RangeBounds}; + +/// A dynamically-sized slice of values in an [`IndexSet`]. +/// +/// This supports indexed operations much like a `[T]` slice, +/// but not any hashed operations on the values. +/// +/// Unlike `IndexSet`, `Slice` does consider the order for [`PartialEq`] +/// and [`Eq`], and it also implements [`PartialOrd`], [`Ord`], and [`Hash`]. +#[repr(transparent)] +pub struct Slice { + pub(crate) entries: [Bucket], +} + +// SAFETY: `Slice` is a transparent wrapper around `[Bucket]`, +// and reference lifetimes are bound together in function signatures. +#[allow(unsafe_code)] +impl Slice { + pub(super) const fn from_slice(entries: &[Bucket]) -> &Self { + unsafe { &*(entries as *const [Bucket] as *const Self) } + } + + pub(super) fn from_boxed(entries: Box<[Bucket]>) -> Box { + unsafe { Box::from_raw(Box::into_raw(entries) as *mut Self) } + } + + fn into_boxed(self: Box) -> Box<[Bucket]> { + unsafe { Box::from_raw(Box::into_raw(self) as *mut [Bucket]) } + } +} + +impl Slice { + pub(crate) fn into_entries(self: Box) -> Vec> { + self.into_boxed().into_vec() + } + + /// Returns an empty slice. + pub const fn new<'a>() -> &'a Self { + Self::from_slice(&[]) + } + + /// Return the number of elements in the set slice. + pub const fn len(&self) -> usize { + self.entries.len() + } + + /// Returns true if the set slice contains no elements. + pub const fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Get a value by index. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_index(&self, index: usize) -> Option<&T> { + self.entries.get(index).map(Bucket::key_ref) + } + + /// Returns a slice of values in the given range of indices. + /// + /// Valid indices are `0 <= index < self.len()`. + pub fn get_range>(&self, range: R) -> Option<&Self> { + let range = try_simplify_range(range, self.entries.len())?; + self.entries.get(range).map(Self::from_slice) + } + + /// Get the first value. + pub fn first(&self) -> Option<&T> { + self.entries.first().map(Bucket::key_ref) + } + + /// Get the last value. + pub fn last(&self) -> Option<&T> { + self.entries.last().map(Bucket::key_ref) + } + + /// Divides one slice into two at an index. + /// + /// ***Panics*** if `index > len`. + pub fn split_at(&self, index: usize) -> (&Self, &Self) { + let (first, second) = self.entries.split_at(index); + (Self::from_slice(first), Self::from_slice(second)) + } + + /// Returns the first value and the rest of the slice, + /// or `None` if it is empty. + pub fn split_first(&self) -> Option<(&T, &Self)> { + if let [first, rest @ ..] = &self.entries { + Some((&first.key, Self::from_slice(rest))) + } else { + None + } + } + + /// Returns the last value and the rest of the slice, + /// or `None` if it is empty. + pub fn split_last(&self) -> Option<(&T, &Self)> { + if let [rest @ .., last] = &self.entries { + Some((&last.key, Self::from_slice(rest))) + } else { + None + } + } + + /// Return an iterator over the values of the set slice. + pub fn iter(&self) -> Iter<'_, T> { + Iter::new(&self.entries) + } + + /// Search over a sorted set for a value. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search`] for more details. + /// + /// Computes in **O(log(n))** time, which is notably less scalable than looking the value up in + /// the set this is a slice from using [`IndexSet::get_index_of`], but this can also position + /// missing values. + pub fn binary_search(&self, x: &T) -> Result + where + T: Ord, + { + self.binary_search_by(|p| p.cmp(x)) + } + + /// Search over a sorted set with a comparator function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result + where + F: FnMut(&'a T) -> Ordering, + { + self.entries.binary_search_by(move |a| f(&a.key)) + } + + /// Search over a sorted set with an extraction function. + /// + /// Returns the position where that value is present, or the position where it can be inserted + /// to maintain the sort. See [`slice::binary_search_by_key`] for more details. + /// + /// Computes in **O(log(n))** time. + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result + where + F: FnMut(&'a T) -> B, + B: Ord, + { + self.binary_search_by(|k| f(k).cmp(b)) + } + + /// Returns the index of the partition point of a sorted set according to the given predicate + /// (the index of the first element of the second partition). + /// + /// See [`slice::partition_point`] for more details. + /// + /// Computes in **O(log(n))** time. + #[must_use] + pub fn partition_point

(&self, mut pred: P) -> usize + where + P: FnMut(&T) -> bool, + { + self.entries.partition_point(move |a| pred(&a.key)) + } +} + +impl<'a, T> IntoIterator for &'a Slice { + type IntoIter = Iter<'a, T>; + type Item = &'a T; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl IntoIterator for Box> { + type IntoIter = IntoIter; + type Item = T; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.into_entries()) + } +} + +impl Default for &'_ Slice { + fn default() -> Self { + Slice::from_slice(&[]) + } +} + +impl Default for Box> { + fn default() -> Self { + Slice::from_boxed(Box::default()) + } +} + +impl Clone for Box> { + fn clone(&self) -> Self { + Slice::from_boxed(self.entries.to_vec().into_boxed_slice()) + } +} + +impl From<&Slice> for Box> { + fn from(slice: &Slice) -> Self { + Slice::from_boxed(Box::from(&slice.entries)) + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +impl PartialEq for Slice { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } +} + +impl Eq for Slice {} + +impl PartialOrd for Slice { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +impl Ord for Slice { + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + self.len().hash(state); + for value in self { + value.hash(state); + } + } +} + +impl Index for Slice { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.entries[index].key + } +} + +// We can't have `impl> Index` because that conflicts with `Index`. +// Instead, we repeat the implementations for all the core range types. +macro_rules! impl_index { + ($($range:ty),*) => {$( + impl Index<$range> for IndexSet { + type Output = Slice; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.as_entries()[range]) + } + } + + impl Index<$range> for Slice { + type Output = Self; + + fn index(&self, range: $range) -> &Self::Output { + Slice::from_slice(&self.entries[range]) + } + } + )*} +} +impl_index!( + ops::Range, + ops::RangeFrom, + ops::RangeFull, + ops::RangeInclusive, + ops::RangeTo, + ops::RangeToInclusive, + (Bound, Bound) +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slice_index() { + fn check(vec_slice: &[i32], set_slice: &Slice, sub_slice: &Slice) { + assert_eq!(set_slice as *const _, sub_slice as *const _); + itertools::assert_equal(vec_slice, set_slice); + } + + let vec: Vec = (0..10).map(|i| i * i).collect(); + let set: IndexSet = vec.iter().cloned().collect(); + let slice = set.as_slice(); + + // RangeFull + check(&vec[..], &set[..], &slice[..]); + + for i in 0usize..10 { + // Index + assert_eq!(vec[i], set[i]); + assert_eq!(vec[i], slice[i]); + + // RangeFrom + check(&vec[i..], &set[i..], &slice[i..]); + + // RangeTo + check(&vec[..i], &set[..i], &slice[..i]); + + // RangeToInclusive + check(&vec[..=i], &set[..=i], &slice[..=i]); + + // (Bound, Bound) + let bounds = (Bound::Excluded(i), Bound::Unbounded); + check(&vec[i + 1..], &set[bounds], &slice[bounds]); + + for j in i..=10 { + // Range + check(&vec[i..j], &set[i..j], &slice[i..j]); + } + + for j in i..10 { + // RangeInclusive + check(&vec[i..=j], &set[i..=j], &slice[i..=j]); + } + } + } +} diff --git a/src/set/tests.rs b/src/set/tests.rs new file mode 100644 index 00000000..35a076e8 --- /dev/null +++ b/src/set/tests.rs @@ -0,0 +1,723 @@ +use super::*; +use std::string::String; + +#[test] +fn it_works() { + let mut set = IndexSet::new(); + assert_eq!(set.is_empty(), true); + set.insert(1); + set.insert(1); + assert_eq!(set.len(), 1); + assert!(set.get(&1).is_some()); + assert_eq!(set.is_empty(), false); +} + +#[test] +fn new() { + let set = IndexSet::::new(); + println!("{:?}", set); + assert_eq!(set.capacity(), 0); + assert_eq!(set.len(), 0); + assert_eq!(set.is_empty(), true); +} + +#[test] +fn insert() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5]; + let not_present = [1, 3, 6, 9, 10]; + let mut set = IndexSet::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(set.len(), i); + set.insert(elt); + assert_eq!(set.len(), i + 1); + assert_eq!(set.get(&elt), Some(&elt)); + } + println!("{:?}", set); + + for &elt in ¬_present { + assert!(set.get(&elt).is_none()); + } +} + +#[test] +fn insert_full() { + let insert = vec![9, 2, 7, 1, 4, 6, 13]; + let present = vec![1, 6, 2]; + let mut set = IndexSet::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(set.len(), i); + let (index, success) = set.insert_full(elt); + assert!(success); + assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); + assert_eq!(set.len(), i + 1); + } + + let len = set.len(); + for &elt in &present { + let (index, success) = set.insert_full(elt); + assert!(!success); + assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); + assert_eq!(set.len(), len); + } +} + +#[test] +fn insert_2() { + let mut set = IndexSet::with_capacity(16); + + let mut values = vec![]; + values.extend(0..16); + values.extend(if cfg!(miri) { 32..64 } else { 128..267 }); + + for &i in &values { + let old_set = set.clone(); + set.insert(i); + for value in old_set.iter() { + if set.get(value).is_none() { + println!("old_set: {:?}", old_set); + println!("set: {:?}", set); + panic!("did not find {} in set", value); + } + } + } + + for &i in &values { + assert!(set.get(&i).is_some(), "did not find {}", i); + } +} + +#[test] +fn insert_dup() { + let mut elements = vec![0, 2, 4, 6, 8]; + let mut set: IndexSet = elements.drain(..).collect(); + { + let (i, v) = set.get_full(&0).unwrap(); + assert_eq!(set.len(), 5); + assert_eq!(i, 0); + assert_eq!(*v, 0); + } + { + let inserted = set.insert(0); + let (i, v) = set.get_full(&0).unwrap(); + assert_eq!(set.len(), 5); + assert_eq!(inserted, false); + assert_eq!(i, 0); + assert_eq!(*v, 0); + } +} + +#[test] +fn insert_order() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut set = IndexSet::new(); + + for &elt in &insert { + set.insert(elt); + } + + assert_eq!(set.iter().count(), set.len()); + assert_eq!(set.iter().count(), insert.len()); + for (a, b) in insert.iter().zip(set.iter()) { + assert_eq!(a, b); + } + for (i, v) in (0..insert.len()).zip(set.iter()) { + assert_eq!(set.get_index(i).unwrap(), v); + } +} + +#[test] +fn shift_insert() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut set = IndexSet::new(); + + for &elt in &insert { + set.shift_insert(0, elt); + } + + assert_eq!(set.iter().count(), set.len()); + assert_eq!(set.iter().count(), insert.len()); + for (a, b) in insert.iter().rev().zip(set.iter()) { + assert_eq!(a, b); + } + for (i, v) in (0..insert.len()).zip(set.iter()) { + assert_eq!(set.get_index(i).unwrap(), v); + } + + // "insert" that moves an existing entry + set.shift_insert(0, insert[0]); + assert_eq!(set.iter().count(), insert.len()); + assert_eq!(insert[0], set[0]); + for (a, b) in insert[1..].iter().rev().zip(set.iter().skip(1)) { + assert_eq!(a, b); + } +} + +#[test] +fn replace() { + let replace = [0, 4, 2, 12, 8, 7, 11, 5]; + let not_present = [1, 3, 6, 9, 10]; + let mut set = IndexSet::with_capacity(replace.len()); + + for (i, &elt) in replace.iter().enumerate() { + assert_eq!(set.len(), i); + set.replace(elt); + assert_eq!(set.len(), i + 1); + assert_eq!(set.get(&elt), Some(&elt)); + } + println!("{:?}", set); + + for &elt in ¬_present { + assert!(set.get(&elt).is_none()); + } +} + +#[test] +fn replace_full() { + let replace = vec![9, 2, 7, 1, 4, 6, 13]; + let present = vec![1, 6, 2]; + let mut set = IndexSet::with_capacity(replace.len()); + + for (i, &elt) in replace.iter().enumerate() { + assert_eq!(set.len(), i); + let (index, replaced) = set.replace_full(elt); + assert!(replaced.is_none()); + assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); + assert_eq!(set.len(), i + 1); + } + + let len = set.len(); + for &elt in &present { + let (index, replaced) = set.replace_full(elt); + assert_eq!(Some(elt), replaced); + assert_eq!(Some(index), set.get_full(&elt).map(|x| x.0)); + assert_eq!(set.len(), len); + } +} + +#[test] +fn replace_2() { + let mut set = IndexSet::with_capacity(16); + + let mut values = vec![]; + values.extend(0..16); + values.extend(if cfg!(miri) { 32..64 } else { 128..267 }); + + for &i in &values { + let old_set = set.clone(); + set.replace(i); + for value in old_set.iter() { + if set.get(value).is_none() { + println!("old_set: {:?}", old_set); + println!("set: {:?}", set); + panic!("did not find {} in set", value); + } + } + } + + for &i in &values { + assert!(set.get(&i).is_some(), "did not find {}", i); + } +} + +#[test] +fn replace_dup() { + let mut elements = vec![0, 2, 4, 6, 8]; + let mut set: IndexSet = elements.drain(..).collect(); + { + let (i, v) = set.get_full(&0).unwrap(); + assert_eq!(set.len(), 5); + assert_eq!(i, 0); + assert_eq!(*v, 0); + } + { + let replaced = set.replace(0); + let (i, v) = set.get_full(&0).unwrap(); + assert_eq!(set.len(), 5); + assert_eq!(replaced, Some(0)); + assert_eq!(i, 0); + assert_eq!(*v, 0); + } +} + +#[test] +fn replace_order() { + let replace = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut set = IndexSet::new(); + + for &elt in &replace { + set.replace(elt); + } + + assert_eq!(set.iter().count(), set.len()); + assert_eq!(set.iter().count(), replace.len()); + for (a, b) in replace.iter().zip(set.iter()) { + assert_eq!(a, b); + } + for (i, v) in (0..replace.len()).zip(set.iter()) { + assert_eq!(set.get_index(i).unwrap(), v); + } +} + +#[test] +fn replace_change() { + // Check pointers to make sure it really changes + let mut set = indexset!(vec![42]); + let old_ptr = set[0].as_ptr(); + let new = set[0].clone(); + let new_ptr = new.as_ptr(); + assert_ne!(old_ptr, new_ptr); + let replaced = set.replace(new).unwrap(); + assert_eq!(replaced.as_ptr(), old_ptr); +} + +#[test] +fn grow() { + let insert = [0, 4, 2, 12, 8, 7, 11]; + let not_present = [1, 3, 6, 9, 10]; + let mut set = IndexSet::with_capacity(insert.len()); + + for (i, &elt) in insert.iter().enumerate() { + assert_eq!(set.len(), i); + set.insert(elt); + assert_eq!(set.len(), i + 1); + assert_eq!(set.get(&elt), Some(&elt)); + } + + println!("{:?}", set); + for &elt in &insert { + set.insert(elt * 10); + } + for &elt in &insert { + set.insert(elt * 100); + } + for (i, &elt) in insert.iter().cycle().enumerate().take(100) { + set.insert(elt * 100 + i as i32); + } + println!("{:?}", set); + for &elt in ¬_present { + assert!(set.get(&elt).is_none()); + } +} + +#[test] +fn reserve() { + let mut set = IndexSet::::new(); + assert_eq!(set.capacity(), 0); + set.reserve(100); + let capacity = set.capacity(); + assert!(capacity >= 100); + for i in 0..capacity { + assert_eq!(set.len(), i); + set.insert(i); + assert_eq!(set.len(), i + 1); + assert_eq!(set.capacity(), capacity); + assert_eq!(set.get(&i), Some(&i)); + } + set.insert(capacity); + assert_eq!(set.len(), capacity + 1); + assert!(set.capacity() > capacity); + assert_eq!(set.get(&capacity), Some(&capacity)); +} + +#[test] +fn try_reserve() { + let mut set = IndexSet::::new(); + assert_eq!(set.capacity(), 0); + assert_eq!(set.try_reserve(100), Ok(())); + assert!(set.capacity() >= 100); + assert!(set.try_reserve(usize::MAX).is_err()); +} + +#[test] +fn shrink_to_fit() { + let mut set = IndexSet::::new(); + assert_eq!(set.capacity(), 0); + for i in 0..100 { + assert_eq!(set.len(), i); + set.insert(i); + assert_eq!(set.len(), i + 1); + assert!(set.capacity() >= i + 1); + assert_eq!(set.get(&i), Some(&i)); + set.shrink_to_fit(); + assert_eq!(set.len(), i + 1); + assert_eq!(set.capacity(), i + 1); + assert_eq!(set.get(&i), Some(&i)); + } +} + +#[test] +fn remove() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut set = IndexSet::new(); + + for &elt in &insert { + set.insert(elt); + } + + assert_eq!(set.iter().count(), set.len()); + assert_eq!(set.iter().count(), insert.len()); + for (a, b) in insert.iter().zip(set.iter()) { + assert_eq!(a, b); + } + + let remove_fail = [99, 77]; + let remove = [4, 12, 8, 7]; + + for &value in &remove_fail { + assert!(set.swap_remove_full(&value).is_none()); + } + println!("{:?}", set); + for &value in &remove { + //println!("{:?}", set); + let index = set.get_full(&value).unwrap().0; + assert_eq!(set.swap_remove_full(&value), Some((index, value))); + } + println!("{:?}", set); + + for value in &insert { + assert_eq!(set.get(value).is_some(), !remove.contains(value)); + } + assert_eq!(set.len(), insert.len() - remove.len()); + assert_eq!(set.iter().count(), insert.len() - remove.len()); +} + +#[test] +fn swap_remove_index() { + let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; + let mut set = IndexSet::new(); + + for &elt in &insert { + set.insert(elt); + } + + let mut vector = insert.to_vec(); + let remove_sequence = &[3, 3, 10, 4, 5, 4, 3, 0, 1]; + + // check that the same swap remove sequence on vec and set + // have the same result. + for &rm in remove_sequence { + let out_vec = vector.swap_remove(rm); + let out_set = set.swap_remove_index(rm).unwrap(); + assert_eq!(out_vec, out_set); + } + assert_eq!(vector.len(), set.len()); + for (a, b) in vector.iter().zip(set.iter()) { + assert_eq!(a, b); + } +} + +#[test] +fn partial_eq_and_eq() { + let mut set_a = IndexSet::new(); + set_a.insert(1); + set_a.insert(2); + let mut set_b = set_a.clone(); + assert_eq!(set_a, set_b); + set_b.swap_remove(&1); + assert_ne!(set_a, set_b); + + let set_c: IndexSet<_> = set_b.into_iter().collect(); + assert_ne!(set_a, set_c); + assert_ne!(set_c, set_a); +} + +#[test] +fn extend() { + let mut set = IndexSet::new(); + set.extend(vec![&1, &2, &3, &4]); + set.extend(vec![5, 6]); + assert_eq!(set.into_iter().collect::>(), vec![1, 2, 3, 4, 5, 6]); +} + +#[test] +fn comparisons() { + let set_a: IndexSet<_> = (0..3).collect(); + let set_b: IndexSet<_> = (3..6).collect(); + let set_c: IndexSet<_> = (0..6).collect(); + let set_d: IndexSet<_> = (3..9).collect(); + + assert!(!set_a.is_disjoint(&set_a)); + assert!(set_a.is_subset(&set_a)); + assert!(set_a.is_superset(&set_a)); + + assert!(set_a.is_disjoint(&set_b)); + assert!(set_b.is_disjoint(&set_a)); + assert!(!set_a.is_subset(&set_b)); + assert!(!set_b.is_subset(&set_a)); + assert!(!set_a.is_superset(&set_b)); + assert!(!set_b.is_superset(&set_a)); + + assert!(!set_a.is_disjoint(&set_c)); + assert!(!set_c.is_disjoint(&set_a)); + assert!(set_a.is_subset(&set_c)); + assert!(!set_c.is_subset(&set_a)); + assert!(!set_a.is_superset(&set_c)); + assert!(set_c.is_superset(&set_a)); + + assert!(!set_c.is_disjoint(&set_d)); + assert!(!set_d.is_disjoint(&set_c)); + assert!(!set_c.is_subset(&set_d)); + assert!(!set_d.is_subset(&set_c)); + assert!(!set_c.is_superset(&set_d)); + assert!(!set_d.is_superset(&set_c)); +} + +#[test] +fn iter_comparisons() { + use std::iter::empty; + + fn check<'a, I1, I2>(iter1: I1, iter2: I2) + where + I1: Iterator, + I2: Iterator, + { + assert!(iter1.copied().eq(iter2)); + } + + let set_a: IndexSet<_> = (0..3).collect(); + let set_b: IndexSet<_> = (3..6).collect(); + let set_c: IndexSet<_> = (0..6).collect(); + let set_d: IndexSet<_> = (3..9).rev().collect(); + + check(set_a.difference(&set_a), empty()); + check(set_a.symmetric_difference(&set_a), empty()); + check(set_a.intersection(&set_a), 0..3); + check(set_a.union(&set_a), 0..3); + + check(set_a.difference(&set_b), 0..3); + check(set_b.difference(&set_a), 3..6); + check(set_a.symmetric_difference(&set_b), 0..6); + check(set_b.symmetric_difference(&set_a), (3..6).chain(0..3)); + check(set_a.intersection(&set_b), empty()); + check(set_b.intersection(&set_a), empty()); + check(set_a.union(&set_b), 0..6); + check(set_b.union(&set_a), (3..6).chain(0..3)); + + check(set_a.difference(&set_c), empty()); + check(set_c.difference(&set_a), 3..6); + check(set_a.symmetric_difference(&set_c), 3..6); + check(set_c.symmetric_difference(&set_a), 3..6); + check(set_a.intersection(&set_c), 0..3); + check(set_c.intersection(&set_a), 0..3); + check(set_a.union(&set_c), 0..6); + check(set_c.union(&set_a), 0..6); + + check(set_c.difference(&set_d), 0..3); + check(set_d.difference(&set_c), (6..9).rev()); + check( + set_c.symmetric_difference(&set_d), + (0..3).chain((6..9).rev()), + ); + check(set_d.symmetric_difference(&set_c), (6..9).rev().chain(0..3)); + check(set_c.intersection(&set_d), 3..6); + check(set_d.intersection(&set_c), (3..6).rev()); + check(set_c.union(&set_d), (0..6).chain((6..9).rev())); + check(set_d.union(&set_c), (3..9).rev().chain(0..3)); +} + +#[test] +fn ops() { + let empty = IndexSet::::new(); + let set_a: IndexSet<_> = (0..3).collect(); + let set_b: IndexSet<_> = (3..6).collect(); + let set_c: IndexSet<_> = (0..6).collect(); + let set_d: IndexSet<_> = (3..9).rev().collect(); + + #[allow(clippy::eq_op)] + { + assert_eq!(&set_a & &set_a, set_a); + assert_eq!(&set_a | &set_a, set_a); + assert_eq!(&set_a ^ &set_a, empty); + assert_eq!(&set_a - &set_a, empty); + } + + assert_eq!(&set_a & &set_b, empty); + assert_eq!(&set_b & &set_a, empty); + assert_eq!(&set_a | &set_b, set_c); + assert_eq!(&set_b | &set_a, set_c); + assert_eq!(&set_a ^ &set_b, set_c); + assert_eq!(&set_b ^ &set_a, set_c); + assert_eq!(&set_a - &set_b, set_a); + assert_eq!(&set_b - &set_a, set_b); + + assert_eq!(&set_a & &set_c, set_a); + assert_eq!(&set_c & &set_a, set_a); + assert_eq!(&set_a | &set_c, set_c); + assert_eq!(&set_c | &set_a, set_c); + assert_eq!(&set_a ^ &set_c, set_b); + assert_eq!(&set_c ^ &set_a, set_b); + assert_eq!(&set_a - &set_c, empty); + assert_eq!(&set_c - &set_a, set_b); + + assert_eq!(&set_c & &set_d, set_b); + assert_eq!(&set_d & &set_c, set_b); + assert_eq!(&set_c | &set_d, &set_a | &set_d); + assert_eq!(&set_d | &set_c, &set_a | &set_d); + assert_eq!(&set_c ^ &set_d, &set_a | &(&set_d - &set_b)); + assert_eq!(&set_d ^ &set_c, &set_a | &(&set_d - &set_b)); + assert_eq!(&set_c - &set_d, set_a); + assert_eq!(&set_d - &set_c, &set_d - &set_b); +} + +#[test] +#[cfg(feature = "std")] +fn from_array() { + let set1 = IndexSet::from([1, 2, 3, 4]); + let set2: IndexSet<_> = [1, 2, 3, 4].into(); + + assert_eq!(set1, set2); +} + +#[test] +fn iter_default() { + struct Item; + fn assert_default() + where + T: Default + Iterator, + { + assert!(T::default().next().is_none()); + } + assert_default::>(); + assert_default::>(); +} + +#[test] +fn test_binary_search_by() { + // adapted from std's test for binary_search + let b: IndexSet = [].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&5)), Err(0)); + + let b: IndexSet = [4].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&3)), Err(0)); + assert_eq!(b.binary_search_by(|x| x.cmp(&4)), Ok(0)); + assert_eq!(b.binary_search_by(|x| x.cmp(&5)), Err(1)); + + let b: IndexSet = [1, 2, 4, 6, 8, 9].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&5)), Err(3)); + assert_eq!(b.binary_search_by(|x| x.cmp(&6)), Ok(3)); + assert_eq!(b.binary_search_by(|x| x.cmp(&7)), Err(4)); + assert_eq!(b.binary_search_by(|x| x.cmp(&8)), Ok(4)); + + let b: IndexSet = [1, 2, 4, 5, 6, 8].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&9)), Err(6)); + + let b: IndexSet = [1, 2, 4, 6, 7, 8, 9].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&6)), Ok(3)); + assert_eq!(b.binary_search_by(|x| x.cmp(&5)), Err(3)); + assert_eq!(b.binary_search_by(|x| x.cmp(&8)), Ok(5)); + + let b: IndexSet = [1, 2, 4, 5, 6, 8, 9].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&7)), Err(5)); + assert_eq!(b.binary_search_by(|x| x.cmp(&0)), Err(0)); + + let b: IndexSet = [1, 3, 3, 3, 7].into(); + assert_eq!(b.binary_search_by(|x| x.cmp(&0)), Err(0)); + assert_eq!(b.binary_search_by(|x| x.cmp(&1)), Ok(0)); + assert_eq!(b.binary_search_by(|x| x.cmp(&2)), Err(1)); + // diff from std as set merges the duplicate keys + assert!(match b.binary_search_by(|x| x.cmp(&3)) { + Ok(1..=2) => true, + _ => false, + }); + assert!(match b.binary_search_by(|x| x.cmp(&3)) { + Ok(1..=2) => true, + _ => false, + }); + assert_eq!(b.binary_search_by(|x| x.cmp(&4)), Err(2)); + assert_eq!(b.binary_search_by(|x| x.cmp(&5)), Err(2)); + assert_eq!(b.binary_search_by(|x| x.cmp(&6)), Err(2)); + assert_eq!(b.binary_search_by(|x| x.cmp(&7)), Ok(2)); + assert_eq!(b.binary_search_by(|x| x.cmp(&8)), Err(3)); +} + +#[test] +fn test_binary_search_by_key() { + // adapted from std's test for binary_search + let b: IndexSet = [].into(); + assert_eq!(b.binary_search_by_key(&5, |&x| x), Err(0)); + + let b: IndexSet = [4].into(); + assert_eq!(b.binary_search_by_key(&3, |&x| x), Err(0)); + assert_eq!(b.binary_search_by_key(&4, |&x| x), Ok(0)); + assert_eq!(b.binary_search_by_key(&5, |&x| x), Err(1)); + + let b: IndexSet = [1, 2, 4, 6, 8, 9].into(); + assert_eq!(b.binary_search_by_key(&5, |&x| x), Err(3)); + assert_eq!(b.binary_search_by_key(&6, |&x| x), Ok(3)); + assert_eq!(b.binary_search_by_key(&7, |&x| x), Err(4)); + assert_eq!(b.binary_search_by_key(&8, |&x| x), Ok(4)); + + let b: IndexSet = [1, 2, 4, 5, 6, 8].into(); + assert_eq!(b.binary_search_by_key(&9, |&x| x), Err(6)); + + let b: IndexSet = [1, 2, 4, 6, 7, 8, 9].into(); + assert_eq!(b.binary_search_by_key(&6, |&x| x), Ok(3)); + assert_eq!(b.binary_search_by_key(&5, |&x| x), Err(3)); + assert_eq!(b.binary_search_by_key(&8, |&x| x), Ok(5)); + + let b: IndexSet = [1, 2, 4, 5, 6, 8, 9].into(); + assert_eq!(b.binary_search_by_key(&7, |&x| x), Err(5)); + assert_eq!(b.binary_search_by_key(&0, |&x| x), Err(0)); + + let b: IndexSet = [1, 3, 3, 3, 7].into(); + assert_eq!(b.binary_search_by_key(&0, |&x| x), Err(0)); + assert_eq!(b.binary_search_by_key(&1, |&x| x), Ok(0)); + assert_eq!(b.binary_search_by_key(&2, |&x| x), Err(1)); + // diff from std as set merges the duplicate keys + assert!(match b.binary_search_by_key(&3, |&x| x) { + Ok(1..=2) => true, + _ => false, + }); + assert!(match b.binary_search_by_key(&3, |&x| x) { + Ok(1..=2) => true, + _ => false, + }); + assert_eq!(b.binary_search_by_key(&4, |&x| x), Err(2)); + assert_eq!(b.binary_search_by_key(&5, |&x| x), Err(2)); + assert_eq!(b.binary_search_by_key(&6, |&x| x), Err(2)); + assert_eq!(b.binary_search_by_key(&7, |&x| x), Ok(2)); + assert_eq!(b.binary_search_by_key(&8, |&x| x), Err(3)); +} + +#[test] +fn test_partition_point() { + // adapted from std's test for partition_point + let b: IndexSet = [].into(); + assert_eq!(b.partition_point(|&x| x < 5), 0); + + let b: IndexSet<_> = [4].into(); + assert_eq!(b.partition_point(|&x| x < 3), 0); + assert_eq!(b.partition_point(|&x| x < 4), 0); + assert_eq!(b.partition_point(|&x| x < 5), 1); + + let b: IndexSet<_> = [1, 2, 4, 6, 8, 9].into(); + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 7), 4); + assert_eq!(b.partition_point(|&x| x < 8), 4); + + let b: IndexSet<_> = [1, 2, 4, 5, 6, 8].into(); + assert_eq!(b.partition_point(|&x| x < 9), 6); + + let b: IndexSet<_> = [1, 2, 4, 6, 7, 8, 9].into(); + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 8), 5); + + let b: IndexSet<_> = [1, 2, 4, 5, 6, 8, 9].into(); + assert_eq!(b.partition_point(|&x| x < 7), 5); + assert_eq!(b.partition_point(|&x| x < 0), 0); + + let b: IndexSet<_> = [1, 3, 3, 3, 7].into(); + assert_eq!(b.partition_point(|&x| x < 0), 0); + assert_eq!(b.partition_point(|&x| x < 1), 0); + assert_eq!(b.partition_point(|&x| x < 2), 1); + assert_eq!(b.partition_point(|&x| x < 3), 1); + assert_eq!(b.partition_point(|&x| x < 4), 2); // diff from std as set merges the duplicate keys + assert_eq!(b.partition_point(|&x| x < 5), 2); + assert_eq!(b.partition_point(|&x| x < 6), 2); + assert_eq!(b.partition_point(|&x| x < 7), 2); + assert_eq!(b.partition_point(|&x| x < 8), 3); +} diff --git a/src/util.rs b/src/util.rs index 5388f470..6fb6fc3c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,17 +1,10 @@ -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() -} - +#[track_caller] pub(crate) fn simplify_range(range: R, len: usize) -> Range where R: RangeBounds, @@ -20,20 +13,46 @@ where Bound::Unbounded => 0, Bound::Included(&i) if i <= len => i, Bound::Excluded(&i) if i < len => i + 1, - bound => panic!("range start {:?} should be <= length {}", bound, len), + Bound::Included(i) | Bound::Excluded(i) => { + panic!("range start index {i} out of range for slice of length {len}") + } }; let end = match range.end_bound() { Bound::Unbounded => len, Bound::Excluded(&i) if i <= len => i, Bound::Included(&i) if i < len => i + 1, - bound => panic!("range end {:?} should be <= length {}", bound, len), + Bound::Included(i) | Bound::Excluded(i) => { + panic!("range end index {i} out of range for slice of length {len}") + } }; if start > end { panic!( - "range start {:?} should be <= range end {:?}", + "range start index {:?} should be <= range end index {:?}", range.start_bound(), range.end_bound() ); } start..end } + +pub(crate) fn try_simplify_range(range: R, len: usize) -> Option> +where + R: RangeBounds, +{ + let start = match range.start_bound() { + Bound::Unbounded => 0, + Bound::Included(&i) if i <= len => i, + Bound::Excluded(&i) if i < len => i + 1, + _ => return None, + }; + let end = match range.end_bound() { + Bound::Unbounded => len, + Bound::Excluded(&i) if i <= len => i, + Bound::Included(&i) if i < len => i + 1, + _ => return None, + }; + if start > end { + return None; + } + Some(start..end) +} diff --git a/test-nostd/Cargo.toml b/test-nostd/Cargo.toml index 0201a728..64d209a9 100644 --- a/test-nostd/Cargo.toml +++ b/test-nostd/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "test-nostd" version = "0.1.0" -authors = ["bluss"] publish = false -edition = "2018" +edition = "2021" -[dependencies] -indexmap = { path = "..", features = ["serde-1"] } +[dependencies.indexmap] +path = ".." +default-features = false +features = ["serde"] [dev-dependencies] diff --git a/test-nostd/src/lib.rs b/test-nostd/src/lib.rs index d54ade9d..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; @@ -11,7 +10,9 @@ use indexmap::IndexSet; struct BadHasher(u64); impl Hasher for BadHasher { - fn finish(&self) -> u64 { self.0 } + fn finish(&self) -> u64 { + self.0 + } fn write(&mut self, bytes: &[u8]) { for &byte in bytes { self.0 += byte as u64 diff --git a/test-serde/Cargo.toml b/test-serde/Cargo.toml index f7abc9e5..bf04c9fa 100644 --- a/test-serde/Cargo.toml +++ b/test-serde/Cargo.toml @@ -1,14 +1,13 @@ [package] name = "test-serde" version = "0.1.0" -authors = ["bluss"] publish = false -edition = "2018" +edition = "2021" [dependencies] [dev-dependencies] fnv = "1.0" -indexmap = { path = "..", features = ["serde-1"] } +indexmap = { path = "..", features = ["serde"] } serde = { version = "1.0.99", features = ["derive"] } serde_test = "1.0.99" diff --git a/test-serde/src/lib.rs b/test-serde/src/lib.rs index b78a752f..553e0c77 100644 --- a/test-serde/src/lib.rs +++ b/test-serde/src/lib.rs @@ -77,7 +77,7 @@ fn test_serde_seq_map() { #[derive(Debug, Deserialize, Serialize)] #[serde(transparent)] struct SeqIndexMap { - #[serde(with = "indexmap::serde_seq")] + #[serde(with = "indexmap::map::serde_seq")] map: IndexMap, } diff --git a/tests/quick.rs b/tests/quick.rs index 10e11b52..56afee72 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -1,13 +1,11 @@ use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; -use quickcheck::quickcheck; use quickcheck::Arbitrary; use quickcheck::Gen; +use quickcheck::QuickCheck; use quickcheck::TestResult; -use rand::Rng; - use fnv::FnvHasher; use std::hash::{BuildHasher, BuildHasherDefault}; type FnvBuilder = BuildHasherDefault; @@ -18,19 +16,18 @@ 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; -use indexmap::map::Entry as OEntry; -use std::collections::hash_map::Entry as HEntry; +use indexmap::map::Entry; +use std::collections::hash_map::Entry as StdEntry; fn set<'a, T: 'a, I>(iter: I) -> HashSet where I: IntoIterator, T: Copy + Hash + Eq, { - iter.into_iter().cloned().collect() + iter.into_iter().copied().collect() } fn indexmap<'a, T: 'a, I>(iter: I) -> IndexMap @@ -38,10 +35,45 @@ where I: IntoIterator, T: Copy + Hash + Eq, { - IndexMap::from_iter(iter.into_iter().cloned().map(|k| (k, ()))) + IndexMap::from_iter(iter.into_iter().copied().map(|k| (k, ()))) +} + +// Helper macro to allow us to use smaller quickcheck limits under miri. +macro_rules! quickcheck_limit { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + quickcheck::quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + let mut quickcheck = QuickCheck::new(); + if cfg!(miri) { + quickcheck = quickcheck + .gen(Gen::new(10)) + .tests(10) + .max_tests(100); + } + + quickcheck.quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) } -quickcheck! { +quickcheck_limit! { fn contains(insert: Vec) -> bool { let mut map = IndexMap::new(); for &key in &insert { @@ -81,6 +113,23 @@ quickcheck! { true } + fn insert_sorted(insert: Vec<(u32, u32)>) -> bool { + let mut hmap = HashMap::new(); + let mut map = IndexMap::new(); + let mut map2 = IndexMap::new(); + for &(key, value) in &insert { + hmap.insert(key, value); + map.insert_sorted(key, value); + match map2.entry(key) { + Entry::Occupied(e) => *e.into_mut() = value, + Entry::Vacant(e) => { e.insert_sorted(value); } + } + } + itertools::assert_equal(hmap.iter().sorted(), &map); + itertools::assert_equal(&map, &map2); + true + } + fn pop(insert: Vec) -> bool { let mut map = IndexMap::new(); for &key in &insert { @@ -96,7 +145,8 @@ quickcheck! { true } - fn with_cap(cap: usize) -> bool { + fn with_cap(template: Vec<()>) -> bool { + let cap = template.len(); let map: IndexMap = IndexMap::with_capacity(cap); println!("wish: {}, got: {} (diff: {})", cap, map.capacity(), map.capacity() as isize - cap as isize); map.capacity() >= cap @@ -123,7 +173,7 @@ quickcheck! { // First see if `Vec::drain` is happy with this range. let result = std::panic::catch_unwind(|| { - let mut keys: Vec = map.keys().cloned().collect(); + let mut keys: Vec = map.keys().copied().collect(); keys.drain(range); keys }); @@ -155,7 +205,7 @@ quickcheck! { let mut iter = map.keys(); for &key in insert.iter().unique() { if elements.contains(&key) { - assert_eq!(Some(key), iter.next().cloned()); + assert_eq!(Some(&key), iter.next()); } } @@ -165,7 +215,7 @@ quickcheck! { fn indexing(insert: Vec) -> bool { let mut map: IndexMap<_, _> = insert.into_iter().map(|x| (x, x)).collect(); - let set: IndexSet<_> = map.keys().cloned().collect(); + let set: IndexSet<_> = map.keys().copied().collect(); assert_eq!(map.len(), set.len()); for (i, &key) in set.iter().enumerate() { @@ -183,6 +233,216 @@ quickcheck! { map[&key] == value && map[i] == value }) } + + // Use `u8` test indices so quickcheck is less likely to go out of bounds. + fn set_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(); + } + + 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 map_swap_indices(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_swap_indices(vec, from, to, IndexMap::swap_indices) + } + + fn occupied_entry_swap_indices(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_swap_indices(vec, from, to, |map, from, to| { + let key = map.keys()[from]; + match map.entry(key) { + Entry::Occupied(entry) => entry.swap_indices(to), + _ => unreachable!(), + } + }) + } + + fn indexed_entry_swap_indices(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_swap_indices(vec, from, to, |map, from, to| { + map.get_index_entry(from).unwrap().swap_indices(to); + }) + } + + fn raw_occupied_entry_swap_indices(vec: Vec, from: u8, to: u8) -> TestResult { + use indexmap::map::raw_entry_v1::{RawEntryApiV1, RawEntryMut}; + test_map_swap_indices(vec, from, to, |map, from, to| { + let key = map.keys()[from]; + match map.raw_entry_mut_v1().from_key(&key) { + RawEntryMut::Occupied(entry) => entry.swap_indices(to), + _ => unreachable!(), + } + }) + } + + // Use `u8` test indices so quickcheck is less likely to go out of bounds. + fn set_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(); + } + + 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() + } + + fn map_move_index(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_move_index(vec, from, to, IndexMap::move_index) + } + + fn occupied_entry_move_index(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_move_index(vec, from, to, |map, from, to| { + let key = map.keys()[from]; + match map.entry(key) { + Entry::Occupied(entry) => entry.move_index(to), + _ => unreachable!(), + } + }) + } + + fn indexed_entry_move_index(vec: Vec, from: u8, to: u8) -> TestResult { + test_map_move_index(vec, from, to, |map, from, to| { + map.get_index_entry(from).unwrap().move_index(to); + }) + } + + fn raw_occupied_entry_move_index(vec: Vec, from: u8, to: u8) -> TestResult { + use indexmap::map::raw_entry_v1::{RawEntryApiV1, RawEntryMut}; + test_map_move_index(vec, from, to, |map, from, to| { + let key = map.keys()[from]; + match map.raw_entry_mut_v1().from_key(&key) { + RawEntryMut::Occupied(entry) => entry.move_index(to), + _ => unreachable!(), + } + }) + } + + fn occupied_entry_shift_insert(vec: Vec, i: u8) -> TestResult { + test_map_shift_insert(vec, i, |map, i, key| { + match map.entry(key) { + Entry::Vacant(entry) => entry.shift_insert(i, ()), + _ => unreachable!(), + }; + }) + } + + fn raw_occupied_entry_shift_insert(vec: Vec, i: u8) -> TestResult { + use indexmap::map::raw_entry_v1::{RawEntryApiV1, RawEntryMut}; + test_map_shift_insert(vec, i, |map, i, key| { + match map.raw_entry_mut_v1().from_key(&key) { + RawEntryMut::Vacant(entry) => entry.shift_insert(i, key, ()), + _ => unreachable!(), + }; + }) + } +} + +fn test_map_swap_indices(vec: Vec, a: u8, b: u8, swap_indices: F) -> TestResult +where + F: FnOnce(&mut IndexMap, usize, usize), +{ + let mut map = IndexMap::::from_iter(vec.into_iter().map(|k| (k, ()))); + let a = usize::from(a); + let b = usize::from(b); + + if a >= map.len() || b >= map.len() { + return TestResult::discard(); + } + + let mut vec = Vec::from_iter(map.keys().copied()); + vec.swap(a, b); + + swap_indices(&mut map, a, b); + + // Check both iteration order and hash lookups + assert!(map.keys().eq(vec.iter())); + assert!(vec + .iter() + .enumerate() + .all(|(i, x)| { map.get_index_of(x) == Some(i) })); + TestResult::passed() +} + +fn test_map_move_index(vec: Vec, from: u8, to: u8, move_index: F) -> TestResult +where + F: FnOnce(&mut IndexMap, usize, usize), +{ + let mut map = IndexMap::::from_iter(vec.into_iter().map(|k| (k, ()))); + let from = usize::from(from); + let to = usize::from(to); + + if from >= map.len() || to >= map.len() { + return TestResult::discard(); + } + + let mut vec = Vec::from_iter(map.keys().copied()); + let x = vec.remove(from); + vec.insert(to, x); + + move_index(&mut map, from, to); + + // Check both iteration order and hash lookups + assert!(map.keys().eq(vec.iter())); + assert!(vec + .iter() + .enumerate() + .all(|(i, x)| { map.get_index_of(x) == Some(i) })); + TestResult::passed() +} + +fn test_map_shift_insert(vec: Vec, i: u8, shift_insert: F) -> TestResult +where + F: FnOnce(&mut IndexMap, usize, u8), +{ + let mut map = IndexMap::::from_iter(vec.into_iter().map(|k| (k, ()))); + let i = usize::from(i); + if i >= map.len() { + return TestResult::discard(); + } + + let mut vec = Vec::from_iter(map.keys().copied()); + let x = vec.pop().unwrap(); + vec.insert(i, x); + + let (last, ()) = map.pop().unwrap(); + assert_eq!(x, last); + map.shrink_to_fit(); // so we might have to grow and rehash the table + + shift_insert(&mut map, i, last); + + // Check both iteration order and hash lookups + assert!(map.keys().eq(vec.iter())); + assert!(vec + .iter() + .enumerate() + .all(|(i, x)| { map.get_index_of(x) == Some(i) })); + TestResult::passed() } use crate::Op::*; @@ -199,8 +459,8 @@ where K: Arbitrary, V: Arbitrary, { - fn arbitrary(g: &mut G) -> Self { - match g.gen::() % 4 { + fn arbitrary(g: &mut Gen) -> Self { + match u32::arbitrary(g) % 4 { 0 => Add(K::arbitrary(g), V::arbitrary(g)), 1 => AddEntry(K::arbitrary(g), V::arbitrary(g)), 2 => Remove(K::arbitrary(g)), @@ -230,10 +490,10 @@ where b.remove(k); } RemoveEntry(ref k) => { - if let OEntry::Occupied(ent) = a.entry(k.clone()) { + if let Entry::Occupied(ent) = a.entry(k.clone()) { ent.swap_remove_entry(); } - if let HEntry::Occupied(ent) = b.entry(k.clone()) { + if let StdEntry::Occupied(ent) = b.entry(k.clone()) { ent.remove_entry(); } } @@ -261,7 +521,7 @@ where true } -quickcheck! { +quickcheck_limit! { fn operations_i8(ops: Large>>) -> bool { let mut map = IndexMap::new(); let mut reference = HashMap::new(); @@ -295,7 +555,7 @@ quickcheck! { let mut reference = HashMap::new(); do_ops(&ops, &mut map, &mut reference); let mut visit = IndexMap::new(); - let keys = Vec::from_iter(map.keys().cloned()); + let keys = Vec::from_iter(map.keys().copied()); for (k, v) in keys.iter().zip(map.values_mut()) { assert_eq!(&reference[k], v); assert!(!visit.contains_key(k)); @@ -372,6 +632,12 @@ quickcheck! { assert_sorted_by_key(map, |t| t.1); } + fn sort_3(keyvals: Large>) -> () { + let mut map: IndexMap<_, _> = IndexMap::from_iter(keyvals.to_vec()); + map.sort_by_cached_key(|&k, _| std::cmp::Reverse(k)); + assert_sorted_by_key(map, |t| std::cmp::Reverse(t.0)); + } + fn reverse(keyvals: Large>) -> () { let mut map: IndexMap<_, _> = IndexMap::from_iter(keyvals.to_vec()); @@ -452,12 +718,12 @@ impl Deref for Alpha { const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; impl Arbitrary for Alpha { - fn arbitrary(g: &mut G) -> Self { - let len = g.next_u32() % g.size() as u32; + fn arbitrary(g: &mut Gen) -> Self { + let len = usize::arbitrary(g) % g.size(); let len = min(len, 16); Alpha( (0..len) - .map(|_| ALPHABET[g.next_u32() as usize % ALPHABET.len()] as char) + .map(|_| ALPHABET[usize::arbitrary(g) % ALPHABET.len()] as char) .collect(), ) } @@ -482,8 +748,8 @@ impl Arbitrary for Large> where T: Arbitrary, { - fn arbitrary(g: &mut G) -> Self { - let len = g.next_u32() % (g.size() * 10) as u32; + fn arbitrary(g: &mut Gen) -> Self { + let len = usize::arbitrary(g) % (g.size() * 10); Large((0..len).map(|_| T::arbitrary(g)).collect()) }