rpds-1.1.0/.cargo_vcs_info.json0000644000000001360000000000100120030ustar { "git": { "sha1": "ec139074ad5a32bcf2815b32f1636c266bca3ca0" }, "path_in_vcs": "" }rpds-1.1.0/Cargo.toml0000644000000046630000000000100100120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60.0" name = "rpds" version = "1.1.0" authors = ["Diogo Sousa "] include = [ "/src/**/*.rs", "/benches/**/*.rs", "/Cargo.toml", "/LICENSE.md", "/README.md", "/release-notes.md", ] description = "Persistent data structures with structural sharing" homepage = "https://github.com/orium/rpds" documentation = "https://docs.rs/rpds" readme = "README.md" keywords = [ "data-structure", "data-structures", "persistent", "immutable", "no_std", ] categories = ["data-structures"] license = "MPL-2.0" repository = "https://github.com/orium/rpds" [package.metadata.docs.rs] features = ["serde"] [[bench]] name = "std_linked_list" harness = false [[bench]] name = "rpds_list" harness = false [[bench]] name = "rpds_list_sync" harness = false [[bench]] name = "std_vec" harness = false [[bench]] name = "rpds_queue" harness = false [[bench]] name = "rpds_queue_sync" harness = false [[bench]] name = "std_vec_deque" harness = false [[bench]] name = "rpds_vector" harness = false [[bench]] name = "rpds_vector_sync" harness = false [[bench]] name = "std_hash_map" harness = false [[bench]] name = "rpds_hash_trie_map" harness = false [[bench]] name = "rpds_hash_trie_map_sync" harness = false [[bench]] name = "std_btree_map" harness = false [[bench]] name = "rpds_red_black_tree_map" harness = false [[bench]] name = "rpds_red_black_tree_map_sync" harness = false [dependencies.archery] version = "1.1.0" features = ["triomphe"] [dependencies.serde] version = "1.0.149" optional = true default-features = false [dev-dependencies.bincode] version = "1.3.3" [dev-dependencies.criterion] version = "0.5.1" [dev-dependencies.pretty_assertions] version = "1.3.0" [dev-dependencies.rand] version = "0.8.5" [dev-dependencies.static_assertions] version = "1.1.0" [features] default = ["std"] fatal-warnings = [] std = [] [badges.codecov] branch = "main" repository = "orium/rpds" service = "github" rpds-1.1.0/Cargo.toml.orig000064400000000000000000000036641046102023000134730ustar 00000000000000[package] name = "rpds" description = "Persistent data structures with structural sharing" version = "1.1.0" authors = ["Diogo Sousa "] edition = "2021" rust-version = "1.60.0" homepage = "https://github.com/orium/rpds" repository = "https://github.com/orium/rpds" documentation = "https://docs.rs/rpds" readme = "README.md" keywords = [ "data-structure", "data-structures", "persistent", "immutable", "no_std" ] categories = [ "data-structures", ] license = "MPL-2.0" # What to include when packaging. include = [ "/src/**/*.rs", "/benches/**/*.rs", "/Cargo.toml", "/LICENSE.md", "/README.md", "/release-notes.md", ] [badges] codecov = { repository = "orium/rpds", branch = "main", service = "github" } [dependencies] archery = { version = "1.1.0", features = ["triomphe"] } serde = { version = "1.0.149", optional = true, default-features = false } [dev-dependencies] criterion = "0.5.1" rand = "0.8.5" # Needed to test serde: bincode = "1.3.3" pretty_assertions = "1.3.0" static_assertions = "1.1.0" [features] default = ["std"] fatal-warnings = [] std = [] [package.metadata.docs.rs] features = ["serde"] [[bench]] name = "std_linked_list" harness = false [[bench]] name = "rpds_list" harness = false [[bench]] name = "rpds_list_sync" harness = false [[bench]] name = "std_vec" harness = false [[bench]] name = "rpds_queue" harness = false [[bench]] name = "rpds_queue_sync" harness = false [[bench]] name = "std_vec_deque" harness = false [[bench]] name = "rpds_vector" harness = false [[bench]] name = "rpds_vector_sync" harness = false [[bench]] name = "std_hash_map" harness = false [[bench]] name = "rpds_hash_trie_map" harness = false [[bench]] name = "rpds_hash_trie_map_sync" harness = false [[bench]] name = "std_btree_map" harness = false [[bench]] name = "rpds_red_black_tree_map" harness = false [[bench]] name = "rpds_red_black_tree_map_sync" harness = false rpds-1.1.0/LICENSE.md000064400000000000000000000362761046102023000122150ustar 00000000000000Mozilla Public License Version 2.0 ================================== ### 1. Definitions **1.1. “Contributor”** means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. **1.2. “Contributor Version”** means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. **1.3. “Contribution”** means Covered Software of a particular Contributor. **1.4. “Covered Software”** means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. **1.5. “Incompatible With Secondary Licenses”** means * **(a)** that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or * **(b)** that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. **1.6. “Executable Form”** means any form of the work other than Source Code Form. **1.7. “Larger Work”** means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. **1.8. “License”** means this document. **1.9. “Licensable”** means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. **1.10. “Modifications”** means any of the following: * **(a)** any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or * **(b)** any new file in Source Code Form that contains any Covered Software. **1.11. “Patent Claims” of a Contributor** means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. **1.12. “Secondary License”** means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. **1.13. “Source Code Form”** means the form of the work preferred for making modifications. **1.14. “You” (or “Your”)** means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means **(a)** the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or **(b)** ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. ### 2. License Grants and Conditions #### 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: * **(a)** under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and * **(b)** under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. #### 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. #### 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: * **(a)** for any code that a Contributor has removed from Covered Software; or * **(b)** for infringements caused by: **(i)** Your and any other third party's modifications of Covered Software, or **(ii)** the combination of its Contributions with other software (except as part of its Contributor Version); or * **(c)** under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). #### 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). #### 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. #### 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. #### 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. ### 3. Responsibilities #### 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. #### 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: * **(a)** such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and * **(b)** You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. #### 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). #### 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. #### 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. ### 4. Inability to Comply Due to Statute or Regulation If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: **(a)** comply with the terms of this License to the maximum extent possible; and **(b)** describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. ### 5. Termination **5.1.** The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated **(a)** provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and **(b)** on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. **5.2.** If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ### 6. Disclaimer of Warranty > Covered Software is provided under this License on an “as is” > basis, without warranty of any kind, either expressed, implied, or > statutory, including, without limitation, warranties that the > Covered Software is free of defects, merchantable, fit for a > particular purpose or non-infringing. The entire risk as to the > quality and performance of the Covered Software is with You. > Should any Covered Software prove defective in any respect, You > (not any Contributor) assume the cost of any necessary servicing, > repair, or correction. This disclaimer of warranty constitutes an > essential part of this License. No use of any Covered Software is > authorized under this License except under this disclaimer. ### 7. Limitation of Liability > Under no circumstances and under no legal theory, whether tort > (including negligence), contract, or otherwise, shall any > Contributor, or anyone who distributes Covered Software as > permitted above, be liable to You for any direct, indirect, > special, incidental, or consequential damages of any character > including, without limitation, damages for lost profits, loss of > goodwill, work stoppage, computer failure or malfunction, or any > and all other commercial damages or losses, even if such party > shall have been informed of the possibility of such damages. This > limitation of liability shall not apply to liability for death or > personal injury resulting from such party's negligence to the > extent applicable law prohibits such limitation. Some > jurisdictions do not allow the exclusion or limitation of > incidental or consequential damages, so this exclusion and > limitation may not apply to You. ### 8. Litigation Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. ### 9. Miscellaneous This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. ### 10. Versions of the License #### 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. #### 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. #### 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). #### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. ## Exhibit A - Source Code Form License Notice This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. ## Exhibit B - “Incompatible With Secondary Licenses” Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. rpds-1.1.0/README.md000064400000000000000000000234361046102023000120620ustar 00000000000000[![Build Status](https://github.com/orium/rpds/workflows/CI/badge.svg)](https://github.com/orium/rpds/actions?query=workflow%3ACI) [![Code Coverage](https://codecov.io/gh/orium/rpds/branch/main/graph/badge.svg)](https://codecov.io/gh/orium/rpds) [![Dependency status](https://deps.rs/repo/github/orium/rpds/status.svg)](https://deps.rs/repo/github/orium/rpds) [![crates.io](https://img.shields.io/crates/v/rpds.svg)](https://crates.io/crates/rpds) [![Downloads](https://img.shields.io/crates/d/rpds.svg)](https://crates.io/crates/rpds) [![Github stars](https://img.shields.io/github/stars/orium/rpds.svg?logo=github)](https://github.com/orium/rpds/stargazers) [![Documentation](https://docs.rs/rpds/badge.svg)](https://docs.rs/rpds/) [![License](https://img.shields.io/crates/l/rpds.svg)](./LICENSE.md) # Rust Persistent Data Structures Rust Persistent Data Structures provides [fully persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure) with structural sharing. ## Setup To use rpds add the following to your `Cargo.toml`: ```toml [dependencies] rpds = "" ``` ## Data structures This crate offers the following data structures: 1. [`List`](#list) 2. [`Vector`](#vector) 3. [`Stack`](#stack) 4. [`Queue`](#queue) 5. [`HashTrieMap`](#hashtriemap) 6. [`HashTrieSet`](#hashtrieset) 7. [`RedBlackTreeMap`](#redblacktreemap) 8. [`RedBlackTreeSet`](#redblacktreeset) ### `List` [![List documentation](https://img.shields.io/badge/doc-List-303070.svg)](https://docs.rs/rpds/latest/rpds/list/struct.List.html) Your classic functional list. #### Example ```rust use rpds::List; let list = List::new().push_front("list"); assert_eq!(list.first(), Some(&"list")); let a_list = list.push_front("a"); assert_eq!(a_list.first(), Some(&"a")); let list_dropped = a_list.drop_first().unwrap(); assert_eq!(list_dropped, list); ``` ### `Vector` [![`Vector` documentation](https://img.shields.io/badge/doc-Vector-303070.svg)](https://docs.rs/rpds/latest/rpds/vector/struct.Vector.html) A sequence that can be indexed. The implementation is described in [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1) and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2). #### Example ```rust use rpds::Vector; let vector = Vector::new() .push_back("I’m") .push_back("a") .push_back("vector"); assert_eq!(vector[1], "a"); let screaming_vector = vector .drop_last().unwrap() .push_back("VECTOR!!!"); assert_eq!(screaming_vector[2], "VECTOR!!!"); ``` ### `Stack` [![`Stack` documentation](https://img.shields.io/badge/doc-Stack-303070.svg)](https://docs.rs/rpds/latest/rpds/stack/struct.Stack.html) A LIFO (last in, first out) data structure. This is just a [`List`](#list) in disguise. #### Example ```rust use rpds::Stack; let stack = Stack::new().push("stack"); assert_eq!(stack.peek(), Some(&"stack")); let a_stack = stack.push("a"); assert_eq!(a_stack.peek(), Some(&"a")); let stack_popped = a_stack.pop().unwrap(); assert_eq!(stack_popped, stack); ``` ### `Queue` [![`Queue` documentation](https://img.shields.io/badge/doc-Queue-303070.svg)](https://docs.rs/rpds/latest/rpds/queue/struct.Queue.html) A FIFO (first in, first out) data structure. #### Example ```rust use rpds::Queue; let queue = Queue::new() .enqueue("um") .enqueue("dois") .enqueue("tres"); assert_eq!(queue.peek(), Some(&"um")); let queue_dequeued = queue.dequeue().unwrap(); assert_eq!(queue_dequeued.peek(), Some(&"dois")); ``` ### `HashTrieMap` [![`HashTrieMap` documentation](https://img.shields.io/badge/doc-HashTrieMap-303070.svg)](https://docs.rs/rpds/latest/rpds/map/hash_trie_map/struct.HashTrieMap.html) A map implemented with a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie). See [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf) for details. #### Example ```rust use rpds::HashTrieMap; let map_en = HashTrieMap::new() .insert(0, "zero") .insert(1, "one"); assert_eq!(map_en.get(&1), Some(&"one")); let map_pt = map_en .insert(1, "um") .insert(2, "dois"); assert_eq!(map_pt.get(&2), Some(&"dois")); let map_pt_binary = map_pt.remove(&2); assert_eq!(map_pt_binary.get(&2), None); ``` ### `HashTrieSet` [![`HashTrieSet` documentation](https://img.shields.io/badge/doc-HashTrieSet-303070.svg)](https://docs.rs/rpds/latest/rpds/set/hash_trie_set/struct.HashTrieSet.html) A set implemented with a [`HashTrieMap`](#hashtriemap). #### Example ```rust use rpds::HashTrieSet; let set = HashTrieSet::new() .insert("zero") .insert("one"); assert!(set.contains(&"one")); let set_extended = set.insert("two"); assert!(set_extended.contains(&"two")); let set_positive = set_extended.remove(&"zero"); assert!(!set_positive.contains(&"zero")); ``` ### `RedBlackTreeMap` [![`RedBlackTreeMap` documentation](https://img.shields.io/badge/doc-RedBlackTreeMap-303070.svg)](https://docs.rs/rpds/latest/rpds/map/red_black_tree_map/struct.RedBlackTreeMap.html) A map implemented with a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree). #### Example ```rust use rpds::RedBlackTreeMap; let map_en = RedBlackTreeMap::new() .insert(0, "zero") .insert(1, "one"); assert_eq!(map_en.get(&1), Some(&"one")); let map_pt = map_en .insert(1, "um") .insert(2, "dois"); assert_eq!(map_pt.get(&2), Some(&"dois")); let map_pt_binary = map_pt.remove(&2); assert_eq!(map_pt_binary.get(&2), None); assert_eq!(map_pt_binary.first(), Some((&0, &"zero"))); ``` ### `RedBlackTreeSet` [![`RedBlackTreeSet` documentation](https://img.shields.io/badge/doc-RedBlackTreeSet-303070.svg)](https://docs.rs/rpds/latest/rpds/set/red_black_tree_set/struct.RedBlackTreeSet.html) A set implemented with a [`RedBlackTreeMap`](#redblacktreemap). #### Example ```rust use rpds::RedBlackTreeSet; let set = RedBlackTreeSet::new() .insert("zero") .insert("one"); assert!(set.contains(&"one")); let set_extended = set.insert("two"); assert!(set_extended.contains(&"two")); let set_positive = set_extended.remove(&"zero"); assert!(!set_positive.contains(&"zero")); assert_eq!(set_positive.first(), Some(&"one")); ``` ## Other features ### Mutable methods When you change a data structure you often do not need its previous versions. For those cases rpds offers you mutable methods which are generally faster: ```rust use rpds::HashTrieSet; let mut set = HashTrieSet::new(); set.insert_mut("zero"); set.insert_mut("one"); let set_0_1 = set.clone(); let set_0_1_2 = set.insert("two"); ``` ### Initialization macros There are convenient initialization macros for all data structures: ```rust use rpds::*; let vector = vector![3, 1, 4, 1, 5]; let map = ht_map!["orange" => "orange", "banana" => "yellow"]; ``` Check the documentation for initialization macros of other data structures. ### Thread safety All data structures in this crate can be shared between threads, but that is an opt-in ability. This is because there is a performance cost to make data structures thread safe. That cost is worth avoiding when you are not actually sharing them between threads. Of course if you try to share a rpds data structure across different threads you can count on the rust compiler to ensure that it is safe to do so. If you are using the version of the data structure that is not thread safe you will get a compile-time error. To create a thread-safe version of any data structure use `new_sync()`: ```rust let vec = Vector::new_sync() .push_back(42); ``` Or use the `_sync` variant of the initialization macro: ```rust let vec = vector_sync!(42); ``` #### Further details Internally the data structures in this crate maintain a lot of reference-counting pointers. These pointers are used both for links between the internal nodes of the data structure as well as for the values it stores. There are two implementations of reference-counting pointers in the standard library: [`Rc`](https://doc.rust-lang.org/stable/alloc/rc/struct.Rc.html) and [`Arc`](https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html). They behave the same way, but `Arc` allows you to share the data it points to across multiple threads. The downside is that it is significantly slower to clone and drop than `Rc`, and persistent data structures do a lot of those operations. In some microbenchmarks with rpds data structure we can see that using `Rc` instead of `Arc` can make some operations twice as fast! You can see this for yourself by running `cargo bench`. To implement this we parameterize the type of reference-counting pointer (`Rc` or `Arc`) as a type argument of the data structure. We use the [archery](https://github.com/orium/archery/) crate to do this in a convenient way. The pointer type can be parameterized like this: ```rust let vec: Vector = Vector::new_with_ptr_kind(); // ↖ // This will use `Arc` pointers. // Change it to `archery::RcK` to use a `Rc` pointer. ``` ### `no_std` support This crate supports `no_std`. To enable that you need to disable the default feature `std`: ```toml [dependencies] rpds = { version = "", default-features = false } ``` ### Serialization We support serialization through [serde](https://crates.io/crates/serde). To use it enable the `serde` feature. To do so change the rpds dependency in your `Cargo.toml` to ```toml [dependencies] rpds = { version = "", features = ["serde"] } ``` ### Bindings Bindings to use rpds from other programming languages exist. Below is a short list of those known to date. * [rpds.py](https://github.com/crate-py/rpds/) – Python Please feel free to send a pull request should you add support in a new language. rpds-1.1.0/benches/rpds_hash_trie_map.rs000064400000000000000000000061411046102023000164050ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::HashTrieMap; use std::hint::black_box; fn rpds_hash_trie_map_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map insert", move |b| { b.iter(|| { let mut map = HashTrieMap::new(); for i in 0..limit { map = map.insert(i, -(i as isize)); } map }) }); } fn rpds_hash_trie_map_insert_mut(c: &mut Criterion) { let limit = 50_000; c.bench_function("rpds hash trie map insert mut", move |b| { b.iter(|| { let mut map = HashTrieMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }) }); } fn rpds_hash_trie_map_remove(c: &mut Criterion) { let limit = 50_000; c.bench_function("rpds hash trie map remove", move |b| { b.iter_with_setup( || { let mut map = HashTrieMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map = map.remove(&i); } map }, ); }); } fn rpds_hash_trie_map_remove_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map remove mut", move |b| { b.iter_with_setup( || { let mut map = HashTrieMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove_mut(&i); } map }, ); }); } fn rpds_hash_trie_map_get(c: &mut Criterion) { let limit = 100_000; let mut map = HashTrieMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds hash trie map get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn rpds_hash_trie_map_iterate(c: &mut Criterion) { let limit = 100_000; let mut map = HashTrieMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds hash trie map iterate", move |b| { b.iter(|| { for kv in map.iter() { black_box(kv); } }) }); } criterion_group!( benches, rpds_hash_trie_map_insert, rpds_hash_trie_map_insert_mut, rpds_hash_trie_map_remove, rpds_hash_trie_map_remove_mut, rpds_hash_trie_map_get, rpds_hash_trie_map_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_hash_trie_map_sync.rs000064400000000000000000000063711046102023000174460ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::HashTrieMapSync; use std::hint::black_box; fn rpds_hash_trie_map_sync_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map sync insert", move |b| { b.iter(|| { let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map = map.insert(i, -(i as isize)); } map }) }); } fn rpds_hash_trie_map_sync_insert_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map sync insert mut", move |b| { b.iter(|| { let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }) }); } fn rpds_hash_trie_map_sync_remove(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map sync remove", move |b| { b.iter_with_setup( || { let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map = map.remove(&i); } map }, ); }); } fn rpds_hash_trie_map_sync_remove_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds hash trie map sync remove mut", move |b| { b.iter_with_setup( || { let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove_mut(&i); } map }, ); }); } fn rpds_hash_trie_map_sync_get(c: &mut Criterion) { let limit = 100_000; let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds hash trie map sync get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn rpds_hash_trie_map_sync_iterate(c: &mut Criterion) { let limit = 1_000_000; let mut map = HashTrieMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds hash trie map sync iterate", move |b| { b.iter(|| { for kv in map.iter() { black_box(kv); } }) }); } criterion_group!( benches, rpds_hash_trie_map_sync_insert, rpds_hash_trie_map_sync_insert_mut, rpds_hash_trie_map_sync_remove, rpds_hash_trie_map_sync_remove_mut, rpds_hash_trie_map_sync_get, rpds_hash_trie_map_sync_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_list.rs000064400000000000000000000073361046102023000145640ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::List; use std::hint::black_box; fn rpds_list_push_front(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list push front", move |b| { b.iter(|| { let mut list: List = List::new(); for i in 0..limit { list = list.push_front(i); } list }) }); } fn rpds_list_push_front_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list push front mut", move |b| { b.iter(|| { let mut list: List = List::new(); for i in 0..limit { list.push_front_mut(i); } list }) }); } fn rpds_list_drop_first(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list drop first", move |b| { b.iter_with_setup( || { let mut list: List = List::new(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list = list.drop_first().unwrap(); } list }, ); }); } fn rpds_list_drop_first_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list drop first mut", move |b| { b.iter_with_setup( || { let mut list: List = List::new(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list.drop_first_mut(); } list }, ); }); } fn rpds_list_reverse(c: &mut Criterion) { let limit = 10_000; c.bench_function("rpds list reverse", move |b| { b.iter_with_setup( || { let mut list: List = List::new(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list = list.reverse(); } list }, ); }); } fn rpds_list_reverse_mut(c: &mut Criterion) { let limit = 10_000; c.bench_function("rpds list reverse mut", move |b| { b.iter_with_setup( || { let mut list: List = List::new(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list.reverse_mut(); } list }, ); }); } fn rpds_list_iterate(c: &mut Criterion) { let limit = 100_000; let mut list = List::new(); for i in 0..limit { list.push_front_mut(i); } c.bench_function("rpds list iterate", move |b| { b.iter(|| { for i in list.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_list_push_front, rpds_list_push_front_mut, rpds_list_drop_first, rpds_list_drop_first_mut, rpds_list_reverse, rpds_list_reverse_mut, rpds_list_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_list_sync.rs000064400000000000000000000076631046102023000156230ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::ListSync; use std::hint::black_box; fn rpds_list_sync_push_front(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list sync push front", move |b| { b.iter(|| { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list = list.push_front(i); } list }) }); } fn rpds_list_sync_push_front_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list sync push front mut", move |b| { b.iter(|| { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } list }) }); } fn rpds_list_sync_drop_first(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list sync drop first", move |b| { b.iter_with_setup( || { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list = list.drop_first().unwrap(); } list }, ); }); } fn rpds_list_sync_drop_first_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds list sync drop first mut", move |b| { b.iter_with_setup( || { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list.drop_first_mut(); } list }, ); }); } fn rpds_list_sync_reverse(c: &mut Criterion) { let limit = 2_000; c.bench_function("rpds list sync reverse", move |b| { b.iter_with_setup( || { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list = list.reverse(); } list }, ); }); } fn rpds_list_sync_reverse_mut(c: &mut Criterion) { let limit = 2_000; c.bench_function("rpds list sync reverse mut", move |b| { b.iter_with_setup( || { let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } list }, |mut list| { for _ in 0..limit { list.reverse_mut(); } list }, ); }); } fn rpds_list_sync_iterate(c: &mut Criterion) { let limit = 1_000_000; let mut list: ListSync = ListSync::new_sync(); for i in 0..limit { list.push_front_mut(i); } c.bench_function("rpds list sync iterate", move |b| { b.iter(|| { for i in list.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_list_sync_push_front, rpds_list_sync_push_front_mut, rpds_list_sync_drop_first, rpds_list_sync_drop_first_mut, rpds_list_sync_reverse, rpds_list_sync_reverse_mut, rpds_list_sync_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_queue.rs000064400000000000000000000051701046102023000147270ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::Queue; use std::hint::black_box; fn rpds_queue_enqueue(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue enqueue", move |b| { b.iter(|| { let mut queue: Queue = Queue::new(); for i in 0..limit { queue = queue.enqueue(i); } queue }) }); } fn rpds_queue_enqueue_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue enqueue mut", move |b| { b.iter(|| { let mut queue: Queue = Queue::new(); for i in 0..limit { queue.enqueue_mut(i); } queue }) }); } fn rpds_queue_dequeue(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue dequeue", move |b| { b.iter_with_setup( || { let mut queue: Queue = Queue::new(); for i in 0..limit { queue.enqueue_mut(i); } queue }, |mut queue| { for _ in 0..limit { queue = queue.dequeue().unwrap(); } queue }, ); }); } fn rpds_queue_dequeue_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue dequeue mut", move |b| { b.iter_with_setup( || { let mut queue: Queue = Queue::new(); for i in 0..limit { queue.enqueue_mut(i); } queue }, |mut queue| { for _ in 0..limit { queue.dequeue_mut(); } queue }, ); }); } fn rpds_queue_iterate(c: &mut Criterion) { let limit = 100_000; let mut queue: Queue = Queue::new(); for i in 0..limit { queue.enqueue_mut(i); } c.bench_function("rpds queue iterate", move |b| { b.iter(|| { for i in queue.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_queue_enqueue, rpds_queue_enqueue_mut, rpds_queue_dequeue, rpds_queue_dequeue_mut, rpds_queue_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_queue_sync.rs000064400000000000000000000054121046102023000157620ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::QueueSync; use std::hint::black_box; fn rpds_queue_sync_enqueue(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue sync enqueue", move |b| { b.iter(|| { let mut queue: QueueSync = QueueSync::new_sync(); for i in 0..limit { queue = queue.enqueue(i); } queue }) }); } fn rpds_queue_sync_enqueue_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue sync enqueue mut", move |b| { b.iter(|| { let mut queue: QueueSync = QueueSync::new_sync(); for i in 0..limit { queue.enqueue_mut(i); } queue }) }); } fn rpds_queue_sync_dequeue(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue sync dequeue", move |b| { b.iter_with_setup( || { let mut queue: QueueSync = QueueSync::new_sync(); for i in 0..limit { queue.enqueue_mut(i); } queue }, |mut queue| { for _ in 0..limit { queue = queue.dequeue().unwrap(); } queue }, ); }); } fn rpds_queue_sync_dequeue_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds queue sync dequeue mut", move |b| { b.iter_with_setup( || { let mut queue: QueueSync = QueueSync::new_sync(); for i in 0..limit { queue.enqueue_mut(i); } queue }, |mut queue| { for _ in 0..limit { queue.dequeue_mut(); } queue }, ); }); } fn rpds_queue_sync_iterate(c: &mut Criterion) { let limit = 1_000_000; let mut queue: QueueSync = QueueSync::new_sync(); for i in 0..limit { queue.enqueue_mut(i); } c.bench_function("rpds queue sync iterate", move |b| { b.iter(|| { for i in queue.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_queue_sync_enqueue, rpds_queue_sync_enqueue_mut, rpds_queue_sync_dequeue, rpds_queue_sync_dequeue_mut, rpds_queue_sync_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_red_black_tree_map.rs000064400000000000000000000063311046102023000173650ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::RedBlackTreeMap; use std::hint::black_box; fn rpds_red_black_tree_map_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map insert", move |b| { b.iter(|| { let mut map = RedBlackTreeMap::new(); for i in 0..limit { map = map.insert(i, -(i as isize)); } map }) }); } fn rpds_red_black_tree_map_insert_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map insert mut", move |b| { b.iter(|| { let mut map = RedBlackTreeMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }) }); } fn rpds_red_black_tree_map_remove(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map remove", move |b| { b.iter_with_setup( || { let mut map = RedBlackTreeMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map = map.remove(&i); } map }, ); }); } fn rpds_red_black_tree_map_remove_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map remove mut", move |b| { b.iter_with_setup( || { let mut map = RedBlackTreeMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove_mut(&i); } map }, ); }); } fn rpds_red_black_tree_map_get(c: &mut Criterion) { let limit = 100_000; let mut map = RedBlackTreeMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds red black tree map get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn rpds_red_black_tree_map_iterate(c: &mut Criterion) { let limit = 100_000; let mut map = RedBlackTreeMap::new(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds red black tree map iterate", move |b| { b.iter(|| { for kv in map.iter() { black_box(kv); } }) }); } criterion_group!( benches, rpds_red_black_tree_map_insert, rpds_red_black_tree_map_insert_mut, rpds_red_black_tree_map_remove, rpds_red_black_tree_map_remove_mut, rpds_red_black_tree_map_get, rpds_red_black_tree_map_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_red_black_tree_map_sync.rs000064400000000000000000000065571046102023000204330ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::RedBlackTreeMapSync; use std::hint::black_box; fn rpds_red_black_tree_map_sync_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map sync insert", move |b| { b.iter(|| { let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map = map.insert(i, -(i as isize)); } map }) }); } fn rpds_red_black_tree_map_sync_insert_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map sync insert mut", move |b| { b.iter(|| { let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }) }); } fn rpds_red_black_tree_map_sync_remove(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map sync remove", move |b| { b.iter_with_setup( || { let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map = map.remove(&i); } map }, ); }); } fn rpds_red_black_tree_map_sync_remove_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds red black tree map sync remove mut", move |b| { b.iter_with_setup( || { let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove_mut(&i); } map }, ); }); } fn rpds_red_black_tree_map_sync_get(c: &mut Criterion) { let limit = 100_000; let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds red black tree map sync get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn rpds_red_black_tree_map_sync_iterate(c: &mut Criterion) { let limit = 1_000_000; let mut map = RedBlackTreeMapSync::new_sync(); for i in 0..limit { map.insert_mut(i, -(i as isize)); } c.bench_function("rpds red black tree map sync iterate", move |b| { b.iter(|| { for kv in map.iter() { black_box(kv); } }) }); } criterion_group!( benches, rpds_red_black_tree_map_sync_insert, rpds_red_black_tree_map_sync_insert_mut, rpds_red_black_tree_map_sync_remove, rpds_red_black_tree_map_sync_remove_mut, rpds_red_black_tree_map_sync_get, rpds_red_black_tree_map_sync_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_vector.rs000064400000000000000000000061161046102023000151060ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::Vector; use std::hint::black_box; fn rpds_vector_push_back(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector push back", move |b| { b.iter(|| { let mut vector: Vector = Vector::new(); for i in 0..limit { vector = vector.push_back(i); } vector }) }); } fn rpds_vector_push_back_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector push back mut", move |b| { b.iter(|| { let mut vector: Vector = Vector::new(); for i in 0..limit { vector.push_back_mut(i); } vector }) }); } fn rpds_vector_drop_last(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector drop last", move |b| { b.iter_with_setup( || { let mut vector: Vector = Vector::new(); for i in 0..limit { vector.push_back_mut(i); } vector }, |mut vector| { for _ in 0..limit { vector = vector.drop_last().unwrap(); } vector }, ); }); } fn rpds_vector_drop_last_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector drop last mut", move |b| { b.iter_with_setup( || { let mut vector: Vector = Vector::new(); for i in 0..limit { vector.push_back_mut(i); } vector }, |mut vector| { for _ in 0..limit { vector.drop_last_mut(); } vector }, ); }); } fn rpds_vector_get(c: &mut Criterion) { let limit = 100_000; let mut vector: Vector = Vector::new(); for i in 0..limit { vector.push_back_mut(i); } c.bench_function("rpds vector get", move |b| { b.iter(|| { for i in 0..limit { black_box(vector.get(i)); } }) }); } fn rpds_vector_iterate(c: &mut Criterion) { let limit = 100_000; let mut vector: Vector = Vector::new(); for i in 0..limit { vector.push_back_mut(i); } c.bench_function("rpds vector iterate", move |b| { b.iter(|| { for i in vector.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_vector_push_back, rpds_vector_push_back_mut, rpds_vector_drop_last, rpds_vector_drop_last_mut, rpds_vector_get, rpds_vector_iterate ); criterion_main!(benches); rpds-1.1.0/benches/rpds_vector_sync.rs000064400000000000000000000063621046102023000161450ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use rpds::VectorSync; use std::hint::black_box; fn rpds_vector_syncpush_back(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector sync push back", move |b| { b.iter(|| { let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector = vector.push_back(i); } vector }) }); } fn rpds_vector_syncpush_back_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector sync push back mut", move |b| { b.iter(|| { let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector.push_back_mut(i); } vector }) }); } fn rpds_vector_syncdrop_last(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector sync drop last", move |b| { b.iter_with_setup( || { let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector.push_back_mut(i); } vector }, |mut vector| { for _ in 0..limit { vector = vector.drop_last().unwrap(); } vector }, ); }); } fn rpds_vector_syncdrop_last_mut(c: &mut Criterion) { let limit = 100_000; c.bench_function("rpds vector sync drop last mut", move |b| { b.iter_with_setup( || { let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector.push_back_mut(i); } vector }, |mut vector| { for _ in 0..limit { vector.drop_last_mut(); } vector }, ); }); } fn rpds_vector_syncget(c: &mut Criterion) { let limit = 1_000_000; let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector.push_back_mut(i); } c.bench_function("rpds vector sync get", move |b| { b.iter(|| { for i in 0..limit { black_box(vector.get(i)); } }) }); } fn rpds_vector_synciterate(c: &mut Criterion) { let limit = 1_000_000; let mut vector: VectorSync = VectorSync::new_sync(); for i in 0..limit { vector.push_back_mut(i); } c.bench_function("rpds vector sync iterate", move |b| { b.iter(|| { for i in vector.iter() { black_box(i); } }) }); } criterion_group!( benches, rpds_vector_syncpush_back, rpds_vector_syncpush_back_mut, rpds_vector_syncdrop_last, rpds_vector_syncdrop_last_mut, rpds_vector_syncget, rpds_vector_synciterate ); criterion_main!(benches); rpds-1.1.0/benches/std_btree_map.rs000064400000000000000000000042001046102023000153540ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::BTreeMap; use std::hint::black_box; fn std_btree_map_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("std b-tree map insert", move |b| { b.iter(|| { let mut map: BTreeMap = BTreeMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } map }) }); } fn std_btree_map_remove(c: &mut Criterion) { let limit = 100_000; c.bench_function("std btree map remove", move |b| { b.iter_with_setup( || { let mut map = BTreeMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove(&i); } map }, ); }); } fn std_btree_map_get(c: &mut Criterion) { let limit = 100_000; let mut map: BTreeMap = BTreeMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } c.bench_function("std b-tree map get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn std_btree_map_iterate(c: &mut Criterion) { let limit = 100_000; let mut map: BTreeMap = BTreeMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } c.bench_function("std b-tree map iterate", move |b| { b.iter(|| { for kv in &map { black_box(kv); } }) }); } criterion_group!( benches, std_btree_map_insert, std_btree_map_remove, std_btree_map_get, std_btree_map_iterate ); criterion_main!(benches); rpds-1.1.0/benches/std_hash_map.rs000064400000000000000000000041511046102023000152030ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::HashMap; use std::hint::black_box; fn std_hash_map_insert(c: &mut Criterion) { let limit = 100_000; c.bench_function("std hash map insert", move |b| { b.iter(|| { let mut map: HashMap = HashMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } map }) }); } fn std_hash_map_remove(c: &mut Criterion) { let limit = 100_000; c.bench_function("std hash map remove", move |b| { b.iter_with_setup( || { let mut map = HashMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } map }, |mut map| { for i in 0..limit { map.remove(&i); } map }, ); }); } fn std_hash_map_get(c: &mut Criterion) { let limit = 100_000; let mut map: HashMap = HashMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } c.bench_function("std hash map get", move |b| { b.iter(|| { for i in 0..limit { black_box(map.get(&i)); } }) }); } fn std_hash_map_iterate(c: &mut Criterion) { let limit = 100_000; let mut map: HashMap = HashMap::new(); for i in 0..limit { map.insert(i, -(i as isize)); } c.bench_function("std hash map iterate", move |b| { b.iter(|| { for kv in &map { black_box(kv); } }) }); } criterion_group!( benches, std_hash_map_insert, std_hash_map_remove, std_hash_map_get, std_hash_map_iterate ); criterion_main!(benches); rpds-1.1.0/benches/std_linked_list.rs000064400000000000000000000055371046102023000157350ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::LinkedList; use std::hint::black_box; fn std_linked_list_push_front(c: &mut Criterion) { let limit = 100_000; c.bench_function("std linked list push front", move |b| { b.iter(|| { let mut linked_list: LinkedList = LinkedList::new(); for i in 0..limit { linked_list.push_front(i); } linked_list }) }); } fn std_linked_list_push_back(c: &mut Criterion) { let limit = 100_000; c.bench_function("std linked list push back", move |b| { b.iter(|| { let mut linked_list: LinkedList = LinkedList::new(); for i in 0..limit { linked_list.push_back(i); } linked_list }) }); } fn std_linked_list_pop_front(c: &mut Criterion) { let limit = 100_000; c.bench_function("std linked list pop front", move |b| { b.iter_with_setup( || { let mut linked_list: LinkedList = LinkedList::new(); for i in 0..limit { linked_list.push_back(i); } linked_list }, |mut linked_list| { for _ in 0..limit { linked_list.pop_front(); } linked_list }, ); }); } fn std_linked_list_pop_back(c: &mut Criterion) { let limit = 100_000; c.bench_function("std linked list pop back", move |b| { b.iter_with_setup( || { let mut linked_list: LinkedList = LinkedList::new(); for i in 0..limit { linked_list.push_back(i); } linked_list }, |mut linked_list| { for _ in 0..limit { linked_list.pop_back(); } linked_list }, ); }); } fn std_linked_list_iterate(c: &mut Criterion) { let limit = 100_000; let mut linked_list: LinkedList = LinkedList::new(); for i in 0..limit { linked_list.push_back(i); } c.bench_function("std linked list iterate", move |b| { b.iter(|| { for i in &linked_list { black_box(i); } }) }); } criterion_group!( benches, std_linked_list_push_front, std_linked_list_push_back, std_linked_list_pop_front, std_linked_list_pop_back, std_linked_list_iterate ); criterion_main!(benches); rpds-1.1.0/benches/std_vec.rs000064400000000000000000000047051046102023000142050ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use std::hint::black_box; fn std_vec_push(c: &mut Criterion) { let limit = 100_000; c.bench_function("std vec push", move |b| { b.iter(|| { let mut vector: Vec = Vec::new(); for i in 0..limit { vector.push(i); } vector }) }); } fn std_vec_pop(c: &mut Criterion) { let limit = 100_000; c.bench_function("std vec pop", move |b| { b.iter_with_setup( || { let mut vector: Vec = Vec::new(); for i in 0..limit { vector.push(i); } vector }, |mut vector| { for _ in 0..limit { vector.pop(); } vector }, ); }); } fn std_vec_reverse(c: &mut Criterion) { let limit = 10_000; c.bench_function("std vec reverse", move |b| { b.iter_with_setup( || { let mut vector: Vec = Vec::new(); for i in 0..limit { vector.push(i); } vector }, |mut vector| { for _ in 0..limit { vector.reverse(); } vector }, ); }); } fn std_vec_get(c: &mut Criterion) { let limit = 100_000; let mut vector: Vec = Vec::new(); for i in 0..limit { vector.push(i); } c.bench_function("std vec get", move |b| { b.iter(|| { for i in 0..limit { black_box(vector.get(i)); } }) }); } fn std_vec_iterate(c: &mut Criterion) { let limit = 100_000; let mut vector: Vec = Vec::new(); for i in 0..limit { vector.push(i); } c.bench_function("std vec iterate", move |b| { b.iter(|| { for i in &vector { black_box(i); } }) }); } criterion_group!(benches, std_vec_push, std_vec_pop, std_vec_reverse, std_vec_get, std_vec_iterate); criterion_main!(benches); rpds-1.1.0/benches/std_vec_deque.rs000064400000000000000000000034151046102023000153650ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(feature = "fatal-warnings", deny(warnings))] use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::VecDeque; use std::hint::black_box; fn std_vec_dequeue_push_back(c: &mut Criterion) { let limit = 100_000; c.bench_function("std vec dequeue push back", move |b| { b.iter(|| { let mut deque: VecDeque = VecDeque::new(); for i in 0..limit { deque.push_back(i); } deque }) }); } fn std_vec_dequeue_pop_front(c: &mut Criterion) { let limit = 100_000; c.bench_function("std vec dequeue pop front", move |b| { b.iter_with_setup( || { let mut queue: VecDeque = VecDeque::new(); for i in 0..limit { queue.push_back(i); } queue }, |mut queue| { for _ in 0..limit { queue.pop_front(); } queue }, ); }); } fn std_vec_dequeue_iterate(c: &mut Criterion) { let limit = 100_000; let mut deque: VecDeque = VecDeque::new(); for i in 0..limit { deque.push_back(i); } c.bench_function("std vec dequeue iterate", move |b| { b.iter(|| { for i in deque.iter() { black_box(i); } }) }); } criterion_group!( benches, std_vec_dequeue_push_back, std_vec_dequeue_pop_front, std_vec_dequeue_iterate ); criterion_main!(benches); rpds-1.1.0/release-notes.md000064400000000000000000000052021046102023000136620ustar 00000000000000# Release notes ## 1.1.0 * Use [triomphe](https://crates.io/crates/triomphe) reference-counting pointer by default in `Sync` data structures, which improves their performance. ## 1.0.1 * Fix the tests of `SparseArrayUsize` on 32-bit computers. This issue did not affect production code which did work correctly on 32-bit platforms. ## 1.0.0 * First stable version. It’s time to commit to a stable release :). * Improved performance of equality check for `{HashTrie,RedBlackTree}Map` and `{HashTrie,RedBlackTree}Set`, as well as subset and superset checks for `{HashTrie,RedBlackTree}Set` when the references are the same. ## 0.13.0 * Updated archery fixing a soundness bug. See issue [#18](https://github.com/orium/archery/issues/18). ## 0.12.0 * Implemented `Hash` for `RedBlackTreeSet`. ## 0.11.0 * Added `{HashTrie,RedBlackTree}Map::get_key_value()` and `{HashTrie,RedBlackTree}Set::get()`. ## 0.10.0 * Improved `{HashTrieMap,HashTrieSet}` iteration performance. ## 0.9.0 * Added `{HashTrie,RedBlackTree}Map::get_mut()`. * Improved `HashTrieMap` performance when using `Rc` pointers. ## 0.8.0 * Added support for `no_std`. ## 0.7.0 * Now the shared pointer type of all data structures use can be parameterizable. See the [Thread safety](./README.md#thread-safety) section in the README for details. ([#7](https://github.com/orium/rpds/issues/7)) * Fix bug where dropping long lists would cause a stack overflow. ([#46](https://github.com/orium/rpds/issues/46)) ## 0.6.0 * Implemented `RedBlackTree{Map,Set}::range()` iterator. * Implemented `IndexMut` and `Vector::get_mut()`. * Added `#[must_use]` to the immutable methods of all data structures. * Improved performance of `List::reverse_mut()`. * Improved performance of `RedBlackTreeSet` serialization. ## 0.5.0 * Mutable methods galore. Now all data structures offer mutable methods. These are generally much faster! * Implemented `Extend` for `Vector`. ## 0.4.0 * Added macros to create data structures with the given values (analog to `vec![]`). * Added `{HashTrieSet,RedBlackTreeSet}::{is_disjoint(),is_subset(),is_superset()}`. ## 0.3.0 * Added support for serialization with serde. * Speed-up `HashTrieMap::remove()` by ~70%. * Speed-up `Vector::push_back()` by ~80%. ## 0.2.0 * Implemented `RedBlackTreeMap` data structure. * Implemented `RedBlackTreeSet` data structure. ## 0.1.0 * Implemented `Queue` data structure. * Implemented `HashTrieSet` data structure. * Implemented `Stack` data structure. * Implemented `List::last()` and `List::reverse()`. ## 0.0.0 * Initial release of rpds. This release contains these data structures: `List`, `Vector`, and `HashTrieMap`. rpds-1.1.0/src/lib.rs000064400000000000000000000273651046102023000125130ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "fatal-warnings", deny(warnings))] #![deny(clippy::correctness)] #![warn(clippy::pedantic)] #![allow(clippy::match_bool)] #![allow(clippy::if_not_else)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::similar_names)] #![allow(clippy::use_self)] #![allow(clippy::single_match_else)] #![allow(clippy::unnested_or_patterns)] #![allow(clippy::wildcard_imports)] #![allow(clippy::match_same_arms)] #![allow(clippy::missing_panics_doc)] #![allow(clippy::type_complexity)] #![allow(clippy::enum_glob_use)] #![allow(clippy::type_repetition_in_bounds)] // TODO This is only needed because `cargo-rdme` requires a path like `crate::⋯`. Once that limitation is // lifted we can remove this. #![allow(rustdoc::redundant_explicit_links)] // Note: If you change this remember to update `README.md`. To do so run `cargo rdme`. //! Rust Persistent Data Structures provides [fully persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure) //! with structural sharing. //! //! # Setup //! //! To use rpds add the following to your `Cargo.toml`: //! //! ```toml //! [dependencies] //! rpds = "" //! ``` //! //! # Data structures //! //! This crate offers the following data structures: //! //! 1. [`List`](#list) //! 2. [`Vector`](#vector) //! 3. [`Stack`](#stack) //! 4. [`Queue`](#queue) //! 5. [`HashTrieMap`](#hashtriemap) //! 6. [`HashTrieSet`](#hashtrieset) //! 7. [`RedBlackTreeMap`](#redblacktreemap) //! 8. [`RedBlackTreeSet`](#redblacktreeset) //! //! ## `List` //! [![List documentation](https://img.shields.io/badge/doc-List-303070.svg)](crate::list::List) //! //! Your classic functional list. //! //! ### Example //! //! ```rust //! use rpds::List; //! //! let list = List::new().push_front("list"); //! //! assert_eq!(list.first(), Some(&"list")); //! //! let a_list = list.push_front("a"); //! //! assert_eq!(a_list.first(), Some(&"a")); //! //! let list_dropped = a_list.drop_first().unwrap(); //! //! assert_eq!(list_dropped, list); //! ``` //! //! ## `Vector` //! [![`Vector` documentation](https://img.shields.io/badge/doc-Vector-303070.svg)](crate::vector::Vector) //! //! A sequence that can be indexed. The implementation is described in //! [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1) //! and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2). //! //! ### Example //! //! ```rust //! use rpds::Vector; //! //! let vector = Vector::new() //! .push_back("I’m") //! .push_back("a") //! .push_back("vector"); //! //! assert_eq!(vector[1], "a"); //! //! let screaming_vector = vector //! .drop_last().unwrap() //! .push_back("VECTOR!!!"); //! //! assert_eq!(screaming_vector[2], "VECTOR!!!"); //! ``` //! //! ## `Stack` //! [![`Stack` documentation](https://img.shields.io/badge/doc-Stack-303070.svg)](crate::stack::Stack) //! //! A LIFO (last in, first out) data structure. This is just a [`List`](#list) in disguise. //! //! ### Example //! //! ```rust //! use rpds::Stack; //! //! let stack = Stack::new().push("stack"); //! //! assert_eq!(stack.peek(), Some(&"stack")); //! //! let a_stack = stack.push("a"); //! //! assert_eq!(a_stack.peek(), Some(&"a")); //! //! let stack_popped = a_stack.pop().unwrap(); //! //! assert_eq!(stack_popped, stack); //! ``` //! //! ## `Queue` //! [![`Queue` documentation](https://img.shields.io/badge/doc-Queue-303070.svg)](crate::queue::Queue) //! //! A FIFO (first in, first out) data structure. //! //! ### Example //! //! ```rust //! use rpds::Queue; //! //! let queue = Queue::new() //! .enqueue("um") //! .enqueue("dois") //! .enqueue("tres"); //! //! assert_eq!(queue.peek(), Some(&"um")); //! //! let queue_dequeued = queue.dequeue().unwrap(); //! //! assert_eq!(queue_dequeued.peek(), Some(&"dois")); //! ``` //! //! ## `HashTrieMap` //! [![`HashTrieMap` documentation](https://img.shields.io/badge/doc-HashTrieMap-303070.svg)](crate::map::hash_trie_map::HashTrieMap) //! //! A map implemented with a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie). //! See [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf) for //! details. //! //! ### Example //! //! ```rust //! use rpds::HashTrieMap; //! //! let map_en = HashTrieMap::new() //! .insert(0, "zero") //! .insert(1, "one"); //! //! assert_eq!(map_en.get(&1), Some(&"one")); //! //! let map_pt = map_en //! .insert(1, "um") //! .insert(2, "dois"); //! //! assert_eq!(map_pt.get(&2), Some(&"dois")); //! //! let map_pt_binary = map_pt.remove(&2); //! //! assert_eq!(map_pt_binary.get(&2), None); //! ``` //! //! ## `HashTrieSet` //! [![`HashTrieSet` documentation](https://img.shields.io/badge/doc-HashTrieSet-303070.svg)](crate::set::hash_trie_set::HashTrieSet) //! //! A set implemented with a [`HashTrieMap`](#hashtriemap). //! //! ### Example //! //! ```rust //! use rpds::HashTrieSet; //! //! let set = HashTrieSet::new() //! .insert("zero") //! .insert("one"); //! //! assert!(set.contains(&"one")); //! //! let set_extended = set.insert("two"); //! //! assert!(set_extended.contains(&"two")); //! //! let set_positive = set_extended.remove(&"zero"); //! //! assert!(!set_positive.contains(&"zero")); //! ``` //! //! ## `RedBlackTreeMap` //! [![`RedBlackTreeMap` documentation](https://img.shields.io/badge/doc-RedBlackTreeMap-303070.svg)](crate::map::red_black_tree_map::RedBlackTreeMap) //! //! A map implemented with a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree). //! //! ### Example //! //! ```rust //! use rpds::RedBlackTreeMap; //! //! let map_en = RedBlackTreeMap::new() //! .insert(0, "zero") //! .insert(1, "one"); //! //! assert_eq!(map_en.get(&1), Some(&"one")); //! //! let map_pt = map_en //! .insert(1, "um") //! .insert(2, "dois"); //! //! assert_eq!(map_pt.get(&2), Some(&"dois")); //! //! let map_pt_binary = map_pt.remove(&2); //! //! assert_eq!(map_pt_binary.get(&2), None); //! //! assert_eq!(map_pt_binary.first(), Some((&0, &"zero"))); //! ``` //! //! ## `RedBlackTreeSet` //! [![`RedBlackTreeSet` documentation](https://img.shields.io/badge/doc-RedBlackTreeSet-303070.svg)](crate::set::red_black_tree_set::RedBlackTreeSet) //! //! A set implemented with a [`RedBlackTreeMap`](#redblacktreemap). //! //! ### Example //! //! ```rust //! use rpds::RedBlackTreeSet; //! //! let set = RedBlackTreeSet::new() //! .insert("zero") //! .insert("one"); //! //! assert!(set.contains(&"one")); //! //! let set_extended = set.insert("two"); //! //! assert!(set_extended.contains(&"two")); //! //! let set_positive = set_extended.remove(&"zero"); //! //! assert!(!set_positive.contains(&"zero")); //! //! assert_eq!(set_positive.first(), Some(&"one")); //! ``` //! //! # Other features //! //! ## Mutable methods //! //! When you change a data structure you often do not need its previous versions. For those cases //! rpds offers you mutable methods which are generally faster: //! //! ```rust //! use rpds::HashTrieSet; //! //! let mut set = HashTrieSet::new(); //! //! set.insert_mut("zero"); //! set.insert_mut("one"); //! //! let set_0_1 = set.clone(); //! let set_0_1_2 = set.insert("two"); //! ``` //! //! ## Initialization macros //! //! There are convenient initialization macros for all data structures: //! //! ```rust //! use rpds::*; //! //! let vector = vector![3, 1, 4, 1, 5]; //! let map = ht_map!["orange" => "orange", "banana" => "yellow"]; //! ``` //! //! Check the documentation for initialization macros of other data structures. //! //! ## Thread safety //! //! All data structures in this crate can be shared between threads, but that is an opt-in ability. //! This is because there is a performance cost to make data structures thread safe. That cost //! is worth avoiding when you are not actually sharing them between threads. //! //! Of course if you try to share a rpds data structure across different threads you can count on //! the rust compiler to ensure that it is safe to do so. If you are using the version of the data //! structure that is not thread safe you will get a compile-time error. //! //! To create a thread-safe version of any data structure use `new_sync()`: //! //! ```rust //! # use rpds::Vector; //! # //! let vec = Vector::new_sync() //! .push_back(42); //! ``` //! //! Or use the `_sync` variant of the initialization macro: //! //! ```rust //! # use rpds::vector_sync; //! # //! let vec = vector_sync!(42); //! ``` //! //! ### Further details //! //! Internally the data structures in this crate maintain a lot of reference-counting pointers. //! These pointers are used both for links between the internal nodes of the data structure as well //! as for the values it stores. //! //! There are two implementations of reference-counting pointers in the standard library: //! [`Rc`](::alloc::rc::Rc) and //! [`Arc`](::alloc::sync::Arc). They behave the same way, but //! `Arc` allows you to share the data it points to across multiple threads. The downside is that //! it is significantly slower to clone and drop than `Rc`, and persistent data structures do a //! lot of those operations. In some microbenchmarks with rpds data structure we can see that //! using `Rc` instead of `Arc` can make some operations twice as fast! You can see this for //! yourself by running `cargo bench`. //! //! To implement this we parameterize the type of reference-counting pointer (`Rc` or `Arc`) as a //! type argument of the data structure. We use the [archery](https://github.com/orium/archery/) //! crate to do this in a convenient way. //! //! The pointer type can be parameterized like this: //! //! ```rust //! # use rpds::Vector; //! # //! let vec: Vector = Vector::new_with_ptr_kind(); //! // ↖ //! // This will use `Arc` pointers. //! // Change it to `archery::RcK` to use a `Rc` pointer. //! ``` //! //! ## `no_std` support //! //! This crate supports `no_std`. To enable that you need to disable the default feature `std`: //! //! ```toml //! [dependencies] //! rpds = { version = "", default-features = false } //! ``` //! //! ## Serialization //! //! We support serialization through [serde](https://crates.io/crates/serde). To use it //! enable the `serde` feature. To do so change the rpds dependency in your `Cargo.toml` to //! //! ```toml //! [dependencies] //! rpds = { version = "", features = ["serde"] } //! ``` //! //! ## Bindings //! //! Bindings to use rpds from other programming languages exist. Below is a short list of those //! known to date. //! //! * [rpds.py](https://github.com/crate-py/rpds/) – Python //! //! Please feel free to send a pull request should you add support in a new language. extern crate alloc; #[cfg(test)] #[macro_use] extern crate std; mod utils; #[macro_use] pub mod list; pub mod map; pub mod queue; pub mod set; pub mod stack; pub mod vector; pub use crate::list::List; pub use crate::list::ListSync; pub use crate::map::hash_trie_map::HashTrieMap; pub use crate::map::hash_trie_map::HashTrieMapSync; pub use crate::map::red_black_tree_map::RedBlackTreeMap; pub use crate::map::red_black_tree_map::RedBlackTreeMapSync; pub use crate::queue::Queue; pub use crate::queue::QueueSync; pub use crate::set::hash_trie_set::HashTrieSet; pub use crate::set::hash_trie_set::HashTrieSetSync; pub use crate::set::red_black_tree_set::RedBlackTreeSet; pub use crate::set::red_black_tree_set::RedBlackTreeSetSync; pub use crate::stack::Stack; pub use crate::stack::StackSync; pub use crate::vector::Vector; pub use crate::vector::VectorSync; rpds-1.1.0/src/list/mod.rs000064400000000000000000000321431046102023000134650ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use alloc::vec::Vec; use archery::*; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt::Display; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; // TODO Use impl trait instead of this when available. pub type Iter<'a, T, P> = core::iter::Map, fn(&SharedPointer) -> &T>; #[doc(hidden)] #[macro_export] macro_rules! list_reverse { ($ptr_kind:ty ; ; $($reversed:expr),*) => { { #[allow(unused_mut)] let mut l: List<_, $ptr_kind> = $crate::List::new_with_ptr_kind(); $( l.push_front_mut($reversed); )* l } }; ($ptr_kind:ty ; $h:expr ; $($reversed:expr),*) => { $crate::list_reverse!($ptr_kind ; ; $h, $($reversed),*) }; ($ptr_kind:ty ; $h:expr, $($t:expr),+ ; $($reversed:expr),*) => { $crate::list_reverse!($ptr_kind ; $($t),* ; $h, $($reversed),*) }; // This is just to handle the cases where this macro is called with an extra comma in the // reserve list, which can happen in a recursive call. ($ptr_kind:ty ; $($t:expr),* ; $($reversed:expr),*,) => { $crate::list_reverse!($ptr_kind ; $($t),* ; $($reversed),*) }; } /// Creates a [`List`](crate::List) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let l = List::new() /// .push_front(3) /// .push_front(2) /// .push_front(1); /// /// assert_eq!(list![1, 2, 3], l); /// ``` #[macro_export] macro_rules! list { ($($e:expr),*) => { $crate::list_reverse!(::archery::RcK ; $($e),* ; ) }; } /// Creates a [`List`](crate::List) that implements `Sync`, containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let l = List::new_sync() /// .push_front(3) /// .push_front(2) /// .push_front(1); /// /// assert_eq!(list_sync![1, 2, 3], l); /// /// fn is_sync() -> impl Sync { /// list_sync![0, 1, 1, 2, 3, 5, 8] /// } /// ``` #[macro_export] macro_rules! list_sync { ($($e:expr),*) => { $crate::list_reverse!(::archery::ArcTK ; $($e),* ; ) }; } /// A persistent list with structural sharing. /// /// # Complexity /// /// Let *n* be the number of elements in the list. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:----------------- | -------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `push_front()` | Θ(1) | Θ(1) | /// | `drop_first()` | Θ(1) | Θ(1) | /// | `reverse()` | Θ(n) | Θ(n) | /// | `first()` | Θ(1) | Θ(1) | /// | `last()` | Θ(1) | Θ(1) | /// | `len()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(1) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This is your classic functional list with "cons" and "nil" nodes, with a little extra sauce to /// make some operations more efficient. #[derive(Debug)] pub struct List where P: SharedPointerKind, { head: Option, P>>, last: Option>, length: usize, } #[derive(Debug)] struct Node where P: SharedPointerKind, { value: SharedPointer, next: Option, P>>, } impl Clone for Node where P: SharedPointerKind, { fn clone(&self) -> Node { Node { value: SharedPointer::clone(&self.value), next: self.next.clone() } } } pub type ListSync = List; impl ListSync { #[must_use] pub fn new_sync() -> ListSync { List::new_with_ptr_kind() } } impl List { #[must_use] pub fn new() -> List { List::new_with_ptr_kind() } } impl List where P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> List { List { head: None, last: None, length: 0 } } #[must_use] pub fn first(&self) -> Option<&T> { self.head.as_ref().map(|node| node.value.borrow()) } #[must_use] pub fn last(&self) -> Option<&T> { self.last.as_ref().map(Borrow::borrow) } #[must_use] pub fn drop_first(&self) -> Option> { let mut new_list = self.clone(); if new_list.drop_first_mut() { Some(new_list) } else { None } } pub fn drop_first_mut(&mut self) -> bool { self.head.take().map_or(false, |h| { self.head = h.next.clone(); self.length -= 1; if self.length == 0 { self.last = None; } true }) } fn push_front_ptr_mut(&mut self, v: SharedPointer) { if self.length == 0 { self.last = Some(SharedPointer::clone(&v)); } let new_head = Node { value: v, next: self.head.take() }; self.head = Some(SharedPointer::new(new_head)); self.length += 1; } #[must_use] pub fn push_front(&self, v: T) -> List { let mut new_list = self.clone(); new_list.push_front_mut(v); new_list } pub fn push_front_mut(&mut self, v: T) { self.push_front_ptr_mut(SharedPointer::new(v)); } #[must_use] pub fn reverse(&self) -> List { let mut new_list = List::new_with_ptr_kind(); // It is significantly faster to re-implement this here than to clone and call // `reverse_mut()`. The reason is that since this is a linear data structure all nodes will // need to be cloned given that the ref count would be greater than one. for v in self.iter_ptr() { new_list.push_front_ptr_mut(SharedPointer::clone(v)); } new_list } pub fn reverse_mut(&mut self) { self.last = self.head.as_ref().map(|next| SharedPointer::clone(&next.value)); let mut prev: Option, P>> = None; let mut current: Option, P>> = self.head.take(); while let Some(mut curr_ptr) = current { let curr = SharedPointer::make_mut(&mut curr_ptr); let curr_next = curr.next.take(); curr.next = prev.take(); current = curr_next; prev = Some(curr_ptr); } self.head = prev; } #[must_use] #[inline] pub fn len(&self) -> usize { self.length } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn iter(&self) -> Iter<'_, T, P> { self.iter_ptr().map(|v| v.borrow()) } #[must_use] pub(crate) fn iter_ptr(&self) -> IterPtr<'_, T, P> { IterPtr::new(self) } } impl List where T: Clone, P: SharedPointerKind, { #[must_use] pub(crate) fn first_mut(&mut self) -> Option<&mut T> { self.head .as_mut() .map(|node| SharedPointer::make_mut(&mut SharedPointer::make_mut(node).value)) } } impl Default for List where P: SharedPointerKind, { fn default() -> List { List::new_with_ptr_kind() } } impl PartialEq> for List where P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &List) -> bool { self.length == other.length && self.iter().eq(other.iter()) } } impl Eq for List where P: SharedPointerKind {} impl, P, PO> PartialOrd> for List where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &List) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for List where P: SharedPointerKind, { fn cmp(&self, other: &List) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for List where P: SharedPointerKind, { fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it doesn't // hash to the same thing as a single collection with the same elements in the same order. self.len().hash(state); for e in self { e.hash(state); } } } impl Clone for List where P: SharedPointerKind, { fn clone(&self) -> List { List { head: self.head.clone(), last: self.last.clone(), length: self.length } } } impl Display for List where P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("[")?; for v in self { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str("]") } } impl<'a, T, P> IntoIterator for &'a List where P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } impl FromIterator for List where P: SharedPointerKind, { fn from_iter>(into_iter: I) -> List { let iter = into_iter.into_iter(); let (min_size, max_size_hint) = iter.size_hint(); let mut vec: Vec = Vec::with_capacity(max_size_hint.unwrap_or(min_size)); for e in iter { vec.push(e); } let mut list: List = List::new_with_ptr_kind(); for e in vec.into_iter().rev() { list.push_front_mut(e); } list } } // Drop the list iteratively to prevent stack overflow. impl Drop for List where P: SharedPointerKind, { fn drop(&mut self) { let mut head = self.head.take(); while let Some(node) = head { if let Ok(mut node) = SharedPointer::try_unwrap(node) { head = node.next.take(); } else { break; } } } } #[derive(Debug)] pub struct IterPtr<'a, T, P> where P: SharedPointerKind, { next: Option<&'a Node>, length: usize, } impl<'a, T, P> IterPtr<'a, T, P> where P: SharedPointerKind, { fn new(list: &List) -> IterPtr<'_, T, P> { IterPtr { next: list.head.as_ref().map(AsRef::as_ref), length: list.len() } } } impl<'a, T, P> Iterator for IterPtr<'a, T, P> where P: SharedPointerKind, { type Item = &'a SharedPointer; fn next(&mut self) -> Option<&'a SharedPointer> { match self.next { Some(Node { value: ref v, next: ref t }) => { self.next = t.as_ref().map(AsRef::as_ref); self.length -= 1; Some(v) } None => None, } } fn size_hint(&self) -> (usize, Option) { (self.length, Some(self.length)) } } impl<'a, T, P> ExactSizeIterator for IterPtr<'a, T, P> where P: SharedPointerKind {} #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for List where T: Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } impl<'de, T, P> Deserialize<'de> for List where T: Deserialize<'de>, P: SharedPointerKind, { fn deserialize>(deserializer: D) -> Result, D::Error> { deserializer .deserialize_seq(ListVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData }) } } struct ListVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData

, } impl<'de, T, P> Visitor<'de> for ListVisitor where T: Deserialize<'de>, P: SharedPointerKind, { type Value = List; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { let mut vec: Vec = if let Some(capacity) = seq.size_hint() { Vec::with_capacity(capacity) } else { Vec::new() }; while let Some(value) = seq.next_element()? { vec.push(value); } let mut list: List = List::new_with_ptr_kind(); for value in vec.into_iter().rev() { list.push_front_mut(value); } Ok(list) } } } #[cfg(test)] mod test; rpds-1.1.0/src/list/test.rs000064400000000000000000000240141046102023000136630ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(ListSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_list_sync_is_send_and_sync() -> impl Send + Sync { list_sync!(0) } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter() { let limit = 1024; let mut list = List::new(); let mut left = limit; for i in 0..limit { list.push_front_mut(i); } for v in list.iter() { left -= 1; assert_eq!(*v, left); } assert_eq!(left, 0); } #[test] fn test_iter_size_hint() { let list = list![0, 1, 2]; let mut iterator = list.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_into_iterator() { let list = list![0, 1, 2, 3]; let mut left = 4; for (expected, n) in list.into_iter().enumerate() { left -= 1; assert!(left >= 0); assert_eq!(*n, expected); } assert_eq!(left, 0); } } #[test] fn test_new() { let empty_list: List = List::new(); assert!(empty_list.head.is_none()); assert_eq!(empty_list.len(), 0); assert!(empty_list.is_empty()); } #[test] fn test_macro_list() { let mut list_1 = List::new(); list_1.push_front_mut(1); let mut list_1_2_3 = List::new(); list_1_2_3.push_front_mut(3); list_1_2_3.push_front_mut(2); list_1_2_3.push_front_mut(1); assert_eq!(List::::new(), list![]); assert_eq!(list_1, list![1]); assert_eq!(list_1_2_3, list![1, 2, 3]); } #[test] fn test_first() { let empty_list: List = List::new(); let singleton_list = list!["hello"]; let list = list![0, 1, 2, 3]; assert_eq!(empty_list.first(), None); assert_eq!(singleton_list.first(), Some(&"hello")); assert_eq!(list.first(), Some(&0)); } #[test] fn test_first_mut() { let a = list![0, 1, 2, 3]; let mut b = a.clone(); *b.first_mut().unwrap() = -1; assert_eq!(a.first(), Some(&0)); assert_eq!(b.first(), Some(&-1)); assert!(a.iter().eq([0, 1, 2, 3].iter())); assert!(b.iter().eq([-1, 1, 2, 3].iter())); } #[test] fn test_last() { let empty_list: List = List::new(); let mut singleton_list = list!["hello"]; let list = list![0, 1, 2, 3]; assert_eq!(empty_list.last(), None); assert_eq!(singleton_list.last(), Some(&"hello")); assert_eq!(list.last(), Some(&3)); singleton_list.drop_first_mut(); assert_eq!(singleton_list.last(), None); } #[test] fn test_drop_first() { let empty_list: List = List::new(); let singleton_list = List::new().push_front("hello"); let list = List::new().push_front(3).push_front(2).push_front(1).push_front(0); assert!(empty_list.is_empty()); assert_eq!(singleton_list.drop_first().unwrap().first(), None); assert_eq!(list.drop_first().unwrap().first(), Some(&1)); assert_eq!(list.len(), 4); assert_eq!(list.drop_first().unwrap().len(), 3); } #[test] fn test_drop_first_mut() { let mut empty_list: List = List::new(); let mut singleton_list = list!["hello"]; let mut list = list![0, 1, 2, 3]; empty_list.drop_first_mut(); assert!(empty_list.is_empty()); singleton_list.drop_first_mut(); assert_eq!(singleton_list.first(), None); list.drop_first_mut(); assert_eq!(list.first(), Some(&1)); assert_eq!(list.len(), 3); } #[test] fn test_reverse() { let empty_list: List = List::new(); let singleton_list = List::new().push_front("hello"); let list = List::new().push_front(3).push_front(2).push_front(1).push_front(0); let list_reversed = List::new().push_front(0).push_front(1).push_front(2).push_front(3); assert_eq!(empty_list.reverse(), empty_list); assert_eq!(empty_list.reverse().last(), None); assert_eq!(singleton_list.reverse(), singleton_list); assert_eq!(singleton_list.reverse().last(), Some(&"hello")); assert_eq!(list.reverse(), list_reversed); assert_eq!(list.reverse().last(), Some(&0)); } #[test] fn test_reverse_mut() { let mut empty_list: List = List::new(); let mut singleton_list = list!["hello"]; let mut list = list![0, 1, 2, 3]; list.reverse_mut(); singleton_list.reverse_mut(); empty_list.reverse_mut(); assert_eq!(empty_list, empty_list); assert_eq!(empty_list.last(), None); assert_eq!(singleton_list, singleton_list); assert_eq!(singleton_list.last(), Some(&"hello")); assert_eq!(list.last(), Some(&0)); } #[test] fn test_from_iterator() { let vec: Vec = vec![10, 11, 12, 13]; let list: List = vec.iter().copied().collect(); assert!(vec.iter().eq(list.iter())); } #[test] fn test_default() { let list: List = List::default(); assert_eq!(list.first(), None); assert_eq!(list.len(), 0); } #[test] fn test_display() { let empty_list: List = List::new(); let singleton_list = list!["hello"]; let list = list![0, 1, 2, 3]; assert_eq!(format!("{}", empty_list), "[]"); assert_eq!(format!("{}", singleton_list), "[hello]"); assert_eq!(format!("{}", list), "[0, 1, 2, 3]"); } #[test] fn test_eq() { let list_1 = list!["a", "a"]; let list_1_prime = list!["a", "a"]; let list_2 = list!["a", "b"]; assert_ne!(list_1, list_2); assert_eq!(list_1, list_1); assert_eq!(list_1, list_1_prime); assert_eq!(list_2, list_2); } #[test] fn test_eq_pointer_kind_consistent() { let list_a = list!["a"]; let list_a_sync = list_sync!["a"]; let list_b = list!["b"]; let list_b_sync = list_sync!["b"]; assert!(list_a == list_a_sync); assert!(list_a != list_b_sync); assert!(list_b == list_b_sync); } #[test] fn test_partial_ord() { let list_1 = list!["a"]; let list_1_prime = list!["a"]; let list_2 = list!["b"]; let list_3 = list![0.0]; let list_4 = list![core::f32::NAN]; assert_eq!(list_1.partial_cmp(&list_1_prime), Some(Ordering::Equal)); assert_eq!(list_1.partial_cmp(&list_2), Some(Ordering::Less)); assert_eq!(list_2.partial_cmp(&list_1), Some(Ordering::Greater)); assert_eq!(list_3.partial_cmp(&list_4), None); } #[test] fn test_ord() { let list_1 = list!["a"]; let list_1_prime = list!["a"]; let list_2 = list!["b"]; assert_eq!(list_1.cmp(&list_1_prime), Ordering::Equal); assert_eq!(list_1.cmp(&list_2), Ordering::Less); assert_eq!(list_2.cmp(&list_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let list_a = list!["a"]; let list_a_sync = list_sync!["a"]; let list_b = list!["b"]; let list_b_sync = list_sync!["b"]; assert!(list_a <= list_a_sync); assert!(list_a < list_b_sync); assert!(list_b >= list_b_sync); assert!(list_a_sync >= list_a); assert!(list_b_sync > list_a); assert!(list_b_sync <= list_b); } fn hash(list: &List) -> u64 { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); list.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let list_1 = list!["a"]; let list_1_prime = list!["a"]; let list_2 = list!["a", "b"]; assert_eq!(hash(&list_1), hash(&list_1)); assert_eq!(hash(&list_1), hash(&list_1_prime)); assert_ne!(hash(&list_1), hash(&list_2)); } #[test] fn test_hash_pointer_kind_consistent() { let list = list!["a"]; let list_sync = list_sync!["a"]; assert_eq!(hash(&list), hash(&list_sync)); } #[test] fn test_clone() { let list = list!["hello", "there"]; let clone = list.clone(); assert!(clone.iter().eq(list.iter())); assert_eq!(clone.len(), list.len()); assert_eq!(clone.last(), list.last()); } #[allow(clippy::many_single_char_names)] #[test] fn test_drop_list() { // When it is dropped, it will set the variable it owned to false. use core::cell::Cell; struct DropStruct<'a>(&'a Cell); impl<'a> Drop for DropStruct<'a> { fn drop(&mut self) { self.0.set(false); } } // test that we actually dropped the elements let (a, b, c, d, e) = (Cell::new(true), Cell::new(true), Cell::new(true), Cell::new(true), Cell::new(true)); let mut x = List::new(); x.push_front_mut(DropStruct(&a)); x.push_front_mut(DropStruct(&b)); x.push_front_mut(DropStruct(&c)); assert_eq!( vec![a.get(), b.get(), c.get(), d.get(), e.get()], vec![true, true, true, true, true] ); let x2 = x.drop_first().unwrap().drop_first().unwrap(); drop(x); assert_eq!( vec![a.get(), b.get(), c.get(), d.get(), e.get()], vec![true, false, false, true, true] ); let y = x2.push_front(DropStruct(&d)); drop(x2); assert_eq!( vec![a.get(), b.get(), c.get(), d.get(), e.get()], vec![true, false, false, true, true] ); let z = y.push_front(DropStruct(&e)); drop(y); assert_eq!( vec![a.get(), b.get(), c.get(), d.get(), e.get()], vec![true, false, false, true, true] ); drop(z); assert_eq!( vec![a.get(), b.get(), c.get(), d.get(), e.get()], vec![false, false, false, false, false] ); } #[test] fn test_drop_large() { let limit = 1024 * 1024; let mut list = List::new(); for i in 0..limit { list.push_front_mut(i); } } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let list: List = list![5, 6, 7, 8]; let encoded = serialize(&list).unwrap(); let decoded: List = deserialize(&encoded).unwrap(); assert_eq!(list, decoded); } rpds-1.1.0/src/map/entry.rs000064400000000000000000000006641046102023000136540ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #[derive(Debug, PartialEq, Eq, Clone)] pub struct Entry { pub key: K, pub value: V, } impl Entry { #[must_use] pub fn new(key: K, value: V) -> Entry { Entry { key, value } } } rpds-1.1.0/src/map/hash_trie_map/mod.rs000064400000000000000000001120001046102023000160610ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ mod sparse_array_usize; use super::entry::Entry; use crate::list; use crate::utils::DefaultBuildHasher; use crate::List; use alloc::vec::Vec; use archery::{ArcTK, RcK, SharedPointer, SharedPointerKind}; use core::borrow::Borrow; use core::fmt::Display; use core::hash::BuildHasher; use core::hash::Hash; use core::iter; use core::iter::FromIterator; use core::ops::Index; use core::slice; use sparse_array_usize::SparseArrayUsize; type HashValue = u64; // TODO Use impl trait instead of this when available. pub type Iter<'a, K, V, P> = iter::Map, fn(&'a SharedPointer, P>) -> (&'a K, &'a V)>; pub type IterKeys<'a, K, V, P> = iter::Map, fn((&'a K, &V)) -> &'a K>; pub type IterValues<'a, K, V, P> = iter::Map, fn((&K, &'a V)) -> &'a V>; #[allow(clippy::cast_possible_truncation)] const DEFAULT_DEGREE: u8 = usize::BITS as u8; /// Creates a [`HashTrieMap`](crate::HashTrieMap) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let m = HashTrieMap::new() /// .insert(1, "one") /// .insert(2, "two") /// .insert(3, "three"); /// /// assert_eq!(ht_map![1 => "one", 2 => "two", 3 => "three"], m); /// ``` #[macro_export] macro_rules! ht_map { ($($k:expr => $v:expr),*) => { { #[allow(unused_mut)] let mut m = $crate::HashTrieMap::new(); $( m.insert_mut($k, $v); )* m } }; } /// Creates a [`HashTrieMap`](crate::HashTrieMap) that implements `Sync`, containing the given /// arguments: /// /// ``` /// # use rpds::*; /// # /// let m = HashTrieMap::new_sync() /// .insert(1, "one") /// .insert(2, "two") /// .insert(3, "three"); /// /// assert_eq!(ht_map_sync![1 => "one", 2 => "two", 3 => "three"], m); /// ``` #[macro_export] macro_rules! ht_map_sync { ($($k:expr => $v:expr),*) => { { #[allow(unused_mut)] let mut m = $crate::HashTrieMap::new_sync(); $( m.insert_mut($k, $v); )* m } }; } /// A persistent map with structural sharing. This implementation uses a /// [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie). /// /// # Complexity /// /// Let *n* be the number of elements in the map. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:-------------------------- | ---------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `insert()` | Θ(1) | Θ(n) | /// | `remove()` | Θ(1) | Θ(n) | /// | `get()` | Θ(1) | Θ(n) | /// | `contains_key()` | Θ(1) | Θ(n) | /// | `size()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(1) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This implementation uses a /// [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie). /// Details can be found in /// [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf). /// /// See the `Node` documentation for details. #[derive(Debug)] pub struct HashTrieMap where P: SharedPointerKind, { root: SharedPointer, P>, size: usize, degree: u8, hasher_builder: H, } pub type HashTrieMapSync = HashTrieMap; /// This map works like a trie that breaks the hash of the key in segments, and the segments are /// used as the index in the trie branches. /// /// Consider the following example, where we have a tree with degree 16 (e.g. each level uses 4 /// bits of the hash) and the following mapping between keys and their hashes: /// /// | *key* | *hash(key)* | /// | ------- | ---------------------------------:| /// | *A* | `0b_0000_0000_···_0000_0010_0110` | /// | *B* | `0b_0000_0000_···_0000_0001_0110` | /// | *C* | `0b_0000_0000_···_0000_0100_0010` | /// | *D* | `0b_0111_0000_···_0000_0000_1000` | /// | *E* | `0b_0111_0000_···_0000_0000_1000` | /// /// Then the tree will look like this: /// /// ```text /// 0 ··· 2 ··· 6 ··· 8 ··· /// ├───┼───┼───┼───┼───┼───┼───┼───┤ /// │ ∅ │ ∅ │ C │ ∅ │ • │ ∅ │ • │ ∅ │ depth 0 /// └───┴───┴───┴───┴─│─┴───┴─│─┴───┘ /// ╱ ╲ /// ╱ ╲ /// ╱ ╲ /// 0 1 2 ··· 0 1 2 ··· /// ├───┼───┼───┼───┤ ├───┼───┼───┼───┤ /// │ ∅ │ B │ A │ ∅ │ │ • │ ∅ │ ∅ │ ∅ │ depth 1 /// └───┴───┴───┴───┘ └─│─┴───┴───┴───┘ /// │ /// · /// · /// · /// │ /// 0 ··· 7 ··· /// ├───┼───┼─────┼───┤ /// │ ∅ │ ∅ │ D E │ ∅ │ depth 16 (maximum depth) /// └───┴───┴─────┴───┘ /// ``` /// /// Note that we stop the insertion process early when possible. In the example above we did not /// had to expand the tree any further to accommodate *C*, since there is no other entry with a /// hash that starts with `0b0010`. The entries *A* and *B* exemplifies the case where a single /// level is not enough because their hash both start with `0b0110`. In case of a full hash /// collision we dig through all the levels of the tree so we get to the final leaf where a /// collision exists, like we can see in the case of *D* and *E*. /// /// # Invariants /// /// The tree has the following invariants (among others): /// /// 1. The root is the only node that can have zero children. /// 2. A node with a collision can only exist at the maximum depth of the tree. /// 3. A non-root branch always have two or more entries under it (because it could be /// compressed). #[derive(Debug)] enum Node where P: SharedPointerKind, { Branch(SparseArrayUsize, P>>), Leaf(Bucket), } #[derive(Debug)] enum Bucket where P: SharedPointerKind, { Single(EntryWithHash), Collision(List, P>), } #[derive(Debug)] struct EntryWithHash where P: SharedPointerKind, { entry: SharedPointer, P>, key_hash: HashValue, } mod node_utils { use super::HashValue; use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; use core::mem::size_of_val; /// Returns the index of the array for the given hash on depth `depth`. /// /// When the hash is exhausted, meaning that we are at the maximum depth, this returns `None`. #[inline] pub fn index_from_hash(hash: HashValue, depth: usize, degree: u8) -> Option { debug_assert!(degree.is_power_of_two()); #[allow(clippy::cast_possible_truncation)] let shift = depth as u32 * degree.trailing_zeros(); #[allow(clippy::cast_lossless)] if (shift as usize) < 8 * size_of_val(&hash) { let mask = degree as HashValue - 1; #[allow(clippy::cast_possible_truncation)] Some(((hash >> shift) & mask) as usize) } else { None } } pub fn hash(v: &T, hasher_builder: &H) -> HashValue { let mut hasher = hasher_builder.build_hasher(); v.hash(&mut hasher); hasher.finish() } } impl Node where K: Eq + Hash, P: SharedPointerKind, { fn new_empty_branch() -> Node { Node::Branch(SparseArrayUsize::new()) } fn get( &self, key: &Q, key_hash: HashValue, depth: usize, degree: u8, ) -> Option<&EntryWithHash> where K: Borrow, Q: Hash + Eq, { match self { Node::Branch(subtrees) => { let index: usize = node_utils::index_from_hash(key_hash, depth, degree) .expect("hash cannot be exhausted if we are on a branch"); subtrees .get(index) .and_then(|subtree| subtree.get(key, key_hash, depth + 1, degree)) } Node::Leaf(bucket) => bucket.get(key, key_hash), } } fn get_mut( &mut self, key: &Q, key_hash: HashValue, depth: usize, degree: u8, ) -> Option<&mut EntryWithHash> where K: Borrow, Q: Hash + Eq, { match self { Node::Branch(subtrees) => { let index: usize = node_utils::index_from_hash(key_hash, depth, degree) .expect("hash cannot be exhausted if we are on a branch"); subtrees.get_mut(index).and_then(|subtree| { SharedPointer::make_mut(subtree).get_mut(key, key_hash, depth + 1, degree) }) } Node::Leaf(bucket) => bucket.get_mut(key, key_hash), } } /// Returns a pair with the node with the new entry and whether the key is new. fn insert(&mut self, entry: EntryWithHash, depth: usize, degree: u8) -> bool { match self { Node::Branch(subtrees) => { let index: usize = node_utils::index_from_hash(entry.key_hash, depth, degree) .expect("hash cannot be exhausted if we are on a branch"); match subtrees.get_mut(index) { Some(subtree) => { SharedPointer::make_mut(subtree).insert(entry, depth + 1, degree) } None => { let new_subtree = Node::Leaf(Bucket::Single(entry)); subtrees.set(index, SharedPointer::new(new_subtree)); true } } } Node::Leaf(bucket) => { // If we are at maximum depth then the hash was totally consumed and we have a // collision. let maximum_depth = node_utils::index_from_hash(entry.key_hash, depth, degree).is_none(); let bucket_contains_key: bool = bucket.contains_key(entry.key(), entry.key_hash); match maximum_depth { // We reached a bucket. If the bucket contains the key we are inserting then // we just need to replace it. false if bucket_contains_key => bucket.insert(entry), // We reached a bucket and the key we will insert is not there. We need to // create a `Node::Branch` and insert the elements of the bucket there, as well // as the new element. false => { // TODO This clone should not be needed. let old_entry: EntryWithHash = match bucket { Bucket::Single(e) => e.clone(), Bucket::Collision(_) => unreachable!( "hash is not exhausted, so there cannot be a collision here" ), }; *self = Node::new_empty_branch(); self.insert(old_entry, depth, degree); self.insert(entry, depth, degree); true } // Hash was already totally consumed. This is a collision. true => bucket.insert(entry), } } } } /// Compresses a node. This makes the shallowest tree that is well-formed, i.e. branches with /// a single entry become a leaf with it. fn compress(&mut self) { let new_node = match self { Node::Branch(subtrees) => { match subtrees.size() { 1 => { let compress: bool = { let subtree = subtrees.first().unwrap(); // Keep collision at the bottom of the tree. matches!(subtree.borrow(), Node::Leaf(Bucket::Single(_))) }; match compress { true => subtrees.pop(), false => None, } } _ => None, } } Node::Leaf(_) => None, }; if let Some(node) = new_node { crate::utils::replace(self, node); } } /// Returns `true` if the key was present. fn remove(&mut self, key: &Q, key_hash: HashValue, depth: usize, degree: u8) -> bool where K: Borrow, Q: Hash + Eq, { match self { Node::Branch(subtrees) => { let index: usize = node_utils::index_from_hash(key_hash, depth, degree) .expect("hash cannot be exhausted if we are on a branch"); match subtrees.get_mut(index) { Some(subtree) => { let subtree = SharedPointer::make_mut(subtree); let removed = subtree.remove(key, key_hash, depth + 1, degree); match (subtree.is_empty(), removed) { (_, false) => (), (false, true) => { // Note that we still must call compress because it is possible that // we had a node with just one entry, which was not compressed // because it had a collision. Maybe now we do not have a collision // and we can compress it. self.compress(); } (true, true) => { subtrees.remove(index); self.compress(); } }; removed } None => false, } } Node::Leaf(bucket) => { let mut bucket_ref = Some(bucket); let removed = Bucket::remove(&mut bucket_ref, key, key_hash); if bucket_ref.is_none() { // TODO Most of these empty branches will be dropped very soon. We might // gain some speed if we avoid this. (However, currently no heap // allocation happens anyway.) // We can do something similar to Bucket::remove() where we receive // a `&mut Option<&mut Bucket<_, _>>`. *self = Node::new_empty_branch(); } removed } } } fn is_empty(&self) -> bool { match self { Node::Branch(subtrees) => subtrees.size() == 0, Node::Leaf(Bucket::Single(_)) => false, Node::Leaf(Bucket::Collision(entries)) => { debug_assert!(entries.len() >= 2, "collisions must have at least two entries"); false } } } } impl Clone for Node where K: Eq + Hash, P: SharedPointerKind, { fn clone(&self) -> Node { match self { Node::Branch(subtrees) => Node::Branch(subtrees.clone()), Node::Leaf(bucket) => Node::Leaf(bucket.clone()), } } } mod bucket_utils { use super::*; pub fn list_remove_first bool>( list: &mut List, predicate: F, ) -> Option { let mut before_needle: Vec = Vec::with_capacity(list.len()); let remaining: &mut List = list; let mut removed = None; while !remaining.is_empty() { let e: T = remaining.first().unwrap().clone(); remaining.drop_first_mut(); if predicate(&e) { removed = Some(e); break; } before_needle.push(e); } let new_entries = remaining; while let Some(e) = before_needle.pop() { new_entries.push_front_mut(e); } removed } } impl Bucket where K: Eq + Hash, P: SharedPointerKind, { fn get(&self, key: &Q, key_hash: HashValue) -> Option<&EntryWithHash> where K: Borrow, Q: Hash + Eq, { match self { Bucket::Single(entry) if entry.matches(key, key_hash) => Some(entry), Bucket::Single(_) => None, Bucket::Collision(entries) => entries.iter().find(|e| e.matches(key, key_hash)), } } fn get_mut( &mut self, key: &Q, key_hash: HashValue, ) -> Option<&mut EntryWithHash> where K: Borrow, Q: Hash + Eq, { match self { Bucket::Single(entry) if entry.matches(key, key_hash) => Some(entry), Bucket::Single(_) => None, Bucket::Collision(entries) => { let removed = bucket_utils::list_remove_first(entries, |e| e.matches(key, key_hash)); removed.and_then(|e| { entries.push_front_mut(e); entries.first_mut() }) } } } #[inline] fn contains_key(&self, key: &Q, key_hash: HashValue) -> bool where K: Borrow, Q: Hash + Eq, { self.get(key, key_hash).is_some() } /// Returns `true` if the key is new. /// /// If there is a collision then `entry` will be put on the front of the entries list to /// improve performance with high temporal locality (since `get()` will try to match according /// to the list order). The order of the rest of the list must be preserved for the same /// reason. fn insert(&mut self, entry: EntryWithHash) -> bool { match self { Bucket::Single(existing_entry) if existing_entry.matches(entry.key(), entry.key_hash) => { *existing_entry = entry; false } Bucket::Single(existing_entry) => { let mut entries = List::new_with_ptr_kind(); // TODO In theory we should not need to clone `existing_entry`. entries.push_front_mut(existing_entry.clone()); entries.push_front_mut(entry); *self = Bucket::Collision(entries); true } Bucket::Collision(entries) => { let key_existed = bucket_utils::list_remove_first(entries, |e| { e.matches(entry.key(), entry.key_hash) }) .is_some(); entries.push_front_mut(entry); !key_existed } } } /// Returns `true` if the key was present. /// /// If the bucket becomes empty `bucket` it be set to `None`. fn remove( bucket: &mut Option<&mut Bucket>, key: &Q, key_hash: HashValue, ) -> bool where K: Borrow, Q: Hash + Eq, { match bucket.take() { Some(b) => { match b { Bucket::Single(existing_entry) if existing_entry.matches(key, key_hash) => { // bucket is already `None`. true } Bucket::Single(_) => { // Nothing to change. *bucket = Some(b); false } Bucket::Collision(entries) => { let removed = bucket_utils::list_remove_first(entries, |e| e.matches(key, key_hash)) .is_some(); match entries.len() { 0 => unreachable!( "impossible to have collision with a single or no entry" ), 1 => { let entry = entries.first().unwrap().clone(); *b = Bucket::Single(entry); } _ => (), }; *bucket = Some(b); removed } } } None => false, } } } impl Clone for Bucket where K: Eq + Hash, P: SharedPointerKind, { fn clone(&self) -> Bucket { match self { Bucket::Single(entry) => Bucket::Single(EntryWithHash::clone(entry)), Bucket::Collision(entries) => Bucket::Collision(List::clone(entries)), } } } impl EntryWithHash where K: Eq + Hash, P: SharedPointerKind, { fn new(key: K, value: V, hash_builder: &H) -> EntryWithHash { let key_hash = node_utils::hash(&key, hash_builder); EntryWithHash { entry: SharedPointer::new(Entry::new(key, value)), key_hash } } fn key(&self) -> &K { &self.entry.key } fn value(&self) -> &V { &self.entry.value } #[inline] fn matches(&self, key: &Q, key_hash: HashValue) -> bool where K: Borrow, Q: Hash + Eq, { self.key_hash == key_hash && self.key().borrow() == key } } impl EntryWithHash where K: Eq + Hash + Clone, V: Clone, P: SharedPointerKind, { fn value_mut(&mut self) -> &mut V { &mut SharedPointer::make_mut(&mut self.entry).value } } impl Clone for EntryWithHash where K: Eq + Hash, P: SharedPointerKind, { fn clone(&self) -> EntryWithHash { EntryWithHash { entry: SharedPointer::clone(&self.entry), key_hash: self.key_hash } } } impl HashTrieMap where K: Eq + Hash, { #[must_use] pub fn new() -> HashTrieMap { HashTrieMap::new_with_degree(DEFAULT_DEGREE) } #[must_use] pub fn new_with_degree(degree: u8) -> HashTrieMap { HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(DefaultBuildHasher::default(), degree) } } impl HashTrieMapSync where K: Eq + Hash, { #[must_use] pub fn new_sync() -> HashTrieMapSync { HashTrieMap::new_sync_with_degree(DEFAULT_DEGREE) } #[must_use] pub fn new_sync_with_degree(degree: u8) -> HashTrieMapSync { HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(DefaultBuildHasher::default(), degree) } } impl HashTrieMap where K: Eq + Hash, H: Clone, P: SharedPointerKind, { #[must_use] pub fn new_with_hasher_and_ptr_kind(hasher_builder: H) -> HashTrieMap { HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher_builder, DEFAULT_DEGREE) } #[must_use] pub fn new_with_hasher_and_degree_and_ptr_kind( hasher_builder: H, degree: u8, ) -> HashTrieMap { assert!(degree.is_power_of_two(), "degree must be a power of two"); assert!(degree <= DEFAULT_DEGREE, "degree is too big"); HashTrieMap { root: SharedPointer::new(Node::new_empty_branch()), size: 0, degree, hasher_builder, } } #[must_use] pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow, Q: Hash + Eq, { let key_hash = node_utils::hash(key, &self.hasher_builder); self.root.get(key, key_hash, 0, self.degree).map(EntryWithHash::value) } #[must_use] pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow, Q: Hash + Eq, { let key_hash = node_utils::hash(key, &self.hasher_builder); self.root.get(key, key_hash, 0, self.degree).map(|e| (e.key(), e.value())) } #[must_use] pub fn insert(&self, key: K, value: V) -> HashTrieMap { let mut new_map = self.clone(); new_map.insert_mut(key, value); new_map } pub fn insert_mut(&mut self, key: K, value: V) { let entry = EntryWithHash::new(key, value, &self.hasher_builder); let is_new_key = SharedPointer::make_mut(&mut self.root).insert(entry, 0, self.degree); if is_new_key { self.size += 1; } } #[must_use] pub fn remove(&self, key: &Q) -> HashTrieMap where K: Borrow, Q: Hash + Eq, { let mut new_map = self.clone(); if new_map.remove_mut(key) { new_map } else { // We want to keep maximum sharing so in case of no change we just `clone()` ourselves. self.clone() } } pub fn remove_mut(&mut self, key: &Q) -> bool where K: Borrow, Q: Hash + Eq, { let key_hash = node_utils::hash(key, &self.hasher_builder); let removed = SharedPointer::make_mut(&mut self.root).remove(key, key_hash, 0, self.degree); // Note that unfortunately, even if nothing was removed, we still might have cloned some // part of the tree unnecessarily. if removed { self.size -= 1; } removed } #[must_use] pub fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: Hash + Eq, { self.get(key).is_some() } /// Test whether the two maps refer to the same content in memory. /// /// This would return true if you’re comparing a map to itself, /// or if you’re comparing a map to a fresh clone of itself. pub(crate) fn ptr_eq( &self, other: &HashTrieMap, ) -> bool { let a = SharedPointer::as_ptr(&self.root); // Note how we're casting the raw pointer changing from P to PO // We cannot perform the equality in a type safe way because the root type depends // on P/PO, and we can't pass different types to SharedPtr::same_ptr or std::ptr::eq. let b = SharedPointer::as_ptr(&other.root).cast::>(); core::ptr::eq(a, b) } #[must_use] #[inline] pub fn size(&self) -> usize { self.size } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.size() == 0 } pub fn iter(&self) -> Iter<'_, K, V, P> { self.iter_ptr().map(|e| (&e.key, &e.value)) } #[must_use] fn iter_ptr(&self) -> IterPtr<'_, K, V, P> { IterPtr::new(self) } pub fn keys(&self) -> IterKeys<'_, K, V, P> { self.iter().map(|(k, _)| k) } pub fn values(&self) -> IterValues<'_, K, V, P> { self.iter().map(|(_, v)| v) } } impl HashTrieMap where K: Eq + Hash + Clone, V: Clone, H: Clone, P: SharedPointerKind, { pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow, Q: Hash + Eq, { // Note that unfortunately, even if nothing is found, we still might have cloned some // part of the tree unnecessarily. let key_hash = node_utils::hash(key, &self.hasher_builder); SharedPointer::make_mut(&mut self.root) .get_mut(key, key_hash, 0, self.degree) .map(EntryWithHash::value_mut) } } impl<'a, K, Q: ?Sized, V, P, H: BuildHasher> Index<&'a Q> for HashTrieMap where K: Eq + Hash + Borrow, Q: Hash + Eq, H: Clone, P: SharedPointerKind, { type Output = V; fn index(&self, key: &Q) -> &V { self.get(key).expect("no entry found for key") } } impl Clone for HashTrieMap where K: Eq + Hash, H: Clone, P: SharedPointerKind, { fn clone(&self) -> HashTrieMap { HashTrieMap { root: SharedPointer::clone(&self.root), size: self.size, degree: self.degree, hasher_builder: self.hasher_builder.clone(), } } } impl Default for HashTrieMap where K: Eq + Hash, H: Default + Clone, P: SharedPointerKind, { fn default() -> HashTrieMap { HashTrieMap::new_with_hasher_and_ptr_kind(H::default()) } } impl PartialEq> for HashTrieMap where K: Hash, H: Clone, P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &HashTrieMap) -> bool { if self.ptr_eq(other) { return true; } self.size() == other.size() && self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) } } impl Eq for HashTrieMap where K: Hash, H: Clone, P: SharedPointerKind, { } impl Display for HashTrieMap where K: Eq + Hash + Display, V: Display, H: Clone, P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("{")?; for (k, v) in self.iter() { if !first { fmt.write_str(", ")?; } k.fmt(fmt)?; fmt.write_str(": ")?; v.fmt(fmt)?; first = false; } fmt.write_str("}") } } impl<'a, K, V, P, H: BuildHasher> IntoIterator for &'a HashTrieMap where K: Eq + Hash, H: Default + Clone, P: SharedPointerKind, { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V, P>; fn into_iter(self) -> Iter<'a, K, V, P> { self.iter() } } impl FromIterator<(K, V)> for HashTrieMap where K: Eq + Hash, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn from_iter>(into_iter: I) -> HashTrieMap { let mut map = HashTrieMap::new_with_hasher_and_ptr_kind(Default::default()); for (k, v) in into_iter { map.insert_mut(k, v); } map } } #[derive(Debug)] pub struct IterPtr<'a, K, V, P> where P: SharedPointerKind, { stack: Vec>, size: usize, } #[derive(Debug)] enum IterStackElement<'a, K, V, P> where P: SharedPointerKind, { Branch(slice::Iter<'a, SharedPointer, P>>), LeafCollision(list::Iter<'a, EntryWithHash, P>), LeafSingle(iter::Once<&'a SharedPointer, P>>), } impl<'a, K, V, P> IterStackElement<'a, K, V, P> where K: Eq + Hash, P: SharedPointerKind, { #[inline] fn new(node: &Node) -> IterStackElement<'_, K, V, P> { match node { Node::Branch(children) => IterStackElement::Branch(children.iter()), Node::Leaf(Bucket::Collision(entries)) => { IterStackElement::LeafCollision(entries.iter()) } Node::Leaf(Bucket::Single(entry)) => { IterStackElement::LeafSingle(iter::once(&entry.entry)) } } } /// Returns the next `Entry` _or_ the next `IterStackElement` to be pushed into the stack. /// If the result is None the `IterStackElement` should be popped from the stack. #[inline] fn next(&mut self) -> Option, P>, Self>> { match self { IterStackElement::Branch(i) => i.next().map(|node| match &**node { Node::Branch(_) | Node::Leaf(Bucket::Collision(_)) => { Err(IterStackElement::new(node)) } Node::Leaf(Bucket::Single(e)) => Ok(&e.entry), }), IterStackElement::LeafCollision(i) => i.next().map(|i| Ok(&i.entry)), IterStackElement::LeafSingle(i) => i.next().map(Ok), } } } mod iter_utils { use super::HashValue; pub fn trie_max_height(degree: u8) -> usize { let bits_per_level = (degree - 1).count_ones() as usize; let hash_bits = HashValue::BITS as usize; (hash_bits / bits_per_level) + usize::from(hash_bits % bits_per_level > 0) } } impl<'a, K, V, P> IterPtr<'a, K, V, P> where K: Eq + Hash, P: SharedPointerKind, { fn new(map: &HashTrieMap) -> IterPtr<'_, K, V, P> { let mut stack: Vec> = Vec::with_capacity(iter_utils::trie_max_height(map.degree) + 1); if map.size() > 0 { stack.push(IterStackElement::new(map.root.borrow())); } IterPtr { stack, size: map.size() } } } impl<'a, K, V, P> Iterator for IterPtr<'a, K, V, P> where K: Eq + Hash, P: SharedPointerKind, { type Item = &'a SharedPointer, P>; fn next(&mut self) -> Option<&'a SharedPointer, P>> { while let Some(stack_element) = self.stack.last_mut() { match stack_element.next() { Some(Ok(elem)) => { self.size -= 1; return Some(elem); } Some(Err(stack_elem)) => { self.stack.push(stack_elem); } None => { self.stack.pop(); } } } None } #[inline] fn size_hint(&self) -> (usize, Option) { (self.size, Some(self.size)) } } impl<'a, K: Eq + Hash, V, P> ExactSizeIterator for IterPtr<'a, K, V, P> where P: SharedPointerKind {} #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for HashTrieMap where K: Eq + Hash + Serialize, V: Serialize, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_map(self) } } impl<'de, K, V, P, H> Deserialize<'de> for HashTrieMap where K: Eq + Hash + Deserialize<'de>, V: Deserialize<'de>, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn deserialize>( deserializer: D, ) -> Result, D::Error> { deserializer.deserialize_map(HashTrieMapVisitor { _phantom_entry: PhantomData, _phantom_h: PhantomData, _phantom_p: PhantomData, }) } } struct HashTrieMapVisitor where P: SharedPointerKind, { _phantom_entry: PhantomData<(K, V)>, _phantom_h: PhantomData, _phantom_p: PhantomData

, } impl<'de, K, V, P, H> Visitor<'de> for HashTrieMapVisitor where K: Eq + Hash + Deserialize<'de>, V: Deserialize<'de>, H: BuildHasher + Clone + Default, P: SharedPointerKind, { type Value = HashTrieMap; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } fn visit_map(self, mut map: A) -> Result, A::Error> where A: MapAccess<'de>, { let mut hash_trie_map = HashTrieMap::new_with_hasher_and_ptr_kind(Default::default()); while let Some((k, v)) = map.next_entry()? { hash_trie_map.insert_mut(k, v); } Ok(hash_trie_map) } } } #[cfg(test)] mod test; rpds-1.1.0/src/map/hash_trie_map/sparse_array_usize/mod.rs000064400000000000000000000052721046102023000220070ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use alloc::vec::Vec; use core::mem::size_of_val; use core::slice; /// Sparse array of size `8⋅size_of::()`. The space used is proportional to the number of /// elements set. #[derive(Debug, PartialEq, Eq)] pub struct SparseArrayUsize { bitmap: usize, array: Vec, } mod sparse_array_usize_utils { #[inline] pub fn map_index(bitmap: usize, virtual_index: usize) -> Option { if bitmap & (1_usize << virtual_index) == 0 { None } else { let mask = (1_usize << virtual_index) - 1; Some((bitmap & mask).count_ones() as usize) } } } impl SparseArrayUsize { pub fn new() -> SparseArrayUsize { SparseArrayUsize { bitmap: 0, array: Vec::new() } } #[inline] pub fn get(&self, index: usize) -> Option<&T> { debug_assert!(index < 8 * size_of_val(&self.bitmap)); sparse_array_usize_utils::map_index(self.bitmap, index).map(|i| &self.array[i]) } #[inline] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { debug_assert!(index < 8 * size_of_val(&self.bitmap)); sparse_array_usize_utils::map_index(self.bitmap, index).map(move |i| &mut self.array[i]) } #[inline] pub fn first(&self) -> Option<&T> { self.array.first() } #[inline] pub fn pop(&mut self) -> Option { self.array.pop() } pub fn set(&mut self, index: usize, value: T) { debug_assert!(index < 8 * size_of_val(&self.bitmap)); match sparse_array_usize_utils::map_index(self.bitmap, index) { Some(i) => self.array[i] = value, None => { let new_bitmap = self.bitmap | (1 << index); let i = sparse_array_usize_utils::map_index(new_bitmap, index).unwrap(); self.bitmap = new_bitmap; self.array.insert(i, value); } } } pub fn remove(&mut self, index: usize) { if let Some(i) = sparse_array_usize_utils::map_index(self.bitmap, index) { self.bitmap ^= 1 << index; self.array.remove(i); } } #[inline] pub fn size(&self) -> usize { self.bitmap.count_ones() as usize } pub fn iter(&self) -> slice::Iter<'_, T> { self.array.iter() } } impl Clone for SparseArrayUsize { fn clone(&self) -> SparseArrayUsize { SparseArrayUsize { bitmap: self.bitmap, array: Vec::clone(&self.array) } } } #[cfg(test)] mod test; rpds-1.1.0/src/map/hash_trie_map/sparse_array_usize/test.rs000064400000000000000000000072101046102023000222010ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use pretty_assertions::assert_eq; const USIZE_BITS: usize = usize::BITS as usize; #[test] fn test_new() { let empty_array: SparseArrayUsize = SparseArrayUsize::new(); assert_eq!(empty_array.bitmap, 0); assert_eq!(empty_array.array.len(), 0); assert_eq!(empty_array.array.capacity(), 0, "Capacity of the branch array is wasteful"); } #[test] fn test_set() { let mut array = SparseArrayUsize::new(); assert_eq!(array.size(), 0); assert_eq!(array.get(0), None); assert_eq!(array.get(USIZE_BITS - 1), None); array.set(3, 'a'); assert_eq!(array.size(), 1); assert_eq!(array.get(2), None); assert_eq!(array.get(3), Some(&'a')); assert_eq!(array.get(4), None); array.set(USIZE_BITS - 4, 'b'); assert_eq!(array.size(), 2); assert_eq!(array.get(3), Some(&'a')); assert_eq!(array.get(USIZE_BITS - 4), Some(&'b')); array.set(3, 'c'); assert_eq!(array.size(), 2); assert_eq!(array.get(3), Some(&'c')); assert_eq!(array.get(USIZE_BITS - 4), Some(&'b')); } #[test] fn test_remove() { let mut array = SparseArrayUsize::new(); array.set(3, 'a'); array.set(USIZE_BITS - 4, 'b'); assert_eq!(array.get(3), Some(&'a')); assert_eq!(array.get(USIZE_BITS - 4), Some(&'b')); array.remove(8); assert_eq!(array.get(3), Some(&'a')); assert_eq!(array.get(USIZE_BITS - 4), Some(&'b')); assert_eq!(array.size(), 2); array.remove(3); assert_eq!(array.get(3), None); assert_eq!(array.get(USIZE_BITS - 4), Some(&'b')); assert_eq!(array.size(), 1); array.remove(USIZE_BITS - 4); assert_eq!(array.get(3), None); assert_eq!(array.get(USIZE_BITS - 4), None); assert_eq!(array.size(), 0); } #[test] fn test_first() { let mut array = SparseArrayUsize::new(); assert_eq!(array.first(), None); array.set(8, 'a'); assert_eq!(array.first(), Some(&'a')); array.set(USIZE_BITS - 4, 'b'); assert_eq!(array.first(), Some(&'a')); array.set(2, 'c'); assert_eq!(array.first(), Some(&'c')); array.set(0, 'c'); assert_eq!(array.first(), Some(&'c')); } #[test] fn test_pop() { let mut array = SparseArrayUsize::new(); array.set(USIZE_BITS - 4, 'b'); array.set(8, 'a'); assert_eq!(array.pop(), Some('b')); assert_eq!(array.pop(), Some('a')); assert_eq!(array.pop(), None); } #[test] fn test_map_index() { for i in 0..(usize::BITS as usize) { assert_eq!(sparse_array_usize_utils::map_index(0, i), None); } let bitmap: usize = 0b_1110_0100_0101; assert_eq!(sparse_array_usize_utils::map_index(bitmap, 0), Some(0)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 1), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 2), Some(1)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 3), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 4), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 5), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 6), Some(2)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 7), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 8), None); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 9), Some(3)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 10), Some(4)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 11), Some(5)); assert_eq!(sparse_array_usize_utils::map_index(bitmap, 12), None); } rpds-1.1.0/src/map/hash_trie_map/test.rs000064400000000000000000001163071046102023000162770ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(HashTrieMapSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_hash_trie_map_sync_is_send_and_sync() -> impl Send + Sync { ht_map_sync!(0 => 0) } impl PartialEq for Bucket where P: SharedPointerKind, { fn eq(&self, other: &Bucket) -> bool { match (self, other) { (Bucket::Single(self_entry), Bucket::Single(other_entry)) => self_entry.eq(other_entry), (Bucket::Collision(self_entry_list), Bucket::Collision(other_entry_list)) => { self_entry_list.eq(other_entry_list) } _ => false, } } } impl Eq for Bucket where P: SharedPointerKind {} impl PartialEq for EntryWithHash where P: SharedPointerKind, { fn eq(&self, other: &EntryWithHash) -> bool { self.entry.eq(&other.entry) } } impl Eq for EntryWithHash where P: SharedPointerKind {} impl PartialEq for Node where P: SharedPointerKind, { fn eq(&self, other: &Node) -> bool { match (self, other) { (Node::Branch(self_children), Node::Branch(other_children)) => { self_children.eq(other_children) } (Node::Leaf(self_bucket), Node::Leaf(other_bucket)) => self_bucket.eq(other_bucket), _ => false, } } } impl Eq for Node where P: SharedPointerKind {} mod bucket { use super::*; use pretty_assertions::assert_eq; #[test] fn test_list_remove_first() { use bucket_utils::list_remove_first; let list_a_b_c = list!['a', 'b', 'c']; let list_b_c = list!['b', 'c']; let list_a_c = list!['a', 'c']; let list_a_b = list!['a', 'b']; let mut list = list_a_b_c.clone(); assert!(list_remove_first(&mut list, |_| false).is_none()); assert_eq!(list, list_a_b_c); let mut list = list_a_b_c.clone(); assert!(list_remove_first(&mut list, |c| *c == 'a').is_some()); assert_eq!(list, list_b_c); let mut list = list_a_b_c.clone(); assert!(list_remove_first(&mut list, |c| *c == 'b').is_some()); assert_eq!(list, list_a_c); let mut list = list_a_b_c; assert!(list_remove_first(&mut list, |c| *c == 'c').is_some()); assert_eq!(list, list_a_b); } #[test] fn test_get() { let hash_builder = crate::utils::DefaultBuildHasher::default(); let entry_a: EntryWithHash<_, _> = EntryWithHash::new(0xAu8, 0, &hash_builder); let entry_b: EntryWithHash<_, _> = EntryWithHash::new(0xBu8, 1, &hash_builder); let entry_c: EntryWithHash<_, _> = EntryWithHash::new(0xCu8, 2, &hash_builder); let bucket_single = Bucket::Single(entry_a.clone()); let bucket_collision = Bucket::Collision(list![entry_b.clone(), entry_a.clone()]); assert_eq!(bucket_single.get(entry_a.key(), entry_a.key_hash), Some(&entry_a)); assert_eq!(bucket_single.get(entry_b.key(), entry_b.key_hash), None); assert_eq!(bucket_collision.get(entry_a.key(), entry_a.key_hash), Some(&entry_a)); assert_eq!(bucket_collision.get(entry_b.key(), entry_b.key_hash), Some(&entry_b)); assert_eq!(bucket_collision.get(entry_c.key(), entry_c.key_hash), None); } #[test] fn test_insert() { let hash_builder = crate::utils::DefaultBuildHasher::default(); let entry_a: EntryWithHash<_, _> = EntryWithHash::new(0xAu8, 0, &hash_builder); let entry_a9: EntryWithHash<_, _> = EntryWithHash::new(0xAu8, 9, &hash_builder); let entry_b: EntryWithHash<_, _> = EntryWithHash::new(0xBu8, 1, &hash_builder); let entry_b9: EntryWithHash<_, _> = EntryWithHash::new(0xBu8, 9, &hash_builder); let entry_c: EntryWithHash<_, _> = EntryWithHash::new(0xCu8, 2, &hash_builder); let entry_d: EntryWithHash<_, _> = EntryWithHash::new(0xDu8, 2, &hash_builder); let bucket_single_a = Bucket::Single(entry_a.clone()); let bucket_single_a9 = Bucket::Single(entry_a9.clone()); let bucket_collision_b_a = Bucket::Collision(list![entry_b.clone(), entry_a.clone()]); let bucket_collision_a_b_c = Bucket::Collision(list![entry_a.clone(), entry_b.clone(), entry_c.clone()]); let bucket_collision_b9_a_c = Bucket::Collision(list![entry_b9.clone(), entry_a.clone(), entry_c.clone()]); let bucket_collision_d_a_b_c = Bucket::Collision(list![entry_d.clone(), entry_a, entry_b.clone(), entry_c]); // Note that we care about the position of the inserted entry: we want it to be in the // beginning of the list as to improve performance with high temporal locality (since // `get()` will try to match according to the list order). The order of the rest of the // list must be preserved for the same reason. let mut bucket = bucket_single_a.clone(); assert!(!bucket.insert(entry_a9)); assert_eq!(bucket, bucket_single_a9); let mut bucket = bucket_single_a; assert!(bucket.insert(entry_b)); assert_eq!(bucket, bucket_collision_b_a); let mut bucket = bucket_collision_a_b_c.clone(); assert!(!bucket.insert(entry_b9)); assert_eq!(bucket, bucket_collision_b9_a_c); let mut bucket = bucket_collision_a_b_c; assert!(bucket.insert(entry_d)); assert_eq!(bucket, bucket_collision_d_a_b_c); } #[test] fn test_remove() { let hash_builder = crate::utils::DefaultBuildHasher::default(); let entry_a: EntryWithHash = EntryWithHash::new(0xAu8, 0, &hash_builder); let entry_b: EntryWithHash = EntryWithHash::new(0xBu8, 1, &hash_builder); let entry_c: EntryWithHash = EntryWithHash::new(0xCu8, 2, &hash_builder); let entry_d: EntryWithHash = EntryWithHash::new(0xDu8, 2, &hash_builder); let bucket_single_a = Bucket::Single(entry_a.clone()); let bucket_collision_b_c = Bucket::Collision(list![entry_b.clone(), entry_c.clone()]); let bucket_collision_a_b_c = Bucket::Collision(list![entry_a.clone(), entry_b.clone(), entry_c]); let mut bucket_ref: Option<&mut Bucket> = None; assert!(!Bucket::remove(&mut bucket_ref, entry_a.key(), entry_a.key_hash)); assert_eq!(bucket_ref, None); let mut bucket = bucket_single_a.clone(); let mut bucket_ref = Some(&mut bucket); assert!(Bucket::remove(&mut bucket_ref, entry_a.key(), entry_a.key_hash)); assert_eq!(bucket_ref, None); let mut bucket = bucket_single_a.clone(); let mut bucket_ref = Some(&mut bucket); assert!(!Bucket::remove(&mut bucket_ref, entry_b.key(), entry_b.key_hash)); assert_eq!(bucket_ref, Some(&mut bucket_single_a.clone())); let mut bucket = bucket_collision_a_b_c.clone(); let mut bucket_ref = Some(&mut bucket); assert!(Bucket::remove(&mut bucket_ref, entry_a.key(), entry_a.key_hash)); assert_eq!(bucket_ref, Some(&mut bucket_collision_b_c.clone())); let mut bucket = bucket_collision_a_b_c.clone(); let mut bucket_ref = Some(&mut bucket); assert!(!Bucket::remove(&mut bucket_ref, entry_d.key(), entry_d.key_hash)); assert_eq!(bucket_ref, Some(&mut bucket_collision_a_b_c.clone())); } } mod hasher_mocks { use super::*; use alloc::boxed::Box; use core::hash::Hasher; use std::collections::BTreeMap; pub struct MockedHashBuilder { byte_map: BTreeMap, } pub struct MockedHasher { last_byte: Option, byte_map: BTreeMap, } impl MockedHashBuilder { pub fn new(byte_map: BTreeMap) -> MockedHashBuilder { MockedHashBuilder { byte_map } } } impl Clone for MockedHashBuilder { fn clone(&self) -> MockedHashBuilder { MockedHashBuilder::new(self.byte_map.clone()) } } impl BuildHasher for MockedHashBuilder { type Hasher = MockedHasher; fn build_hasher(&self) -> MockedHasher { MockedHasher { last_byte: None, byte_map: self.byte_map.clone() } } } impl Hasher for MockedHasher { fn finish(&self) -> HashValue { *self.byte_map.get(self.last_byte.as_ref().unwrap()).unwrap() } fn write(&mut self, bytes: &[u8]) { self.last_byte = self.last_byte.or_else(|| bytes.last().copied()); } } pub struct LimitedHashSpaceHashBuilder { inner_hash_builder: crate::utils::DefaultBuildHasher, hash_space_size: usize, } pub struct LimitedHashSpaceHasher { inner_hasher: Box, hash_space_size: usize, } impl LimitedHashSpaceHashBuilder { pub fn new(hash_space_size: usize) -> LimitedHashSpaceHashBuilder { LimitedHashSpaceHashBuilder { inner_hash_builder: crate::utils::DefaultBuildHasher::default(), hash_space_size, } } } impl Clone for LimitedHashSpaceHashBuilder { fn clone(&self) -> LimitedHashSpaceHashBuilder { LimitedHashSpaceHashBuilder { inner_hash_builder: self.inner_hash_builder.clone(), hash_space_size: self.hash_space_size, } } } impl BuildHasher for LimitedHashSpaceHashBuilder { type Hasher = LimitedHashSpaceHasher; fn build_hasher(&self) -> LimitedHashSpaceHasher { LimitedHashSpaceHasher { inner_hasher: Box::new(self.inner_hash_builder.build_hasher()), hash_space_size: self.hash_space_size, } } } impl Hasher for LimitedHashSpaceHasher { fn finish(&self) -> HashValue { self.inner_hasher.finish() % (self.hash_space_size as HashValue) } fn write(&mut self, bytes: &[u8]) { self.inner_hasher.write(bytes); } } } mod node { use super::*; use hasher_mocks::*; use pretty_assertions::assert_eq; use std::collections::BTreeMap; #[test] fn test_new_empty_branch() { let node: Node = Node::new_empty_branch(); match node { Node::Branch(array) => assert_eq!(array.size(), 0), Node::Leaf(_) => panic!("Invalid node type"), } } #[allow(clippy::unusual_byte_groupings)] #[allow(clippy::unreadable_literal)] #[test] fn test_index_from_hash() { let hash: HashValue = 0b_000100_100011_000010_100001 | (1 << 63); assert_eq!(node_utils::index_from_hash(hash, 0, 64), Some(0b100001)); assert_eq!(node_utils::index_from_hash(hash, 1, 64), Some(0b000010)); assert_eq!(node_utils::index_from_hash(hash, 2, 64), Some(0b100011)); assert_eq!(node_utils::index_from_hash(hash, 3, 64), Some(0b000100)); assert_eq!(node_utils::index_from_hash(hash, 10, 64), Some(0b001000)); assert_eq!(node_utils::index_from_hash(hash, 11, 64), None); assert_eq!(node_utils::index_from_hash(hash, 15, 16), Some(0b1000)); assert_eq!(node_utils::index_from_hash(hash, 16, 16), None); } fn dummy_hash_builder() -> MockedHashBuilder { let hash_mapping: BTreeMap = [ (0xA, 0b_0010_0110), (0xB, 0b_0001_0110), (0xC, 0b_0100_0010), (0xD, 0b_0000_1000 | (0b0111 << 60)), (0xE, 0b_0000_1000 | (0b0111 << 60)), (0x0, 0b_0000_1000 | (0b0111 << 60)), (0x1, 0b_0000_0110 | (0b0101 << 60)), (0x2, 0b_0000_1111 | (0b0111 << 60)), ] .iter() .copied() .collect(); MockedHashBuilder::new(hash_mapping) } /// This constructs the following tree: /// /// ```text /// 0 ··· 2 ··· 6 ··· 8 ··· /// ├───┼───┼───┼───┼───┼───┼───┼───┤ /// │ ∅ │ ∅ │ C │ ∅ │ • │ ∅ │ • │ ∅ │ depth 0 /// └───┴───┴───┴───┴─│─┴───┴─│─┴───┘ /// ╱ ╲ /// ╱ ╲ /// ╱ ╲ /// 0 1 2 ··· 0 1 2 ··· /// ├───┼───┼───┼───┤ ├───┼───┼───┼───┤ /// │ ∅ │ B │ A │ ∅ │ │ • │ ∅ │ ∅ │ ∅ │ depth 1 /// └───┴───┴───┴───┘ └─│─┴───┴───┴───┘ /// │ /// · /// · /// · /// │ /// 0 ··· 7 ··· /// ├───┼───┼─────┼───┤ /// │ ∅ │ ∅ │ D E │ ∅ │ depth 16 (maximum depth) /// └───┴───┴─────┴───┘ /// ``` fn dummy_hash_trie_map() -> HashTrieMap { let hash_builder: MockedHashBuilder = dummy_hash_builder(); let entry_a = EntryWithHash::new(0xAu8, 0, &hash_builder); let entry_b = EntryWithHash::new(0xBu8, 1, &hash_builder); let entry_c = EntryWithHash::new(0xCu8, 2, &hash_builder); let entry_d = EntryWithHash::new(0xDu8, 3, &hash_builder); let entry_e = EntryWithHash::new(0xEu8, 4, &hash_builder); let bucket_a = Bucket::Single(entry_a); let bucket_b = Bucket::Single(entry_b); let bucket_c = Bucket::Single(entry_c); let bucket_de = Bucket::Collision(list![entry_e, entry_d]); let node_depth_1_first = { let mut array = SparseArrayUsize::new(); array.set(1, SharedPointer::new(Node::Leaf(bucket_b))); array.set(2, SharedPointer::new(Node::Leaf(bucket_a))); Node::Branch(array) }; let node_maximum_depth = { let mut array = SparseArrayUsize::new(); array.set(7, SharedPointer::new(Node::Leaf(bucket_de))); Node::Branch(array) }; let maximum_depth_branch = { let mut branch = node_maximum_depth; for _ in 0..14 { let mut array = SparseArrayUsize::new(); array.set(0, SharedPointer::new(branch)); branch = Node::Branch(array); } branch }; let node_root = { let mut array = SparseArrayUsize::new(); array.set(2, SharedPointer::new(Node::Leaf(bucket_c))); array.set(6, SharedPointer::new(node_depth_1_first)); array.set(8, SharedPointer::new(maximum_depth_branch)); Node::Branch(array) }; HashTrieMap { root: SharedPointer::new(node_root), size: 5, degree: 16, hasher_builder: hash_builder, } } #[test] fn test_get() { let map = dummy_hash_trie_map(); assert_eq!(map.get(&0xA), Some(&0)); assert_eq!(map.get(&0xB), Some(&1)); assert_eq!(map.get(&0xC), Some(&2)); assert_eq!(map.get(&0xD), Some(&3)); assert_eq!(map.get(&0xE), Some(&4)); assert_eq!(map.get(&0x0), None); assert_eq!(map.get(&0x1), None); assert_eq!(map.get(&0x2), None); } #[test] fn test_get_mut() { let original = dummy_hash_trie_map(); let mut map = original.clone(); *map.get_mut(&0xB).unwrap() = -1; *map.get_mut(&0xE).unwrap() = -1; assert!(map.get_mut(&0x1).is_none()); assert!(map.get_mut(&0x2).is_none()); assert_eq!(original.get(&0xA), Some(&0)); assert_eq!(original.get(&0xB), Some(&1)); assert_eq!(original.get(&0xC), Some(&2)); assert_eq!(original.get(&0xD), Some(&3)); assert_eq!(original.get(&0xE), Some(&4)); assert_eq!(original.get(&0x0), None); assert_eq!(original.get(&0x1), None); assert_eq!(original.get(&0x2), None); assert_eq!(map.get(&0xA), Some(&0)); assert_eq!(map.get(&0xB), Some(&-1)); assert_eq!(map.get(&0xC), Some(&2)); assert_eq!(map.get(&0xD), Some(&3)); assert_eq!(map.get(&0xE), Some(&-1)); assert_eq!(map.get(&0x0), None); assert_eq!(map.get(&0x1), None); assert_eq!(map.get(&0x2), None); } #[test] fn test_contains_key() { let map = dummy_hash_trie_map(); assert!(map.contains_key(&0xA)); assert!(map.contains_key(&0xE)); assert!(!map.contains_key(&0x0)); } #[test] fn test_insert() { let mut map: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16); assert_eq!(map.size(), 0); map = map.insert(0xA, 10); assert_eq!(map.size(), 1); map = map.insert(0xB, 1); assert_eq!(map.size(), 2); map = map.insert(0xC, 2); assert_eq!(map.size(), 3); map = map.insert(0xD, 3); assert_eq!(map.size(), 4); map = map.insert(0xE, 4); assert_eq!(map.size(), 5); map = map.insert(0xA, 0); assert_eq!(map.size(), 5); assert_eq!(map.get(&0xA), Some(&0)); assert_eq!(map.get(&0xB), Some(&1)); assert_eq!(map.get(&0xC), Some(&2)); assert_eq!(map.get(&0xD), Some(&3)); assert_eq!(map.get(&0xE), Some(&4)); assert_eq!(map.get(&0x0), None); assert_eq!(map.get(&0x1), None); assert_eq!(map.get(&0x2), None); assert_eq!(map.root, dummy_hash_trie_map().root); } #[test] fn test_compress() { let hash_builder: MockedHashBuilder = dummy_hash_builder(); let entry_a: EntryWithHash<_, _> = EntryWithHash::new(0xAu8, 0, &hash_builder); let entry_b: EntryWithHash<_, _> = EntryWithHash::new(0xBu8, 1, &hash_builder); let bucket_a = Bucket::Single(entry_a.clone()); let bucket_b = Bucket::Single(entry_b.clone()); let bucket_a_b = Bucket::Collision(list![entry_a, entry_b]); let empty_branch = Node::::new_empty_branch(); let branch_with_collision = { let mut array = SparseArrayUsize::new(); array.set(4, SharedPointer::new(Node::Leaf(bucket_a_b.clone()))); SharedPointer::<_, RcK>::new(Node::Branch(array)) }; let branch_with_two_subtrees = { let mut array = SparseArrayUsize::new(); array.set(4, SharedPointer::new(Node::Leaf(bucket_a.clone()))); array.set(7, SharedPointer::new(Node::Leaf(bucket_b.clone()))); SharedPointer::<_, RcK>::new(Node::Branch(array)) }; let branch_with_single_bucket = { let mut array = SparseArrayUsize::new(); array.set(4, SharedPointer::new(Node::Leaf(bucket_a.clone()))); Node::Branch(array) }; let branch_with_branch = { let mut array = SparseArrayUsize::new(); array.set(4, SharedPointer::new(Node::::new_empty_branch())); SharedPointer::<_, RcK>::new(Node::Branch(array)) }; let leaf_with_single_bucket_a = SharedPointer::<_, RcK>::new(Node::Leaf(bucket_a.clone())); let leaf_with_collision_bucket_a_b = SharedPointer::<_, RcK>::new(Node::Leaf(bucket_a_b.clone())); let mut node = Node::clone(&empty_branch); node.compress(); assert_eq!(node, empty_branch); let mut node = Node::clone(&branch_with_collision); node.compress(); assert_eq!(node, *branch_with_collision.borrow()); let mut node = Node::clone(&branch_with_two_subtrees); node.compress(); assert_eq!(node, *branch_with_two_subtrees.borrow()); let mut node = Node::clone(&branch_with_single_bucket); node.compress(); assert_eq!(node, *leaf_with_single_bucket_a.borrow()); let mut node = Node::clone(&branch_with_branch); node.compress(); assert_eq!(node, *branch_with_branch.borrow()); let mut node = Node::clone(&leaf_with_single_bucket_a); node.compress(); assert_eq!(node, *leaf_with_single_bucket_a.borrow()); let mut node = Node::clone(&leaf_with_collision_bucket_a_b); node.compress(); assert_eq!(node, *leaf_with_collision_bucket_a_b.borrow()); } #[test] fn test_remove() { let map_a_b_c_d_e: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xA, 0) .insert(0xB, 1) .insert(0xC, 2) .insert(0xD, 3) .insert(0xE, 4); let map_a_b_d_e: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xA, 0) .insert(0xB, 1) .insert(0xD, 3) .insert(0xE, 4); let map_a_c_d_e: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xA, 0) .insert(0xC, 2) .insert(0xD, 3) .insert(0xE, 4); let map_c_d_e: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xC, 2) .insert(0xD, 3) .insert(0xE, 4); let map_a_b_c_e: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xA, 0) .insert(0xB, 1) .insert(0xC, 2) .insert(0xE, 4); let map_a_b_c: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16) .insert(0xA, 0) .insert(0xB, 1) .insert(0xC, 2); let map_empty: HashTrieMap<_, _, RcK, _> = HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(dummy_hash_builder(), 16); // Just a sanity check. assert_eq!(map_a_b_c_d_e.root, dummy_hash_trie_map().root); let removed_c = map_a_b_c_d_e.remove(&0xC); let removed_b = map_a_b_c_d_e.remove(&0xB); let removed_b_a = map_a_b_c_d_e.remove(&0xB).remove(&0xA); let removed_d = map_a_b_c_d_e.remove(&0xD); let removed_d_e = map_a_b_c_d_e.remove(&0xD).remove(&0xE); let removed_all = map_a_b_c_d_e.remove(&0xA).remove(&0xB).remove(&0xC).remove(&0xD).remove(&0xE); assert_eq!(removed_c.root, map_a_b_d_e.root); assert_eq!(removed_b.root, map_a_c_d_e.root); assert_eq!(removed_b_a.root, map_c_d_e.root); assert_eq!(removed_d.root, map_a_b_c_e.root); assert_eq!(removed_d_e.root, map_a_b_c.root); assert_eq!(removed_all.root, map_empty.root); assert_eq!(map_a_b_c_d_e.remove(&0x0).root, map_a_b_c_d_e.root); assert_eq!(map_a_b_c_d_e.remove(&0x1).root, map_a_b_c_d_e.root); assert_eq!(map_a_b_c_d_e.remove(&0x2).root, map_a_b_c_d_e.root); } } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_trie_max_height() { assert_eq!(iter_utils::trie_max_height(2), 64); assert_eq!(iter_utils::trie_max_height(16), 16); assert_eq!(iter_utils::trie_max_height(32), 13); assert_eq!(iter_utils::trie_max_height(64), 11); } #[test] fn test_iter_empty() { let map: HashTrieMap = HashTrieMap::new(); for _ in map.iter() { panic!("iterator should be empty"); } } fn iterator_test(initial_map: HashTrieMap) { let mut map = initial_map; let limit: usize = 50_000; for i in 0..limit { map = map.insert(i as u32, -(i as i32)); } let mut touched = vec![false; limit]; for (k, v) in map.iter() { assert!(!touched[*k as usize]); assert_eq!(*k as i32, -*v); touched[*k as usize] = true; } assert!(touched.iter().all(|b| *b)); } #[test] fn test_iter() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { iterator_test(HashTrieMap::new_with_degree(degree)); } } #[test] fn test_iter_high_collision() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { let hasher = hasher_mocks::LimitedHashSpaceHashBuilder::new(1000); iterator_test(HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher, degree)); } } #[test] fn test_iter_size_hint() { let map = ht_map![0 => 10, 1 => 11, 2 => 12]; let mut iterator = map.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_iter_keys() { let map = ht_map![0 => 10, 1 => 11, 2 => 12]; let mut touched = [false; 3]; for k in map.keys() { assert!(!touched[*k as usize]); touched[*k as usize] = true; } assert!(touched.iter().all(|b| *b)); } #[test] fn test_iter_values() { let map = ht_map![10 => 0, 11 => 1, 12 => 2]; let mut touched = [false; 3]; for v in map.values() { assert!(!touched[*v as usize]); touched[*v as usize] = true; } assert!(touched.iter().all(|b| *b)); } #[test] fn test_into_iterator() { let map = ht_map![0 => 10, 1 => 11, 2 => 12]; let mut left = 3; for _ in &map { left -= 1; assert!(left >= 0); } assert_eq!(left, 0); } } #[test] fn test_macro_ht_map() { let map_1 = HashTrieMap::new().insert(1, 2); let map_1_2_3 = HashTrieMap::new().insert(1, 2).insert(2, 3).insert(3, 4); assert_eq!(HashTrieMap::::new(), ht_map![]); assert_eq!(map_1, ht_map![1 => 2]); assert_eq!(map_1_2_3, ht_map![1 => 2, 2 => 3, 3 => 4]); } #[test] fn test_get_key_value() { let mut map = HashTrieMap::new(); map = map.insert("foo", 4); assert_eq!(map.get_key_value("foo"), Some((&"foo", &4))); map = map.insert("bar", 2); assert_eq!(map.get_key_value("foo"), Some((&"foo", &4))); assert_eq!(map.get_key_value("bar"), Some((&"bar", &2))); } #[test] fn test_insert_simple() { let mut map = HashTrieMap::new(); assert_eq!(map.size(), 0); map = map.insert("foo", 4); assert_eq!(map.size(), 1); assert_eq!(map.get("foo"), Some(&4)); map = map.insert("bar", 2); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); map = map.insert("baz", 12); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); map = map.insert("foo", 7); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&7)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); } fn insert_test(initial_map: HashTrieMap) { let mut map = initial_map; // These are relatively small limits. We prefer to do a more hardcore test in the mutable // version. let limit = 5_000; let overwrite_limit = 1_000; for i in 0..limit { map = map.insert(i, -(i as i32)); assert_eq!(map.size(), (i as usize) + 1); assert_eq!(map.get(&i), Some(&-(i as i32))); // Lets also check a previous value. let prev_key = i / 2; assert_eq!(map.get(&prev_key), Some(&-(prev_key as i32))); } // Now we test some overwrites. for i in 0..overwrite_limit { assert_eq!(map.get(&i), Some(&-(i as i32))); map = map.insert(i, 2 * i as i32); assert_eq!(map.size(), limit as usize); assert_eq!(map.get(&i), Some(&(2 * i as i32))); } } #[test] fn test_insert() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { insert_test(HashTrieMap::new_with_degree(degree)); } } #[test] fn test_insert_high_collision() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { let hasher = hasher_mocks::LimitedHashSpaceHashBuilder::new(1000); insert_test(HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher, degree)); } } #[test] fn test_insert_simple_mut() { let mut map = HashTrieMap::new(); assert_eq!(map.size(), 0); map.insert_mut("foo", 4); assert_eq!(map.size(), 1); assert_eq!(map.get("foo"), Some(&4)); map.insert_mut("bar", 2); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); map.insert_mut("baz", 12); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); map.insert_mut("foo", 7); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&7)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); } fn insert_test_mut(initial_map: HashTrieMap) { let mut map = initial_map; let limit = 25_000; let overwrite_limit = 5_000; for i in 0..limit { map.insert_mut(i, -(i as i32)); assert_eq!(map.size(), (i as usize) + 1); assert_eq!(map.get(&i), Some(&-(i as i32))); // Lets also check a previous value. let prev_key = i / 2; assert_eq!(map.get(&prev_key), Some(&-(prev_key as i32))); } // Now we test some overwrites. for i in 0..overwrite_limit { assert_eq!(map.get(&i), Some(&-(i as i32))); map.insert_mut(i, 2 * i as i32); assert_eq!(map.size(), limit as usize); assert_eq!(map.get(&i), Some(&(2 * i as i32))); } } #[test] fn test_insert_mut() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { insert_test_mut(HashTrieMap::new_with_degree(degree)); } } #[test] fn test_insert_high_collision_mut() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { let hasher = hasher_mocks::LimitedHashSpaceHashBuilder::new(1000); insert_test_mut(HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher, degree)); } } #[test] fn test_remove_simple() { let mut map = ht_map![ "foo" => 4, "bar" => 12, "mumble" => 13, "baz" => 42 ]; let empty_map: HashTrieMap = HashTrieMap::new(); assert_eq!(empty_map.remove(&3), empty_map); assert_eq!(map.size(), 4); map = map.remove("not-there"); assert_eq!(map.size(), 4); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), Some(&13)); assert_eq!(map.get("baz"), Some(&42)); map = map.remove("mumble"); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), None); assert_eq!(map.get("baz"), Some(&42)); map = map.remove("foo"); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), None); map = map.remove("baz"); assert_eq!(map.size(), 1); assert_eq!(map.get("baz"), None); map = map.remove("bar"); assert_eq!(map.size(), 0); assert_eq!(map.get("bar"), None); } fn remove_test(initial_map: HashTrieMap) { let mut map = initial_map; // These are relatively small limits. We prefer to do a more hardcore test in the mutable // version. let limit = 5_000; for i in 0..limit { map = map.insert(i, -(i as i32)); } // Now lets remove half of it. for i in (0..limit / 2).map(|i| 2 * i) { assert_eq!(map.get(&i), Some(&-(i as i32))); map = map.remove(&i); assert!(!map.contains_key(&i)); assert_eq!(map.size(), (limit - i / 2 - 1) as usize); // Also check than the previous one is ok. if i > 0 { assert_eq!(map.get(&(i - 1)), Some(&-((i - 1) as i32))); } } } #[test] fn test_remove() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { remove_test(HashTrieMap::new_with_degree(degree)); } } #[test] fn test_remove_high_collision() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { let hasher = hasher_mocks::LimitedHashSpaceHashBuilder::new(1000); remove_test(HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher, degree)); } } #[test] fn test_remove_simple_mut() { let mut map = ht_map![ "foo" => 4, "bar" => 12, "mumble" => 13, "baz" => 42 ]; assert_eq!(map.size(), 4); assert!(!map.remove_mut("not-there")); assert_eq!(map.size(), 4); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), Some(&13)); assert_eq!(map.get("baz"), Some(&42)); assert!(map.remove_mut("mumble")); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), None); assert_eq!(map.get("baz"), Some(&42)); assert!(map.remove_mut("foo")); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), None); assert!(map.remove_mut("baz")); assert_eq!(map.size(), 1); assert_eq!(map.get("baz"), None); assert!(map.remove_mut("bar")); assert_eq!(map.size(), 0); assert_eq!(map.get("bar"), None); } fn remove_test_mut(initial_map: HashTrieMap) { let mut map = initial_map; let limit = 25_000; for i in 0..limit { map.insert_mut(i, -(i as i32)); } // Now lets remove half of it. for i in (0..limit / 2).map(|i| 2 * i) { assert_eq!(map.get(&i), Some(&-(i as i32))); map.remove_mut(&i); assert!(!map.contains_key(&i)); assert_eq!(map.size(), (limit - i / 2 - 1) as usize); // Also check than the previous one is ok. if i > 0 { assert_eq!(map.get(&(i - 1)), Some(&-((i - 1) as i32))); } } } #[test] fn test_remove_mut() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { remove_test_mut(HashTrieMap::new_with_degree(degree)); } } #[test] fn test_remove_high_collision_mut() { let degrees: Vec = [2, 4, 16, 32, DEFAULT_DEGREE] .into_iter() .filter(|d| *d <= DEFAULT_DEGREE) // we only want valid degrees .collect(); for degree in degrees { let hasher = hasher_mocks::LimitedHashSpaceHashBuilder::new(1000); remove_test_mut(HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher, degree)); } } #[test] fn test_index() { let map = ht_map![5 => "hello", 12 => "there"]; assert_eq!(map[&5], "hello"); assert_eq!(map[&12], "there"); } #[test] fn test_from_iterator() { let vec: Vec<(i32, &str)> = vec![(2, "two"), (5, "five")]; let map: HashTrieMap = vec.iter().copied().collect(); let expected_map = ht_map![2 => "two", 5 => "five"]; assert_eq!(map, expected_map); } #[test] fn test_default() { let map: HashTrieMap = HashTrieMap::default(); assert_eq!(map.size(), 0); assert!(map.is_empty()); } #[test] fn test_display() { let empty_map: HashTrieMap = HashTrieMap::new(); let singleton_map = ht_map!["hi" =>"hello"]; let map = ht_map![5 => "hello", 12 => "there"]; assert_eq!(format!("{}", empty_map), "{}"); assert_eq!(format!("{}", singleton_map), "{hi: hello}"); assert!( format!("{map}") == "{5: hello, 12: there}" || format!("{map}") == "{12: there, 5: hello}" ); } #[test] fn test_eq() { let map_1 = ht_map!["a" => 0xa, "b" => 0xb]; let map_1_prime = ht_map!["a" => 0xa, "b" => 0xb]; let map_1_prime_2 = ht_map!["a" => 0xa, "b" => 0xb, "b" => 0xb]; let map_2 = ht_map!["a" => 0xa, "b" => 0xb + 1]; let map_3 = ht_map!["a" => 0xa, "b" => 0xb + 1, "c" => 0xc]; assert_eq!(map_1, map_1_prime); assert_eq!(map_1, map_1_prime_2); assert_eq!(map_1, map_1); assert_eq!(map_2, map_2); // We also check this since `assert_ne!()` does not call `ne`. assert!(map_1.ne(&map_2)); assert!(map_2.ne(&map_3)); } #[test] fn test_eq_pointer_kind_consistent() { let map_a = ht_map!["a" => 0]; let map_a_sync = ht_map_sync!["a" => 0]; let map_b = ht_map!["b" => 1]; let map_b_sync = ht_map_sync!["b" => 1]; assert!(map_a == map_a_sync); assert!(map_a != map_b_sync); assert!(map_b == map_b_sync); } #[test] fn test_clone() { let map = ht_map!["hello" => 4, "there" => 5]; let clone = map.clone(); assert_eq!(clone.size(), map.size()); assert_eq!(clone.get("hello"), Some(&4)); assert_eq!(clone.get("there"), Some(&5)); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let map: HashTrieMap = ht_map![5 => 6, 7 => 8, 9 => 10, 11 => 12]; let encoded = serialize(&map).unwrap(); let decoded: HashTrieMap = deserialize(&encoded).unwrap(); assert_eq!(map, decoded); } rpds-1.1.0/src/map/mod.rs000064400000000000000000000004161046102023000132650ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ mod entry; pub mod hash_trie_map; pub mod red_black_tree_map; rpds-1.1.0/src/map/red_black_tree_map/mod.rs000064400000000000000000001453611046102023000170600ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::entry::Entry; use archery::{ArcTK, RcK, SharedPointer, SharedPointerKind}; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt::Display; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; use core::marker::PhantomData; use core::ops::{Index, RangeBounds, RangeFull}; // TODO Use impl trait instead of this when available. pub type Iter<'a, K, V, P> = core::iter::Map, fn(&'a SharedPointer, P>) -> (&'a K, &'a V)>; pub type IterKeys<'a, K, V, P> = core::iter::Map, fn((&'a K, &V)) -> &'a K>; pub type IterValues<'a, K, V, P> = core::iter::Map, fn((&K, &'a V)) -> &'a V>; pub type RangeIter<'a, K, V, RB, Q, P> = core::iter::Map< RangeIterPtr<'a, K, V, RB, Q, P>, fn(&'a SharedPointer, P>) -> (&'a K, &'a V), >; /// Creates a [`RedBlackTreeMap`](crate::RedBlackTreeMap) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let m = RedBlackTreeMap::new() /// .insert(1, "one") /// .insert(2, "two") /// .insert(3, "three"); /// /// assert_eq!(rbt_map![1 => "one", 2 => "two", 3 => "three"], m); /// ``` #[macro_export] macro_rules! rbt_map { ($($k:expr => $v:expr),*) => { { #[allow(unused_mut)] let mut m = $crate::RedBlackTreeMap::new(); $( m.insert_mut($k, $v); )* m } }; } /// Creates a [`RedBlackTreeMap`](crate::RedBlackTreeMap) that implements `Sync`, containing the /// given arguments: /// /// ``` /// # use rpds::*; /// # /// let m = RedBlackTreeMap::new_sync() /// .insert(1, "one") /// .insert(2, "two") /// .insert(3, "three"); /// /// assert_eq!(rbt_map_sync![1 => "one", 2 => "two", 3 => "three"], m); /// ``` #[macro_export] macro_rules! rbt_map_sync { ($($k:expr => $v:expr),*) => { { #[allow(unused_mut)] let mut m = $crate::RedBlackTreeMap::new_sync(); $( m.insert_mut($k, $v); )* m } }; } /// A persistent map with structural sharing. This implementation uses a /// [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree). /// /// # Complexity /// /// Let *n* be the number of elements in the map. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:-------------------------- | ---------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `insert()` | Θ(log(n)) | Θ(log(n)) | /// | `remove()` | Θ(log(n)) | Θ(log(n)) | /// | `get()` | Θ(log(n)) | Θ(log(n)) | /// | `contains_key()` | Θ(log(n)) | Θ(log(n)) | /// | `size()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(log(n)) | Θ(log(n)) | /// | iterator step | Θ(1) | Θ(log(n)) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This implementation uses a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree) as /// described in "Purely Functional Data Structures" by Chris Okasaki, page 27. Deletion is /// implemented according to the paper "Red-Black Trees with Types" by Stefan Kahrs /// ([reference implementation](https://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs)) #[derive(Debug)] pub struct RedBlackTreeMap where P: SharedPointerKind, { root: Option, P>>, size: usize, } pub type RedBlackTreeMapSync = RedBlackTreeMap; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Color { Red, Black, } #[derive(Debug)] struct Node where P: SharedPointerKind, { entry: SharedPointer, P>, color: Color, left: Option, P>>, right: Option, P>>, } impl Clone for Node where P: SharedPointerKind, { fn clone(&self) -> Node { Node { entry: SharedPointer::clone(&self.entry), color: self.color, left: self.left.clone(), right: self.right.clone(), } } } impl Node where K: Ord, P: SharedPointerKind, { fn new_red(entry: Entry) -> Node { Node { entry: SharedPointer::new(entry), color: Color::Red, left: None, right: None } } fn new_black(entry: Entry) -> Node { Node { entry: SharedPointer::new(entry), color: Color::Black, left: None, right: None } } fn borrow(node: &Option, P>>) -> Option<&Node> { node.as_ref().map(Borrow::borrow) } fn left_color(&self) -> Option { self.left.as_ref().map(|l| l.color) } fn right_color(&self) -> Option { self.right.as_ref().map(|r| r.color) } fn get(&self, key: &Q) -> Option<&Entry> where K: Borrow, Q: Ord, { match key.cmp(self.entry.key.borrow()) { Ordering::Less => self.left.as_ref().and_then(|l| l.get(key)), Ordering::Equal => Some(&self.entry), Ordering::Greater => self.right.as_ref().and_then(|r| r.get(key)), } } fn first(&self) -> &Entry { match self.left { Some(ref l) => l.first(), None => &self.entry, } } fn last(&self) -> &Entry { match self.right { Some(ref r) => r.last(), None => &self.entry, } } /// Balances an unbalanced node. This is a function is described in "Purely Functional /// Data Structures" by Chris Okasaki, page 27. /// /// The transformation is done as the figure below shows. /// /// ```text /// ╭────────────────────╮ /// │ ┌───┐ │ /// │ │ │ Red node │ /// │ └───┘ │ /// ┏━━━┓ │ │ /// ┃ z ┃ │ ┏━━━┓ │ /// ┗━━━┛ │ ┃ ┃ Black node │ /// ╱ ╲ │ ┗━━━┛ │ /// ┌───┐ d ╰────────────────────╯ /// │ y │ Case 1 /// └───┘ ╭──────────────────────────────────────────────────╮ /// ╱ ╲ ╰────────────────────────────────────────────────╮ │ /// ┌───┐ c │ │ /// │ x │ │ │ /// └───┘ │ │ /// ╱ ╲ │ │ /// a b │ │ /// │ │ /// │ │ /// │ │ /// ┏━━━┓ │ │ /// ┃ z ┃ │ │ /// ┗━━━┛ │ │ /// ╱ ╲ │ │ /// ┌───┐ d Case 2 │ │ /// │ x │ ╭─────────────────────────────╲ │ │ /// └───┘ ╰────────────────────────────╲ ╲ ╲ ╱ /// ╱ ╲ ╲ ╲ /// a ┌───┐ ╲ ╲ /// │ y │ ╲ ╲ ┌───┐ /// └───┘ ╲ ╲ │ y │ /// ╱ ╲ ╲ │ └───┘ /// b c ───┘ ╱ ╲ /// ╱ ╲ /// ┏━━━┓ ┏━━━┓ /// ┃ x ┃ ┃ z ┃ /// ┏━━━┓ ┗━━━┛ ┗━━━┛ /// ┃ x ┃ ───┐ ╱ ╲ ╱ ╲ /// ┗━━━┛ ╱ │ ╱ ╲ ╱ ╲ /// ╱ ╲ ╱ ╱ a b c d /// a ┌───┐ ╱ ╱ /// │ z │ ╱ ╱ /// └───┘ Case 3 ╱ ╱ ╱ ╲ /// ╱ ╲ ╭────────────────────────────╱ ╱ │ │ /// ┌───┐ d ╰─────────────────────────────╱ │ │ /// │ y │ │ │ /// └───┘ │ │ /// ╱ ╲ │ │ /// b c │ │ /// │ │ /// │ │ /// │ │ /// ┏━━━┓ │ │ /// ┃ x ┃ │ │ /// ┗━━━┛ │ │ /// ╱ ╲ │ │ /// a ┌───┐ Case 4 │ │ /// │ y │ ╭────────────────────────────────────────────────┘ │ /// └───┘ ╰──────────────────────────────────────────────────┘ /// ╱ ╲ /// b ┌───┐ /// │ z │ /// └───┘ /// ╱ ╲ /// c d /// ``` fn balance(&mut self) { use core::mem::swap; use Color::Black as B; use Color::Red as R; match self.color { B => { let color_l: Option = self.left_color(); let color_l_l: Option = self.left.as_ref().and_then(|l| l.left_color()); let color_l_r: Option = self.left.as_ref().and_then(|l| l.right_color()); let color_r: Option = self.right_color(); let color_r_l: Option = self.right.as_ref().and_then(|r| r.left_color()); let color_r_r: Option = self.right.as_ref().and_then(|r| r.right_color()); match (color_l, color_l_l, color_l_r, color_r, color_r_l, color_r_r) { // Case 1 (Some(R), Some(R), ..) => { let mut node_l_ptr = self.left.take().unwrap(); let node_l: &mut Node = SharedPointer::make_mut(&mut node_l_ptr); let mut node_l_l_ptr = node_l.left.take().unwrap(); let node_l_l: &mut Node = SharedPointer::make_mut(&mut node_l_l_ptr); self.color = Color::Red; node_l.color = Color::Black; node_l_l.color = Color::Black; swap(&mut self.entry, &mut node_l.entry); swap(&mut node_l.left, &mut node_l.right); swap(&mut self.right, &mut node_l.right); self.left = Some(node_l_l_ptr); self.right = Some(node_l_ptr); } // Case 2 (Some(R), _, Some(R), ..) => { let mut node_l_ptr = self.left.take().unwrap(); let node_l: &mut Node = SharedPointer::make_mut(&mut node_l_ptr); let mut node_l_r_ptr = node_l.right.take().unwrap(); let node_l_r: &mut Node = SharedPointer::make_mut(&mut node_l_r_ptr); self.color = Color::Red; node_l.color = Color::Black; node_l_r.color = Color::Black; swap(&mut self.entry, &mut node_l_r.entry); swap(&mut node_l_r.left, &mut node_l_r.right); swap(&mut node_l.right, &mut node_l_r.right); swap(&mut self.right, &mut node_l_r.right); self.right = Some(node_l_r_ptr); self.left = Some(node_l_ptr); } // Case 3 (.., Some(R), Some(R), _) => { let mut node_r_ptr = self.right.take().unwrap(); let node_r: &mut Node = SharedPointer::make_mut(&mut node_r_ptr); let mut node_r_l_ptr = node_r.left.take().unwrap(); let node_r_l: &mut Node = SharedPointer::make_mut(&mut node_r_l_ptr); self.color = Color::Red; node_r.color = Color::Black; node_r_l.color = Color::Black; swap(&mut self.entry, &mut node_r_l.entry); swap(&mut node_r.left, &mut node_r_l.right); swap(&mut node_r_l.left, &mut node_r_l.right); swap(&mut self.left, &mut node_r_l.left); self.left = Some(node_r_l_ptr); self.right = Some(node_r_ptr); } // Case 4 (.., Some(R), _, Some(R)) => { let mut node_r_ptr = self.right.take().unwrap(); let node_r: &mut Node = SharedPointer::make_mut(&mut node_r_ptr); let mut node_r_r_ptr = node_r.right.take().unwrap(); let node_r_r: &mut Node = SharedPointer::make_mut(&mut node_r_r_ptr); self.color = Color::Red; node_r.color = Color::Black; node_r_r.color = Color::Black; swap(&mut self.entry, &mut node_r.entry); swap(&mut node_r.left, &mut node_r.right); swap(&mut self.left, &mut node_r.left); self.right = Some(node_r_r_ptr); self.left = Some(node_r_ptr); } _ => (), } } R => (), } } /// Inserts the entry and returns whether the key is new. fn insert(root: &mut Option, P>>, key: K, value: V) -> bool { fn ins( node: &mut Option, P>>, k: K, v: V, is_root: bool, ) -> bool { match node { Some(node) => { let node = SharedPointer::make_mut(node); let ret = match k.cmp(&node.entry.key) { Ordering::Less => { let is_new_key = ins(&mut node.left, k, v, false); // Small optimization: avoid unnecessary calls to balance. if is_new_key { node.balance(); } is_new_key } Ordering::Equal => { node.entry = SharedPointer::new(Entry::new(k, v)); false } Ordering::Greater => { let is_new_key = ins(&mut node.right, k, v, false); // Small optimization: avoid unnecessary calls to balance. if is_new_key { node.balance(); } is_new_key } }; if is_root { node.color = Color::Black; } ret } None => { *node = if is_root { Some(SharedPointer::new(Node::new_black(Entry::new(k, v)))) } else { Some(SharedPointer::new(Node::new_red(Entry::new(k, v)))) }; true } } } ins(root, key, value, true) } /// Returns `false` if node has no children to merge. fn remove_fuse( node: &mut Node, left: Option, P>>, right: Option, P>>, ) -> bool { use Color::Black as B; use Color::Red as R; use core::mem::swap; match (left, right) { (None, None) => false, (None, Some(r_ptr)) => { crate::utils::replace(node, r_ptr); true } (Some(l_ptr), None) => { crate::utils::replace(node, l_ptr); true } (Some(mut l_ptr), Some(mut r_ptr)) => { match (l_ptr.color, r_ptr.color) { (B, R) => { let r = SharedPointer::make_mut(&mut r_ptr); let rl = r.left.take(); // This will always return `true`. Node::remove_fuse(node, Some(l_ptr), rl); swap(node, r); node.left = Some(r_ptr); } (R, B) => { let l = SharedPointer::make_mut(&mut l_ptr); let lr = l.right.take(); // This will always return `true`. Node::remove_fuse(node, lr, Some(r_ptr)); swap(node, l); node.right = Some(l_ptr); } (R, R) => { let r = SharedPointer::make_mut(&mut r_ptr); let rl = r.left.take(); let l = SharedPointer::make_mut(&mut l_ptr); let lr = l.right.take(); let fused = Node::remove_fuse(node, lr, rl); match node.color { R if fused => { let fl = node.left.take(); let fr = node.right.take(); l.right = fl; r.left = fr; node.left = Some(l_ptr); node.right = Some(r_ptr); } _ => { swap(l, node); if fused { r.left = Some(l_ptr); } node.right = Some(r_ptr); } } } (B, B) => { let r = SharedPointer::make_mut(&mut r_ptr); let rl = r.left.take(); let l = SharedPointer::make_mut(&mut l_ptr); let lr = l.right.take(); let fused = Node::remove_fuse(node, lr, rl); match node.color { R if fused => { let fl = node.left.take(); let fr = node.right.take(); l.right = fl; r.left = fr; node.left = Some(l_ptr); node.right = Some(r_ptr); } _ => { swap(l, node); if fused { r.left = Some(l_ptr); } node.color = Color::Red; node.right = Some(r_ptr); node.remove_balance_left(); } } } }; true } } } fn remove_balance(&mut self) { match (self.left_color(), self.right_color()) { (Some(Color::Red), Some(Color::Red)) => { SharedPointer::make_mut(self.left.as_mut().unwrap()).color = Color::Black; SharedPointer::make_mut(self.right.as_mut().unwrap()).color = Color::Black; self.color = Color::Red; } _ => { // Our `balance()` does nothing unless the color is black, which the caller // must ensure. debug_assert_eq!(self.color, Color::Black); self.balance(); } } } fn remove_balance_left(&mut self) { use Color::Black as B; use Color::Red as R; use core::mem::swap; let color_l: Option = self.left_color(); let color_r: Option = self.right_color(); let color_r_l: Option = self.right.as_ref().and_then(|r| r.left_color()); match (color_l, color_r, color_r_l) { (Some(R), ..) => { let self_l = SharedPointer::make_mut(self.left.as_mut().unwrap()); self.color = Color::Red; self_l.color = Color::Black; } (_, Some(B), _) => { let self_r = SharedPointer::make_mut(self.right.as_mut().unwrap()); self.color = Color::Black; self_r.color = Color::Red; self.remove_balance(); } (_, Some(R), Some(B)) => { let self_r = SharedPointer::make_mut(self.right.as_mut().unwrap()); let mut self_r_l_ptr = self_r.left.take().unwrap(); let self_r_l = SharedPointer::make_mut(&mut self_r_l_ptr); let new_r_l = self_r_l.right.take(); self_r.color = Color::Black; self_r.left = new_r_l; SharedPointer::make_mut(self_r.right.as_mut().unwrap()).color = Color::Red; self_r.remove_balance(); self.color = Color::Red; self_r_l.right = self_r_l.left.take(); self_r_l.left = self.left.take(); swap(&mut self.entry, &mut self_r_l.entry); self.left = Some(self_r_l_ptr); } _ => unreachable!(), } } fn remove_balance_right(&mut self) { use Color::Black as B; use Color::Red as R; use core::mem::swap; let color_r: Option = self.right_color(); let color_l: Option = self.left_color(); let color_l_r: Option = self.left.as_ref().and_then(|l| l.right_color()); match (color_l, color_l_r, color_r) { (.., Some(R)) => { let self_r = SharedPointer::make_mut(self.right.as_mut().unwrap()); self.color = Color::Red; self_r.color = Color::Black; } (Some(B), ..) => { let self_l = SharedPointer::make_mut(self.left.as_mut().unwrap()); self.color = Color::Black; self_l.color = Color::Red; self.remove_balance(); } (Some(R), Some(B), _) => { let self_l = SharedPointer::make_mut(self.left.as_mut().unwrap()); let mut self_l_r_ptr = self_l.right.take().unwrap(); let self_l_r = SharedPointer::make_mut(&mut self_l_r_ptr); let new_l_r = self_l_r.left.take(); self_l.color = Color::Black; self_l.right = new_l_r; SharedPointer::make_mut(self_l.left.as_mut().unwrap()).color = Color::Red; self_l.remove_balance(); self.color = Color::Red; self_l_r.left = self_l_r.right.take(); self_l_r.right = self.right.take(); swap(&mut self.entry, &mut self_l_r.entry); self.right = Some(self_l_r_ptr); } _ => unreachable!(), } } /// Returns `true` if the key was present. /// /// If the node becomes empty `*root` will be set to `None`. fn remove(root: &mut Option, P>>, key: &Q) -> bool where K: Borrow, Q: Ord, { fn del_left(node: &mut Node, k: &Q) -> bool where K: Borrow + Ord, Q: Ord, P: SharedPointerKind, { let original_left_color = node.left_color(); let removed = del(&mut node.left, k, false); node.color = Color::Red; // In case of rebalance the color does not matter. if let Some(Color::Black) = original_left_color { node.remove_balance_left(); } removed } fn del_right(node: &mut Node, k: &Q) -> bool where K: Borrow + Ord, Q: Ord, P: SharedPointerKind, { let original_right_color = node.right_color(); let removed = del(&mut node.right, k, false); node.color = Color::Red; // In case of rebalance the color does not matter. if let Some(Color::Black) = original_right_color { node.remove_balance_right(); } removed } fn del( node: &mut Option, P>>, k: &Q, is_root: bool, ) -> bool where K: Borrow + Ord, Q: Ord, P: SharedPointerKind, { let (removed, make_node_none) = match *node { Some(ref mut node) => { let node = SharedPointer::make_mut(node); let ret = match k.cmp(node.entry.key.borrow()) { Ordering::Less => (del_left(node, k), false), Ordering::Equal => { let left = node.left.take(); let right = node.right.take(); let make_node_none = !Node::remove_fuse(node, left, right); (true, make_node_none) } Ordering::Greater => (del_right(node, k), false), }; if is_root { node.color = Color::Black; } ret } None => (false, false), }; if make_node_none { *node = None; } removed } del(root, key, true) } } impl Node where K: Ord + Clone, V: Clone, P: SharedPointerKind, { fn get_mut(&mut self, key: &Q) -> Option<&mut Entry> where K: Borrow, Q: Ord, { match key.cmp(self.entry.key.borrow()) { Ordering::Less => { self.left.as_mut().and_then(|l| SharedPointer::make_mut(l).get_mut(key)) } Ordering::Equal => Some(SharedPointer::make_mut(&mut self.entry)), Ordering::Greater => { self.right.as_mut().and_then(|r| SharedPointer::make_mut(r).get_mut(key)) } } } } impl RedBlackTreeMapSync where K: Ord, { #[must_use] pub fn new_sync() -> RedBlackTreeMapSync { RedBlackTreeMap::new_with_ptr_kind() } } impl RedBlackTreeMap where K: Ord, { #[must_use] pub fn new() -> RedBlackTreeMap { RedBlackTreeMap::new_with_ptr_kind() } } impl RedBlackTreeMap where K: Ord, P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> RedBlackTreeMap { RedBlackTreeMap { root: None, size: 0 } } #[must_use] pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow, Q: Ord, { self.root.as_ref().and_then(|r| r.get(key)).map(|e| &e.value) } #[must_use] pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow, Q: Ord, { self.root.as_ref().and_then(|r| r.get(key)).map(|e| (&e.key, &e.value)) } #[must_use] pub fn first(&self) -> Option<(&K, &V)> { self.root.as_ref().map(|r| r.first()).map(|e| (&e.key, &e.value)) } #[must_use] pub fn last(&self) -> Option<(&K, &V)> { self.root.as_ref().map(|r| r.last()).map(|e| (&e.key, &e.value)) } #[must_use] pub fn insert(&self, key: K, value: V) -> RedBlackTreeMap { let mut new_map = self.clone(); new_map.insert_mut(key, value); new_map } pub fn insert_mut(&mut self, key: K, value: V) { let is_new_key = Node::insert(&mut self.root, key, value); if is_new_key { self.size += 1; } } #[must_use] pub fn remove(&self, key: &Q) -> RedBlackTreeMap where K: Borrow, Q: Ord, { let mut new_map = self.clone(); if new_map.remove_mut(key) { new_map } else { // We want to keep maximum sharing so in case of no change we just `clone()` ourselves. self.clone() } } pub fn remove_mut(&mut self, key: &Q) -> bool where K: Borrow, Q: Ord, { let removed = Node::remove(&mut self.root, key); // Note that unfortunately, even if nothing was removed, we still might have cloned some // part of the tree unnecessarily. if removed { self.size -= 1; } removed } #[must_use] pub fn contains_key(&self, key: &Q) -> bool where K: Borrow, Q: Ord, { self.get(key).is_some() } /// Test whether the two maps refer to the same content in memory. /// /// This would return true if you’re comparing a map to itself, /// or if you’re comparing a map to a fresh clone of itself. pub(crate) fn ptr_eq(&self, other: &RedBlackTreeMap) -> bool { let a = self.root.as_ref().map_or(core::ptr::null(), SharedPointer::as_ptr); // Note how we're casting the raw pointer changing from P to PO // We cannot perform the equality in a type safe way because the root type depends // on P/PO, and we can't pass different types to SharedPtr::same_ptr or std::ptr::eq. let b = other .root .as_ref() .map_or(core::ptr::null(), SharedPointer::as_ptr) .cast::>(); core::ptr::eq(a, b) } #[must_use] #[inline] pub fn size(&self) -> usize { self.size } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.size() == 0 } pub fn iter(&self) -> Iter<'_, K, V, P> { self.iter_ptr().map(|e| (&e.key, &e.value)) } #[must_use] fn iter_ptr(&self) -> IterPtr<'_, K, V, P> { IterPtr::new(self) } pub fn keys(&self) -> IterKeys<'_, K, V, P> { self.iter().map(|(k, _)| k) } pub fn values(&self) -> IterValues<'_, K, V, P> { self.iter().map(|(_, v)| v) } pub fn range(&self, range: RB) -> RangeIter<'_, K, V, RB, Q, P> where K: Borrow, Q: Ord + ?Sized, RB: RangeBounds, { use core::ops::Bound::*; match (range.start_bound(), range.end_bound()) { (Excluded(s), Excluded(e)) if s == e => { panic!("range start and end are equal and excluded") } (Included(s), Included(e)) | (Included(s), Excluded(e)) | (Excluded(s), Included(e)) | (Excluded(s), Excluded(e)) if s > e => { panic!("range start is greater than range end") } (_, _) => RangeIterPtr::new(self, range).map(|e| (&e.key, &e.value)), } } } impl RedBlackTreeMap where K: Ord + Clone, V: Clone, P: SharedPointerKind, { pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow, Q: Ord, { // Note that unfortunately, even if nothing is found, we still might have cloned some // part of the tree unnecessarily. self.root .as_mut() .and_then(|r| SharedPointer::make_mut(r).get_mut(key).map(|e| &mut e.value)) } } impl<'a, K, Q: ?Sized, V, P> Index<&'a Q> for RedBlackTreeMap where K: Ord + Borrow, Q: Ord, P: SharedPointerKind, { type Output = V; fn index(&self, key: &Q) -> &V { self.get(key).expect("no entry found for key") } } impl Clone for RedBlackTreeMap where K: Ord, P: SharedPointerKind, { fn clone(&self) -> RedBlackTreeMap { RedBlackTreeMap { root: self.root.clone(), size: self.size } } } impl Default for RedBlackTreeMap where K: Ord, P: SharedPointerKind, { fn default() -> RedBlackTreeMap { RedBlackTreeMap::new_with_ptr_kind() } } impl PartialEq> for RedBlackTreeMap where K: Ord, P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &RedBlackTreeMap) -> bool { if self.ptr_eq(other) { return true; } self.size() == other.size() && self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) } } impl Eq for RedBlackTreeMap where K: Ord, P: SharedPointerKind, { } impl PartialOrd> for RedBlackTreeMap where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &RedBlackTreeMap) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for RedBlackTreeMap where P: SharedPointerKind, { fn cmp(&self, other: &RedBlackTreeMap) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for RedBlackTreeMap where K: Ord, { fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it // doesn't hash to the same thing as a single collection with the same elements in the same // order. self.size().hash(state); for e in self { e.hash(state); } } } impl Display for RedBlackTreeMap where K: Ord + Display, V: Display, P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("{")?; for (k, v) in self { if !first { fmt.write_str(", ")?; } k.fmt(fmt)?; fmt.write_str(": ")?; v.fmt(fmt)?; first = false; } fmt.write_str("}") } } impl<'a, K, V, P> IntoIterator for &'a RedBlackTreeMap where K: Ord, P: SharedPointerKind, { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V, P>; fn into_iter(self) -> Iter<'a, K, V, P> { self.iter() } } // TODO This can be improved to create a perfectly balanced tree. impl FromIterator<(K, V)> for RedBlackTreeMap where K: Ord, P: SharedPointerKind, { fn from_iter>(into_iter: I) -> RedBlackTreeMap { let mut map = RedBlackTreeMap::new_with_ptr_kind(); for (k, v) in into_iter { map.insert_mut(k, v); } map } } mod iter_utils { use super::{Entry, Node, RedBlackTreeMap}; use alloc::vec::Vec; use archery::{SharedPointer, SharedPointerKind}; use core::borrow::Borrow; use core::ops::Bound; // This is a stack for navigating through the tree. It can be used to go either forwards or // backwards: you choose the direction at construction time, and then every call to `advance` // goes in that direction. #[derive(Debug)] pub struct IterStack<'a, K, V, P> where P: SharedPointerKind, { // The invariant maintained by `stack` depends on whether we are moving forwards or backwards. // In either case, the current node is at the top of the stack. If we are moving forwards, the // rest of the stack consists of those ancestors of the current node that contain the current // node in their left subtree. In other words, the keys in the stack increase as we go from the // top of the stack to the bottom. stack: Vec<&'a Node>, backwards: bool, } impl<'a, K, V, P> IterStack<'a, K, V, P> where K: Ord, P: SharedPointerKind, { pub fn new( map: &'a RedBlackTreeMap, start_bound: Bound<&Q>, end_bound: Bound<&Q>, backwards: bool, ) -> IterStack<'a, K, V, P> where K: Borrow, Q: Ord + ?Sized, { let size = conservative_height(map.size()) + 1; let mut stack = IterStack { stack: Vec::with_capacity(size), backwards }; if let Some(ref root) = map.root { stack.dig_towards(root.borrow(), start_bound, end_bound); } stack.clear_if_finished(start_bound, end_bound); stack } fn clear_if_finished(&mut self, start_bound: Bound<&Q>, end_bound: Bound<&Q>) where K: Borrow, Q: Ord + ?Sized, { use core::ops::Bound::*; if let Some(entry) = self.current() { let in_range = if self.backwards { match start_bound { Included(v) => entry.key.borrow() >= v, Excluded(v) => entry.key.borrow() > v, Unbounded => true, } } else { match end_bound { Included(v) => entry.key.borrow() <= v, Excluded(v) => entry.key.borrow() < v, Unbounded => true, } }; if !in_range { self.stack.clear(); } }; } #[inline] pub fn current(&self) -> Option<&'a SharedPointer, P>> { self.stack.last().map(|node| &node.entry) } fn dig(&mut self) where K: Borrow, Q: Ord + ?Sized, { let child = self.stack.last().and_then(|node| { let c = if self.backwards { &node.right } else { &node.left }; Node::borrow(c) }); if let Some(c) = child { self.stack.push(c); self.dig(); } } fn dig_towards( &mut self, node: &'a Node, start_bound: Bound<&Q>, end_bound: Bound<&Q>, ) where K: Borrow, Q: Ord + ?Sized, { use core::ops::Bound::*; let in_range = if self.backwards { match end_bound { Included(v) => node.entry.key.borrow() <= v, Excluded(v) => node.entry.key.borrow() < v, Unbounded => true, } } else { match start_bound { Included(v) => node.entry.key.borrow() >= v, Excluded(v) => node.entry.key.borrow() > v, Unbounded => true, } }; if in_range { self.stack.push(node); } let child = match (self.backwards, in_range) { (false, true) => &node.left, (false, false) => &node.right, (true, true) => &node.right, (true, false) => &node.left, }; if let Some(c) = child { self.dig_towards(c.borrow(), start_bound, end_bound); } } pub fn advance(&mut self, start_bound: Bound<&Q>, end_bound: Bound<&Q>) where K: Borrow, Q: Ord + ?Sized, { if let Some(node) = self.stack.pop() { let child = if self.backwards { &node.left } else { &node.right }; if let Some(c) = Node::borrow(child) { self.stack.push(c); self.dig(); } self.clear_if_finished(start_bound, end_bound); } } } pub fn lg_floor(size: usize) -> usize { debug_assert!(size > 0); let c: usize = usize::BITS as usize - size.leading_zeros() as usize; c - 1 } pub fn conservative_height(size: usize) -> usize { if size > 0 { 2 * lg_floor(size + 1) } else { 0 } } } #[derive(Debug)] pub struct IterPtr<'a, K, V, P> where P: SharedPointerKind, { range_iter: RangeIterPtr<'a, K, V, RangeFull, K, P>, // Number of elements left in the iterator. size: usize, } impl<'a, K, V, P> IterPtr<'a, K, V, P> where K: Ord, P: SharedPointerKind, { fn new(map: &RedBlackTreeMap) -> IterPtr<'_, K, V, P> { IterPtr { range_iter: RangeIterPtr::new(map, ..), size: map.size } } } impl<'a, K, V, P> Iterator for IterPtr<'a, K, V, P> where K: Ord, P: SharedPointerKind, { type Item = &'a SharedPointer, P>; fn next(&mut self) -> Option<&'a SharedPointer, P>> { if self.size > 0 { self.size -= 1; self.range_iter.next() } else { None } } fn size_hint(&self) -> (usize, Option) { (self.size, Some(self.size)) } } impl<'a, K, V, P> DoubleEndedIterator for IterPtr<'a, K, V, P> where K: Ord, P: SharedPointerKind, { fn next_back(&mut self) -> Option<&'a SharedPointer, P>> { if self.size > 0 { self.size -= 1; self.range_iter.next_back() } else { None } } } impl<'a, K: Ord, V, P> ExactSizeIterator for IterPtr<'a, K, V, P> where P: SharedPointerKind {} #[derive(Debug)] pub struct RangeIterPtr<'a, K, V, RB, Q: ?Sized, P> where P: SharedPointerKind, { map: &'a RedBlackTreeMap, stack_forward: Option>, stack_backward: Option>, range: RB, _q: PhantomData, } impl<'a, K, V, Q, RB, P> RangeIterPtr<'a, K, V, RB, Q, P> where K: Ord + Borrow, Q: Ord + ?Sized, RB: RangeBounds, P: SharedPointerKind, { fn new(map: &'a RedBlackTreeMap, range: RB) -> RangeIterPtr<'_, K, V, RB, Q, P> { RangeIterPtr { map, stack_forward: None, stack_backward: None, range, _q: PhantomData } } fn init_if_needed(&mut self, backwards: bool) { use iter_utils::IterStack; let stack_field = if backwards { &mut self.stack_backward } else { &mut self.stack_forward }; if stack_field.is_none() { *stack_field = Some(IterStack::new( self.map, self.range.start_bound(), self.range.end_bound(), backwards, )); } } fn is_remaining_range_empty(&self) -> bool { match (&self.stack_forward, &self.stack_backward) { (Some(stack_forward), Some(stack_backward)) => { match (stack_forward.current(), stack_backward.current()) { (Some(left), Some(right)) => left.key > right.key, (_, _) => true, } } (_, _) => false, } } fn current_forward(&self) -> Option<&'a SharedPointer, P>> { match self.is_remaining_range_empty() { true => None, false => self.stack_forward.as_ref().unwrap().current(), } } fn advance_forward(&mut self) { self.stack_forward .as_mut() .unwrap() .advance(self.range.start_bound(), self.range.end_bound()); } fn current_backward(&self) -> Option<&'a SharedPointer, P>> { match self.is_remaining_range_empty() { true => None, false => self.stack_backward.as_ref().unwrap().current(), } } fn advance_backward(&mut self) { self.stack_backward .as_mut() .unwrap() .advance(self.range.start_bound(), self.range.end_bound()); } } impl<'a, K, V, RB, Q, P> Iterator for RangeIterPtr<'a, K, V, RB, Q, P> where K: Ord + Borrow, Q: Ord + ?Sized, RB: RangeBounds, P: SharedPointerKind, { type Item = &'a SharedPointer, P>; fn next(&mut self) -> Option { self.init_if_needed(false); let current = self.current_forward(); self.advance_forward(); current } } impl<'a, K, V, RB, Q, P> DoubleEndedIterator for RangeIterPtr<'a, K, V, RB, Q, P> where K: Ord + Borrow, Q: Ord + ?Sized, RB: RangeBounds, P: SharedPointerKind, { fn next_back(&mut self) -> Option<&'a SharedPointer, P>> { self.init_if_needed(true); let current = self.current_backward(); self.advance_backward(); current } } #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for RedBlackTreeMap where K: Ord + Serialize, V: Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_map(self) } } impl<'de, K, V, P> Deserialize<'de> for RedBlackTreeMap where K: Ord + Deserialize<'de>, V: Deserialize<'de>, P: SharedPointerKind, { fn deserialize>( deserializer: D, ) -> Result, D::Error> { deserializer.deserialize_map(RedBlackTreeMapVisitor { _phantom_entry: PhantomData, _phantom_p: PhantomData, }) } } struct RedBlackTreeMapVisitor where P: SharedPointerKind, { _phantom_entry: PhantomData<(K, V)>, _phantom_p: PhantomData

, } impl<'de, K, V, P> Visitor<'de> for RedBlackTreeMapVisitor where K: Ord + Deserialize<'de>, V: Deserialize<'de>, P: SharedPointerKind, { type Value = RedBlackTreeMap; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } fn visit_map(self, mut map: A) -> Result, A::Error> where A: MapAccess<'de>, { let mut rb_tree_map = RedBlackTreeMap::new_with_ptr_kind(); while let Some((k, v)) = map.next_entry()? { rb_tree_map.insert_mut(k, v); } Ok(rb_tree_map) } } } #[cfg(test)] mod test; rpds-1.1.0/src/map/red_black_tree_map/test.rs000064400000000000000000001705331046102023000172570ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use alloc::vec::Vec; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(RedBlackTreeMapSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_red_black_tree_map_sync_is_send_and_sync() -> impl Send + Sync { rbt_map_sync!(0 => 0) } #[derive(Debug)] enum InvariantViolation { SizeConsistency, BinarySearch, BlackRoot, RedNodeBlackChildren, BlackHeightBalanced, } impl Node where P: SharedPointerKind, { fn count(&self) -> usize { 1 + self.left.as_ref().map_or(0, |l| l.count()) + self.right.as_ref().map_or(0, |r| r.count()) } fn is_black_height_balanced(&self) -> bool { fn black_height(node: &Node) -> Result { let bheight_left = node.left.as_ref().map_or(Ok(0), |l| black_height(l))?; let bheight_right = node.right.as_ref().map_or(Ok(0), |r| black_height(r))?; if bheight_left == bheight_right { let bheight = bheight_right; Ok(bheight + usize::from(node.color == Color::Black)) } else { Err(()) } } black_height(self).is_ok() } fn red_nodes_have_black_children(&self) -> bool { let self_ok = if self.color == Color::Red { self.left.as_ref().map_or(Color::Black, |l| l.color) == Color::Black && self.right.as_ref().map_or(Color::Black, |r| r.color) == Color::Black } else { true }; self_ok && self.left.as_ref().map_or(true, |l| l.red_nodes_have_black_children()) && self.right.as_ref().map_or(true, |r| r.red_nodes_have_black_children()) } fn has_binary_search_property(&self) -> bool where K: Clone + Ord, { fn go( node: &Node, last: &mut Option, ) -> bool { let ok_left = node.left.as_ref().map_or(true, |l| go(l, last)); let new_last = node.entry.key.clone(); let ok_node = last.as_ref().map_or(true, |l| *l < new_last); *last = Some(new_last); let ok_right = node.right.as_ref().map_or(true, |r| go(r, last)); ok_left && ok_node && ok_right } let mut last: Option = None; go(self, &mut last) } fn make_black(self) -> Node { let mut node = self; node.color = Color::Black; node } fn make_red(self) -> Node { let mut node = self; node.color = Color::Red; node } } impl RedBlackTreeMap where K: Ord + Clone, { fn check_consistent(&self) -> Result<(), InvariantViolation> { if !self.root.as_ref().map_or(true, |r| r.has_binary_search_property()) { Result::Err(InvariantViolation::BinarySearch) } else if !self.root.as_ref().map_or(true, |r| r.red_nodes_have_black_children()) { Result::Err(InvariantViolation::RedNodeBlackChildren) } else if !self.root.as_ref().map_or(true, |r| r.is_black_height_balanced()) { Result::Err(InvariantViolation::BlackHeightBalanced) } else if !self.root.as_ref().map_or(true, |r| r.color == Color::Black) { Result::Err(InvariantViolation::BlackRoot) } else if self.root.as_ref().map_or(0, |r| r.count()) != self.size() { Result::Err(InvariantViolation::SizeConsistency) } else { Ok(()) } } } impl PartialEq for Node where K: PartialEq, V: PartialEq, P: SharedPointerKind, { fn eq(&self, other: &Node) -> bool { self.entry == other.entry && self.color == other.color && self.left == other.left && self.right == other.right } } impl Eq for Node where P: SharedPointerKind {} mod node { use super::*; use pretty_assertions::assert_eq; fn dummy_entry(v: T) -> Entry { Entry { key: v.clone(), value: v } } fn dummy_node(v: T) -> Node { Node { entry: SharedPointer::new(dummy_entry(v)), color: Color::Red, left: None, right: None, } } fn dummy_node_with_children( v: T, left: Option>, right: Option>, ) -> Node { Node { entry: SharedPointer::new(dummy_entry(v)), color: Color::Red, left: left.map(SharedPointer::new), right: right.map(SharedPointer::new), } } /// Returns the following tree: /// /// ```text /// ╭───╮ /// │ 1 │ /// ╰───╯ /// ╱ ╲ /// ╱ ╲ /// ╭───╮ ╭───╮ /// │ 0 │ │ 2 │ /// ╰───╯ ╰───╯ /// ╲ /// ╲ /// ╭───╮ /// │ 3 │ /// ╰───╯ /// ``` fn dummy_tree_0_1_2_3() -> Node { let node_0 = dummy_node(0); let node_3 = dummy_node(3); let node_2 = dummy_node_with_children(2, None, Some(node_3)); dummy_node_with_children(1, Some(node_0), Some(node_2)) } #[test] fn test_get() { let tree = dummy_tree_0_1_2_3(); assert_eq!(tree.get(&0).unwrap().value, 0); assert_eq!(tree.get(&1).unwrap().value, 1); assert_eq!(tree.get(&2).unwrap().value, 2); assert_eq!(tree.get(&3).unwrap().value, 3); assert_eq!(tree.get(&4), None); } #[test] fn test_get_mut() { let original = dummy_tree_0_1_2_3(); let mut tree = original.clone(); tree.get_mut(&2).unwrap().value = -2; tree.get_mut(&3).unwrap().value = -3; assert!(tree.get_mut(&4).is_none()); assert_eq!(original.get(&0).unwrap().value, 0); assert_eq!(original.get(&1).unwrap().value, 1); assert_eq!(original.get(&2).unwrap().value, 2); assert_eq!(original.get(&3).unwrap().value, 3); assert_eq!(original.get(&4), None); assert_eq!(tree.get(&0).unwrap().value, 0); assert_eq!(tree.get(&1).unwrap().value, 1); assert_eq!(tree.get(&2).unwrap().value, -2); assert_eq!(tree.get(&3).unwrap().value, -3); assert_eq!(tree.get(&4), None); } #[test] fn test_first() { let tree = dummy_tree_0_1_2_3(); assert_eq!(tree.first().key, 0); } #[test] fn test_last() { let tree = dummy_tree_0_1_2_3(); assert_eq!(tree.last().key, 3); } #[allow(clippy::too_many_lines)] #[test] fn test_balance() { // ╭────────────────────╮ // │ ┌───┐ │ // │ │ │ Red node │ // │ └───┘ │ // ┏━━━┓ │ │ // ┃ z ┃ │ ┏━━━┓ │ // ┗━━━┛ │ ┃ ┃ Black node │ // ╱ ╲ │ ┗━━━┛ │ // ┌───┐ d ╰────────────────────╯ // │ y │ Case 1 // └───┘ ╭──────────────────────────────────────────────────╮ // ╱ ╲ ╰────────────────────────────────────────────────╮ │ // ┌───┐ c │ │ // │ x │ │ │ // └───┘ │ │ // ╱ ╲ │ │ // a b │ │ // │ │ // │ │ // │ │ // ┏━━━┓ │ │ // ┃ z ┃ │ │ // ┗━━━┛ │ │ // ╱ ╲ │ │ // ┌───┐ d Case 2 │ │ // │ x │ ╭─────────────────────────────╲ │ │ // └───┘ ╰────────────────────────────╲ ╲ ╲ ╱ // ╱ ╲ ╲ ╲ // a ┌───┐ ╲ ╲ // │ y │ ╲ ╲ ┌───┐ // └───┘ ╲ ╲ │ y │ // ╱ ╲ ╲ ┃ └───┘ // b c ───┘ ╱ ╲ // ╱ ╲ // ┏━━━┓ ┏━━━┓ // ┃ x ┃ ┃ z ┃ // ┏━━━┓ ┗━━━┛ ┗━━━┛ // ┃ x ┃ ───┐ ╱ ╲ ╱ ╲ // ┗━━━┛ ╱ ┃ ╱ ╲ ╱ ╲ // ╱ ╲ ╱ ╱ a b c d // a ┌───┐ ╱ ╱ // │ z │ ╱ ╱ // └───┘ Case 3 ╱ ╱ ╱ ╲ // ╱ ╲ ╭────────────────────────────╱ ╱ │ │ // ┌───┐ d ╰─────────────────────────────╱ │ │ // │ y │ │ │ // └───┘ │ │ // ╱ ╲ │ │ // b c │ │ // │ │ // │ │ // │ │ // ┏━━━┓ │ │ // ┃ x ┃ │ │ // ┗━━━┛ │ │ // ╱ ╲ │ │ // a ┌───┐ Case 4 │ │ // │ y │ ╭────────────────────────────────────────────────┘ │ // └───┘ ╰──────────────────────────────────────────────────┘ // ╱ ╲ // b ┌───┐ // │ z │ // └───┘ // ╱ ╲ // c d let entry_x = SharedPointer::new(Entry::new('x', ())); let entry_y = SharedPointer::new(Entry::new('y', ())); let entry_z = SharedPointer::new(Entry::new('z', ())); let tree_a = SharedPointer::new(Node::new_black(Entry::new('a', ()))); let tree_b = SharedPointer::new(Node::new_black(Entry::new('b', ()))); let tree_c = SharedPointer::new(Node::new_black(Entry::new('c', ()))); let tree_d = SharedPointer::new(Node::new_black(Entry::new('d', ()))); let mut tree_case_1: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_z), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_x), color: Color::Red, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::clone(&tree_b)), })), right: Some(SharedPointer::clone(&tree_c)), })), right: Some(SharedPointer::clone(&tree_d)), }; let mut tree_case_2: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_z), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_x), color: Color::Red, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::clone(&tree_b)), right: Some(SharedPointer::clone(&tree_c)), })), })), right: Some(SharedPointer::clone(&tree_d)), }; let mut tree_case_3: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_x), color: Color::Black, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_z), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::clone(&tree_b)), right: Some(SharedPointer::clone(&tree_c)), })), right: Some(SharedPointer::clone(&tree_d)), })), }; let mut tree_case_4: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_x), color: Color::Black, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::clone(&tree_b)), right: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_z), color: Color::Red, left: Some(SharedPointer::clone(&tree_c)), right: Some(SharedPointer::clone(&tree_d)), })), })), }; let mut tree_none_of_the_above: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_z), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_x), color: Color::Black, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::clone(&tree_b)), })), right: Some(SharedPointer::clone(&tree_c)), })), right: Some(SharedPointer::clone(&tree_d)), }; let mut tree_balanced: Node<_, _, RcK> = Node { entry: SharedPointer::clone(&entry_y), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_x), color: Color::Black, left: Some(SharedPointer::clone(&tree_a)), right: Some(SharedPointer::clone(&tree_b)), })), right: Some(SharedPointer::new(Node { entry: SharedPointer::clone(&entry_z), color: Color::Black, left: Some(SharedPointer::clone(&tree_c)), right: Some(SharedPointer::clone(&tree_d)), })), }; tree_case_1.balance(); assert_eq!(tree_case_1, tree_balanced.clone()); tree_case_2.balance(); assert_eq!(tree_case_2, tree_balanced.clone()); tree_case_3.balance(); assert_eq!(tree_case_3, tree_balanced.clone()); tree_case_4.balance(); assert_eq!(tree_case_4, tree_balanced.clone()); let tree_none_of_the_above_original = tree_none_of_the_above.clone(); tree_none_of_the_above.balance(); assert_eq!(tree_none_of_the_above, tree_none_of_the_above_original); let tree_balanced_original = tree_balanced.clone(); tree_balanced.balance(); assert_eq!(tree_balanced, tree_balanced_original); } #[test] fn test_insert() { let mut node = None; let is_new_key = Node::insert(&mut node, 0, 1); let expected_node: Node<_, _, RcK> = Node::new_black(Entry::new(0, 1)); assert!(is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); let is_new_key = Node::insert(&mut node, 0, 2); let expected_node: Node<_, _, RcK> = Node::new_black(Entry::new(0, 2)); assert!(!is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); let is_new_key = Node::insert(&mut node, 10, 3); let expected_node: Node<_, _, RcK> = Node { entry: SharedPointer::new(Entry::new(0, 2)), color: Color::Black, left: None, right: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(10, 3)), color: Color::Red, left: None, right: None, })), }; assert!(is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); let is_new_key = Node::insert(&mut node, 10, 4); let expected_node: Node<_, _, RcK> = Node { entry: SharedPointer::new(Entry::new(0, 2)), color: Color::Black, left: None, right: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(10, 4)), color: Color::Red, left: None, right: None, })), }; assert!(!is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); let is_new_key = Node::insert(&mut node, 5, 5); // It is going to get rebalanced (by case 3). let expected_node: Node<_, _, RcK> = Node { entry: SharedPointer::new(Entry::new(5, 5)), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(0, 2)), color: Color::Black, left: None, right: None, })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(10, 4)), color: Color::Black, left: None, right: None, })), }; assert!(is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); let is_new_key = Node::insert(&mut node, 0, 1); // It is going to get rebalanced (by case 3). let expected_node: Node<_, _, RcK> = Node { entry: SharedPointer::new(Entry::new(5, 5)), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(0, 1)), color: Color::Black, left: None, right: None, })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(Entry::new(10, 4)), color: Color::Black, left: None, right: None, })), }; assert!(!is_new_key); assert_eq!(node.as_ref().map(Borrow::borrow), Some(&expected_node)); } #[allow(clippy::too_many_lines)] #[test] fn test_remove_fuse() { let mut node = dummy_node(""); assert!(!Node::remove_fuse(&mut node, None, None)); let right = dummy_node("x"); let expected_node = right.clone(); assert!(Node::remove_fuse(&mut node, None, Some(SharedPointer::new(right)))); assert_eq!(node, expected_node); let left = dummy_node("x"); let expected_node = left.clone(); assert!(Node::remove_fuse(&mut node, Some(SharedPointer::new(left)), None)); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("a")), color: Color::Black, left: None, right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: None, right: Some(SharedPointer::new(dummy_node("c"))), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(left.clone())), right: Some(SharedPointer::new(dummy_node("c"))), }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("c")), color: Color::Black, left: None, right: None, }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(right.clone())), }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("c").make_red())), right: Some(SharedPointer::new(dummy_node("d"))), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("c")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: None, })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: None, right: Some(SharedPointer::new(dummy_node("d"))), })), }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("c").make_black())), right: Some(SharedPointer::new(dummy_node("d"))), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("c").make_black())), right: Some(SharedPointer::new(dummy_node("d"))), })), }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("c").make_red())), right: Some(SharedPointer::new(dummy_node("d"))), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("c")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: None, })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: None, right: Some(SharedPointer::new(dummy_node("d"))), })), }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); let left = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: None, }; let right = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("c").make_black())), right: Some(SharedPointer::new(dummy_node("d"))), }; let expected_node = { let mut n = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("c").make_black())), right: Some(SharedPointer::new(dummy_node("d"))), })), }; n.remove_balance_left(); n }; assert!(Node::remove_fuse( &mut node, Some(SharedPointer::new(left)), Some(SharedPointer::new(right)) )); assert_eq!(node, expected_node); } #[test] fn test_remove_balance() { let mut node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("c"))), right: Some(SharedPointer::new(dummy_node("d"))), })), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("c"))), right: Some(SharedPointer::new(dummy_node("d"))), })), }; node.remove_balance(); assert_eq!(node, expected_node); } #[test] fn test_remove_balance_left() { let bl = Node { entry: SharedPointer::new(dummy_entry("bl")), color: Color::Black, left: None, right: None, }; let mut node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(dummy_node("c"))), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(dummy_node("c"))), }; node.remove_balance_left(); assert_eq!(node, expected_node); let mut node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(bl.clone())), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), }; let expected_node = { let mut n = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(bl.clone())), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), }; n.remove_balance(); n }; node.remove_balance_left(); assert_eq!(node, expected_node); let mut node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(bl.clone())), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(dummy_node("c").make_black())), })), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(bl.clone())), right: Some(SharedPointer::new(dummy_node("a"))), })), right: Some(SharedPointer::new({ let mut n = Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("b"))), right: Some(SharedPointer::new(dummy_node("c").make_red())), }; n.remove_balance(); n })), }; node.remove_balance_left(); assert_eq!(node, expected_node); } #[test] fn test_remove_balance_right() { let bl = Node { entry: SharedPointer::new(dummy_entry("bl")), color: Color::Black, left: None, right: None, }; let mut node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("b"))), right: Some(SharedPointer::new(dummy_node("c"))), })), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("b"))), right: Some(SharedPointer::new(dummy_node("c"))), })), }; node.remove_balance_right(); assert_eq!(node, expected_node); let mut node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(bl.clone())), }; let expected_node = { let mut n = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a"))), right: Some(SharedPointer::new(dummy_node("b"))), })), right: Some(SharedPointer::new(bl.clone())), }; n.remove_balance(); n }; node.remove_balance_right(); assert_eq!(node, expected_node); let mut node = Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Black, // Irrelevant left: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Red, left: Some(SharedPointer::new(dummy_node("a").make_black())), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("b"))), right: Some(SharedPointer::new(dummy_node("c"))), })), })), right: Some(SharedPointer::new(bl.clone())), }; let expected_node = Node { entry: SharedPointer::new(dummy_entry("y")), color: Color::Red, left: Some(SharedPointer::new({ let mut n = Node { entry: SharedPointer::new(dummy_entry("x")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("a").make_red())), right: Some(SharedPointer::new(dummy_node("b"))), }; n.remove_balance(); n })), right: Some(SharedPointer::new(Node { entry: SharedPointer::new(dummy_entry("z")), color: Color::Black, left: Some(SharedPointer::new(dummy_node("c"))), right: Some(SharedPointer::new(bl.clone())), })), }; node.remove_balance_right(); assert_eq!(node, expected_node); } } mod internal { use super::*; use alloc::vec::Vec; use pretty_assertions::assert_eq; fn insert_test(values: &[u32]) { let mut map = RedBlackTreeMap::new(); for (i, &v) in values.iter().enumerate() { map.insert_mut(v, 2 * v); let other_v = values[i / 2]; assert_eq!(map.get(&v), Some(&(2 * v))); assert_eq!(map.get(&other_v), Some(&(2 * other_v))); if let Err(error) = map.check_consistent() { panic!( "Consistency error in red-black tree ({:?}). Insertions: {:?}", error, &values[0..=i] ); } } } #[test] fn test_insert_sorted() { let vec: Vec = (0..4092).collect(); insert_test(&vec); } #[test] fn test_insert() { use rand::rngs::StdRng; use rand::seq::SliceRandom; use rand::SeedableRng; let limit = 25_000; let seed: [u8; 32] = [ 24, 73, 23, 5, 34, 57, 253, 46, 245, 73, 23, 155, 137, 250, 46, 46, 217, 3, 55, 157, 137, 250, 46, 46, 217, 3, 55, 157, 34, 135, 34, 123, ]; let mut rng: StdRng = SeedableRng::from_seed(seed); let mut permutation: [u32; 64] = { let mut p: [u32; 64] = [0; 64]; for i in 0..64 { p[i as usize] = i; } p }; for _ in 0..limit { permutation.shuffle(&mut rng); insert_test(&permutation); } } fn remove_test(values_insert: &[u32], values_remove: &[u32]) { let mut map = RedBlackTreeMap::new(); for &v in values_insert { map.insert_mut(v, 2 * v); } for (i, v) in values_remove.iter().enumerate() { map.remove_mut(v); assert!(!map.contains_key(v)); if let Err(error) = map.check_consistent() { panic!("Consistency error in red-black tree ({:?}). Insertions: {:?}. Removals: {:?}", error, &values_insert, &values_remove[0..=i]); } } } #[test] fn test_remove_sorted() { let vec: Vec = (0..4092).collect(); let vec_rev: Vec = (0..4092).rev().collect(); remove_test(&vec, &vec); remove_test(&vec, &vec_rev); } #[test] fn test_remove() { use rand::rngs::StdRng; use rand::seq::SliceRandom; use rand::SeedableRng; let limit = 25_000; let seed: [u8; 32] = [ 24, 73, 23, 5, 34, 57, 253, 46, 245, 73, 23, 155, 137, 250, 46, 46, 217, 3, 55, 157, 137, 250, 46, 46, 217, 3, 55, 157, 34, 135, 34, 123, ]; let mut rng: StdRng = SeedableRng::from_seed(seed); let mut permutation_insert: [u32; 64] = { let mut p: [u32; 64] = [0; 64]; for i in 0..64 { p[i as usize] = i; } p }; let mut permutation_remove: [u32; 64] = permutation_insert; for _ in 0..limit { permutation_insert.shuffle(&mut rng); permutation_remove.shuffle(&mut rng); remove_test(&permutation_insert, &permutation_remove); } } } mod iter { use super::*; use alloc::vec::Vec; use pretty_assertions::assert_eq; #[test] fn test_lg_floor() { assert_eq!(iter_utils::lg_floor(1), 0); assert_eq!(iter_utils::lg_floor(2), 1); assert_eq!(iter_utils::lg_floor(3), 1); assert_eq!(iter_utils::lg_floor(4), 2); assert_eq!(iter_utils::lg_floor(5), 2); assert_eq!(iter_utils::lg_floor(7), 2); assert_eq!(iter_utils::lg_floor(8), 3); assert_eq!(iter_utils::lg_floor(9), 3); assert_eq!(iter_utils::lg_floor(15), 3); assert_eq!(iter_utils::lg_floor(16), 4); assert_eq!(iter_utils::lg_floor(17), 4); } #[test] fn test_iter_empty() { let map: RedBlackTreeMap = RedBlackTreeMap::new(); for _ in map.iter() { panic!("iterator should be empty"); } } #[test] fn test_iter_empty_backwards() { let map: RedBlackTreeMap = RedBlackTreeMap::new(); for _ in map.iter().rev() { panic!("iterator should be empty"); } } #[test] fn test_iter_big_map() { let limit = 512; let mut map = RedBlackTreeMap::new(); let mut expected = 0; let mut left = limit; for i in (0..limit).rev() { map = map.insert(i, 2 * i); } for (k, v) in map.iter() { left -= 1; assert!(left >= 0); assert_eq!(*k, expected); assert_eq!(*v, 2 * expected); expected += 1; } assert_eq!(left, 0); } #[test] fn test_iter_big_map_backwards() { let limit = 512; let mut map = RedBlackTreeMap::new(); let mut expected = limit - 1; let mut left = limit; for i in 0..limit { map = map.insert(i, 2 * i); } for (k, v) in map.iter().rev() { left -= 1; assert!(left >= 0); assert_eq!(*k, expected); assert_eq!(*v, 2 * expected); expected -= 1; } assert_eq!(left, 0); } #[test] fn test_iter_both_directions() { let map = rbt_map![ 0 => 10, 1 => 11, 2 => 12, 3 => 13, 4 => 14, 5 => 15 ]; let mut iterator = map.iter(); assert_eq!(iterator.next(), Some((&0, &10))); assert_eq!(iterator.next_back(), Some((&5, &15))); assert_eq!(iterator.next_back(), Some((&4, &14))); assert_eq!(iterator.next(), Some((&1, &11))); assert_eq!(iterator.next(), Some((&2, &12))); assert_eq!(iterator.next_back(), Some((&3, &13))); assert_eq!(iterator.next_back(), None); assert_eq!(iterator.next(), None); } #[test] fn test_iter_size_hint() { let map = rbt_map![ 0 => 10, 1 => 11, 2 => 12 ]; let mut iterator = map.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next_back(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next_back(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_iter_sorted() { let map = rbt_map![ 5 => (), 6 => (), 2 => (), 1 => () ]; let mut iterator = map.iter(); assert_eq!(iterator.next(), Some((&1, &()))); assert_eq!(iterator.next(), Some((&2, &()))); assert_eq!(iterator.next(), Some((&5, &()))); assert_eq!(iterator.next(), Some((&6, &()))); assert_eq!(iterator.next(), None); } #[test] fn test_iter_keys() { let map = rbt_map![ 0 => 10, 1 => 11, 2 => 12 ]; let mut iter = map.keys(); assert_eq!(iter.next(), Some(&0)); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), None); } #[test] fn test_iter_values() { let map = rbt_map![ 10 => 0, 11 => 1, 12 => 2 ]; let mut iter = map.values(); assert_eq!(iter.next(), Some(&0)); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), None); } #[test] fn test_into_iterator() { let map = rbt_map![ 0 => 0, 1 => 2, 2 => 4, 3 => 6 ]; let mut left = 4; for (expected, (k, v)) in map.into_iter().enumerate() { left -= 1; assert!(left >= 0); assert_eq!(*k, expected); assert_eq!(*v, 2 * expected); } assert_eq!(left, 0); } #[test] fn test_range() { use core::ops::Bound::*; fn cmp + Clone>( map: &RedBlackTreeMap, range: RB, expected: &[(i32, i32)], ) { assert_eq!( map.range(range.clone()).map(|(k, v)| (*k, *v)).collect::>(), expected ); assert_eq!( map.range(range).rev().map(|(k, v)| (*k, *v)).collect::>(), expected.iter().copied().rev().collect::>() ); } let map = rbt_map![ 0 => 0, 1 => 2, 2 => 4, 3 => 6 ]; cmp(&map, .., &[(0, 0), (1, 2), (2, 4), (3, 6)]); cmp(&map, 1.., &[(1, 2), (2, 4), (3, 6)]); cmp(&map, 1..3, &[(1, 2), (2, 4)]); cmp(&map, 1..=3, &[(1, 2), (2, 4), (3, 6)]); cmp(&map, ..3, &[(0, 0), (1, 2), (2, 4)]); cmp(&map, (Excluded(1), Included(3)), &[(2, 4), (3, 6)]); } #[test] fn test_range_empty() { let map = rbt_map![ 0 => 0, 1 => 2, 10 => 20, 11 => 22 ]; assert_eq!(map.range(1..1).next(), None); assert_eq!(map.range(3..10).next(), None); assert_eq!(map.range(..0).next(), None); assert_eq!(map.range(3..=9).next(), None); assert_eq!(map.range(13..).next(), None); } } #[test] fn test_macro_rbt_map() { let set_1 = RedBlackTreeMap::new().insert(1, 2); let set_1_2_3 = RedBlackTreeMap::new().insert(1, 2).insert(2, 3).insert(3, 4); assert_eq!(RedBlackTreeMap::::new(), rbt_map![]); assert_eq!(set_1, rbt_map![1 => 2]); assert_eq!(set_1_2_3, rbt_map![1 => 2, 2 => 3, 3 => 4]); } #[test] fn test_get_key_value() { let mut map = RedBlackTreeMap::new(); map = map.insert("foo", 4); assert_eq!(map.get_key_value("foo"), Some((&"foo", &4))); map = map.insert("bar", 2); assert_eq!(map.get_key_value("foo"), Some((&"foo", &4))); assert_eq!(map.get_key_value("bar"), Some((&"bar", &2))); } #[test] fn test_insert_simple() { let mut map = RedBlackTreeMap::new(); assert_eq!(map.size(), 0); map = map.insert("foo", 4); assert_eq!(map.size(), 1); assert_eq!(map.get("foo"), Some(&4)); map = map.insert("bar", 2); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); map = map.insert("baz", 12); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); map = map.insert("foo", 7); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&7)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); assert!(map.contains_key("baz")); } #[test] fn test_insert() { let mut map = RedBlackTreeMap::new(); // These are relatively small limits. We prefer to do a more hardcore test in the mutable // version. let limit = 5_000; let overwrite_limit = 1_000; for i in 0..limit { map = map.insert(i, -i); assert_eq!(map.size(), (i as usize) + 1); assert_eq!(map.get(&i), Some(&-i)); // Lets also check a previous value. let prev_key = i / 2; assert_eq!(map.get(&prev_key), Some(&-prev_key)); } // Now we test some overwrites. for i in 0..overwrite_limit { assert_eq!(map.get(&i), Some(&-i)); map = map.insert(i, 2 * i); assert_eq!(map.size(), limit as usize); assert_eq!(map.get(&i), Some(&(2 * i))); } } #[test] fn test_insert_mut_simple() { let mut map = RedBlackTreeMap::new(); assert_eq!(map.size(), 0); map.insert_mut("foo", 4); assert_eq!(map.size(), 1); assert_eq!(map.get("foo"), Some(&4)); map.insert_mut("bar", 2); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); map.insert_mut("baz", 12); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); map.insert_mut("foo", 7); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&7)); assert_eq!(map.get("bar"), Some(&2)); assert_eq!(map.get("baz"), Some(&12)); assert!(map.contains_key("baz")); } #[test] fn test_insert_mut() { let mut map = RedBlackTreeMap::new(); let limit = 25_000; let overwrite_limit = 5_000; for i in 0..limit { map.insert_mut(i, -i); assert_eq!(map.size(), (i as usize) + 1); assert_eq!(map.get(&i), Some(&-i)); // Lets also check a previous value. let prev_key = i / 2; assert_eq!(map.get(&prev_key), Some(&-prev_key)); } // Now we test some overwrites. for i in 0..overwrite_limit { assert_eq!(map.get(&i), Some(&-i)); map.insert_mut(i, 2 * i); assert_eq!(map.size(), limit as usize); assert_eq!(map.get(&i), Some(&(2 * i))); } } #[test] fn test_contains_key() { let map = rbt_map!["foo" => 7]; assert!(map.contains_key("foo")); assert!(!map.contains_key("baz")); } #[test] fn test_remove_simple() { let mut map = rbt_map![ "foo" => 4, "bar" => 12, "mumble" => 13, "baz" => 42 ]; let empty_map: RedBlackTreeMap = RedBlackTreeMap::new(); assert_eq!(empty_map.remove(&3), empty_map); assert_eq!(map.size(), 4); map = map.remove("not-there"); assert_eq!(map.size(), 4); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), Some(&13)); assert_eq!(map.get("baz"), Some(&42)); map = map.remove("mumble"); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), None); assert_eq!(map.get("baz"), Some(&42)); map = map.remove("foo"); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), None); map = map.remove("baz"); assert_eq!(map.size(), 1); assert_eq!(map.get("baz"), None); map = map.remove("bar"); assert_eq!(map.size(), 0); assert_eq!(map.get("bar"), None); } #[test] fn test_remove() { let mut map = RedBlackTreeMap::new(); // These are relatively small limits. We prefer to do a more hardcore test in the mutable // version. let limit = 5_000; for i in 0..limit { map = map.insert(i, -i); } // Now lets remove half of it. for i in (0..limit / 2).map(|i| 2 * i) { assert_eq!(map.get(&i), Some(&-i)); map = map.remove(&i); assert!(!map.contains_key(&i)); assert_eq!(map.size(), (limit - i / 2 - 1) as usize); // Also check than the previous one is ok. if i > 0 { assert_eq!(map.get(&(i - 1)), Some(&-(i - 1))); } } } #[test] fn test_remove_mut_simple() { let mut map = rbt_map![ "foo" => 4, "bar" => 12, "mumble" => 13, "baz" => 42 ]; assert_eq!(map.size(), 4); assert!(!map.remove_mut("not-there")); assert_eq!(map.size(), 4); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), Some(&13)); assert_eq!(map.get("baz"), Some(&42)); assert!(map.remove_mut("mumble")); assert_eq!(map.size(), 3); assert_eq!(map.get("foo"), Some(&4)); assert_eq!(map.get("bar"), Some(&12)); assert_eq!(map.get("mumble"), None); assert_eq!(map.get("baz"), Some(&42)); assert!(map.remove_mut("foo")); assert_eq!(map.size(), 2); assert_eq!(map.get("foo"), None); assert!(map.remove_mut("baz")); assert_eq!(map.size(), 1); assert_eq!(map.get("baz"), None); assert!(map.remove_mut("bar")); assert_eq!(map.size(), 0); assert_eq!(map.get("bar"), None); } #[test] fn test_remove_mut() { let mut map = RedBlackTreeMap::new(); let limit = 25_000; for i in 0..limit { map.insert_mut(i, -i); } // Now lets remove half of it. for i in (0..limit / 2).map(|i| 2 * i) { assert_eq!(map.get(&i), Some(&-i)); map.remove_mut(&i); assert!(!map.contains_key(&i)); assert_eq!(map.size(), (limit - i / 2 - 1) as usize); // Also check than the previous one is ok. if i > 0 { assert_eq!(map.get(&(i - 1)), Some(&-(i - 1))); } } } #[test] fn test_first() { let map = rbt_map![5 => "hello", 12 => "there"]; assert_eq!(map.first(), Some((&5, &"hello"))); } #[test] fn test_last() { let map = rbt_map![5 => "hello", 12 => "there"]; assert_eq!(map.last(), Some((&12, &"there"))); } #[test] fn test_index() { let map = rbt_map![5 => "hello", 12 => "there"]; assert_eq!(map[&5], "hello"); assert_eq!(map[&12], "there"); } #[test] fn test_from_iterator() { let vec: Vec<(i32, &str)> = vec![(2, "two"), (5, "five")]; let map: RedBlackTreeMap = vec.iter().copied().collect(); let expected_map = rbt_map![2 => "two", 5 => "five"]; assert_eq!(map, expected_map); } #[test] fn test_default() { let map: RedBlackTreeMap = RedBlackTreeMap::default(); assert_eq!(map.size(), 0); assert!(map.is_empty()); } #[test] fn test_display() { let empty_map: RedBlackTreeMap = RedBlackTreeMap::new(); let singleton_map = rbt_map!["hi" => "hello"]; let map = rbt_map![5 => "hello", 12 => "there"]; assert_eq!(format!("{}", empty_map), "{}"); assert_eq!(format!("{}", singleton_map), "{hi: hello}"); assert_eq!(format!("{}", map), "{5: hello, 12: there}"); } #[test] fn test_eq() { let map_1 = rbt_map!["a" => 0xa, "b" => 0xb]; let map_1_prime = rbt_map!["a" => 0xa, "b" => 0xb]; let map_1_prime_2 = rbt_map!["a" => 0xa, "b" => 0xb, "b" => 0xb]; let map_2 = rbt_map!["a" => 0xa, "b" => 0xb + 1]; let map_3 = rbt_map!["a" => 0xa, "b" => 0xb + 1, "c" => 0xc]; assert_eq!(map_1, map_1_prime); assert_eq!(map_1, map_1_prime_2); assert_eq!(map_1, map_1); assert_eq!(map_2, map_2); // We also check this since `assert_ne!()` does not call `ne`. assert!(map_1.ne(&map_2)); assert!(map_2.ne(&map_3)); } #[test] fn test_eq_pointer_kind_consistent() { let map_a = rbt_map!["a" => 0]; let map_a_sync = rbt_map_sync!["a" => 0]; let map_b = rbt_map!["b" => 1]; let map_b_sync = rbt_map_sync!["b" => 1]; assert!(map_a == map_a_sync); assert!(map_a != map_b_sync); assert!(map_b == map_b_sync); } #[test] fn test_partial_ord() { let map_1 = rbt_map!["a" => 0xa]; let map_1_prime = rbt_map!["a" => 0xa]; let map_2 = rbt_map!["b" => 0xb]; let map_3 = rbt_map![0 => 0.0]; let map_4 = rbt_map![0 => core::f32::NAN]; assert_eq!(map_1.partial_cmp(&map_1_prime), Some(Ordering::Equal)); assert_eq!(map_1.partial_cmp(&map_2), Some(Ordering::Less)); assert_eq!(map_2.partial_cmp(&map_1), Some(Ordering::Greater)); assert_eq!(map_3.partial_cmp(&map_4), None); } #[test] fn test_ord() { let map_1 = rbt_map!["a" => 0xa]; let map_1_prime = rbt_map!["a" => 0xa]; let map_2 = rbt_map!["b" => 0xb]; assert_eq!(map_1.cmp(&map_1_prime), Ordering::Equal); assert_eq!(map_1.cmp(&map_2), Ordering::Less); assert_eq!(map_2.cmp(&map_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let map_a = rbt_map!["a" => 0]; let map_a_sync = rbt_map_sync!["a" => 0]; let map_b = rbt_map!["b" => 1]; let map_b_sync = rbt_map_sync!["b" => 1]; assert!(map_a <= map_a_sync); assert!(map_a < map_b_sync); assert!(map_b >= map_b_sync); assert!(map_a_sync >= map_a); assert!(map_b_sync > map_a); assert!(map_b_sync <= map_b); } fn hash(map: &RedBlackTreeMap) -> u64 where P: SharedPointerKind, { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); map.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let map_1 = rbt_map!["a" => 0xa]; let map_1_prime = rbt_map!["a" => 0xa]; let map_2 = rbt_map!["b" => 0xb, "a" => 0xa]; assert_eq!(hash(&map_1), hash(&map_1)); assert_eq!(hash(&map_1), hash(&map_1_prime)); assert_ne!(hash(&map_1), hash(&map_2)); } #[test] fn test_hash_pointer_kind_consistent() { let map = rbt_map!["a" => 0]; let map_sync = rbt_map_sync!["a" => 0]; assert_eq!(hash(&map), hash(&map_sync)); } #[test] fn test_clone() { let map = rbt_map!["hello" => 4, "there" => 5]; let clone = map.clone(); assert_eq!(clone.size(), map.size()); assert_eq!(clone.get("hello"), Some(&4)); assert_eq!(clone.get("there"), Some(&5)); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let map: RedBlackTreeMap = rbt_map![5 => 6, 7 => 8, 9 => 10, 11 => 12]; let encoded = serialize(&map).unwrap(); let decoded: RedBlackTreeMap = deserialize(&encoded).unwrap(); assert_eq!(map, decoded); } rpds-1.1.0/src/queue/mod.rs000064400000000000000000000235701046102023000136420ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::List; use alloc::vec::Vec; use archery::*; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt::Display; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; // TODO Use impl trait instead of this when available. type IterPtr<'a, T, P> = core::iter::Chain, LazilyReversedListIter<'a, T, P>>; pub type Iter<'a, T, P> = core::iter::Map, fn(&SharedPointer) -> &T>; /// Creates a [`Queue`](crate::Queue) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let q = Queue::new() /// .enqueue(1) /// .enqueue(2) /// .enqueue(3); /// /// assert_eq!(queue![1, 2, 3], q); /// ``` #[macro_export] macro_rules! queue { ($($e:expr),*) => { { #[allow(unused_mut)] let mut q = $crate::Queue::new(); $( q.enqueue_mut($e); )* q } }; } /// Creates a [`Queue`](crate::Queue) that implements `Sync`, containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let q = Queue::new_sync() /// .enqueue(1) /// .enqueue(2) /// .enqueue(3); /// /// assert_eq!(queue_sync![1, 2, 3], q); /// /// fn is_sync() -> impl Sync { /// queue_sync![0, 1, 3] /// } /// ``` #[macro_export] macro_rules! queue_sync { ($($e:expr),*) => { { #[allow(unused_mut)] let mut q = $crate::Queue::new_sync(); $( q.enqueue_mut($e); )* q } }; } /// A persistent queue with structural sharing. /// /// # Complexity /// /// Let *n* be the number of elements in the queue. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:--------------------- | -------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `enqueue()` | Θ(1) | Θ(1) | /// | `dequeue()` | Θ(1) | Θ(n) | /// | `dequeue()` amortized | Θ(1) | Θ(1) | /// | `peek()` | Θ(1) | Θ(1) | /// | `len()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(n) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This queue is implemented as described in /// [Immutability in C# Part Four: An Immutable Queue](https://goo.gl/hWyMuS). #[derive(Debug)] pub struct Queue where P: SharedPointerKind, { in_list: List, out_list: List, } pub type QueueSync = Queue; impl QueueSync { #[must_use] pub fn new_sync() -> QueueSync { Queue::new_with_ptr_kind() } } impl Queue { #[must_use] pub fn new() -> Queue { Queue::new_with_ptr_kind() } } impl Queue where P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> Queue { Queue { in_list: List::new_with_ptr_kind(), out_list: List::new_with_ptr_kind() } } #[must_use] pub fn peek(&self) -> Option<&T> { if !self.out_list.is_empty() { self.out_list.first() } else { self.in_list.last() } } #[must_use] pub fn dequeue(&self) -> Option> { let mut new_queue = self.clone(); if new_queue.dequeue_mut() { Some(new_queue) } else { None } } pub fn dequeue_mut(&mut self) -> bool { if !self.out_list.is_empty() { self.out_list.drop_first_mut(); true } else if !self.in_list.is_empty() { core::mem::swap(&mut self.in_list, &mut self.out_list); self.out_list.reverse_mut(); self.out_list.drop_first_mut(); true } else { false } } #[must_use] pub fn enqueue(&self, v: T) -> Queue { let mut new_queue = self.clone(); new_queue.enqueue_mut(v); new_queue } pub fn enqueue_mut(&mut self, v: T) { self.in_list.push_front_mut(v); } #[must_use] #[inline] pub fn len(&self) -> usize { self.in_list.len() + self.out_list.len() } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn iter(&self) -> Iter<'_, T, P> { self.iter_ptr().map(|v| v.borrow()) } fn iter_ptr(&self) -> IterPtr<'_, T, P> { self.out_list.iter_ptr().chain(LazilyReversedListIter::new(&self.in_list)) } } impl Default for Queue where P: SharedPointerKind, { fn default() -> Queue { Queue::new_with_ptr_kind() } } impl PartialEq> for Queue where P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &Queue) -> bool { self.len() == other.len() && self.iter().eq(other.iter()) } } impl Eq for Queue where P: SharedPointerKind {} impl, P, PO> PartialOrd> for Queue where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &Queue) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for Queue where P: SharedPointerKind, { fn cmp(&self, other: &Queue) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for Queue where P: SharedPointerKind, { fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it // doesn't hash to the same thing as a single collection with the same elements in the same // order. self.len().hash(state); for e in self { e.hash(state); } } } impl Clone for Queue where P: SharedPointerKind, { fn clone(&self) -> Queue { Queue { in_list: self.in_list.clone(), out_list: self.out_list.clone() } } } impl Display for Queue where P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("Queue(")?; for v in self { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str(")") } } impl<'a, T, P> IntoIterator for &'a Queue where P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } impl FromIterator for Queue where P: SharedPointerKind, { fn from_iter>(into_iter: I) -> Queue { Queue { out_list: List::from_iter(into_iter), in_list: List::new_with_ptr_kind() } } } pub enum LazilyReversedListIter<'a, T: 'a, P> where P: SharedPointerKind, { Uninitialized { list: &'a List }, Initialized { vec: Vec<&'a SharedPointer>, current: Option }, } impl<'a, T, P> LazilyReversedListIter<'a, T, P> where P: SharedPointerKind, { fn new(list: &List) -> LazilyReversedListIter<'_, T, P> { LazilyReversedListIter::Uninitialized { list } } } impl<'a, T, P> Iterator for LazilyReversedListIter<'a, T, P> where P: SharedPointerKind, { type Item = &'a SharedPointer; fn next(&mut self) -> Option<&'a SharedPointer> { match self { LazilyReversedListIter::Uninitialized { list } => { let len = list.len(); let mut vec: Vec<&'a SharedPointer> = Vec::with_capacity(len); for v in list.iter_ptr() { vec.push(v); } *self = LazilyReversedListIter::Initialized { vec, current: if len > 0 { Some(len - 1) } else { None }, }; self.next() } LazilyReversedListIter::Initialized { ref vec, ref mut current } => { let v = current.map(|i| vec[i]); *current = match *current { Some(0) => None, Some(i) => Some(i - 1), None => None, }; v } } } fn size_hint(&self) -> (usize, Option) { let len = match self { LazilyReversedListIter::Uninitialized { list } => list.len(), LazilyReversedListIter::Initialized { current: Some(i), .. } => i + 1, LazilyReversedListIter::Initialized { current: None, .. } => 0, }; (len, Some(len)) } } impl<'a, T, P> ExactSizeIterator for LazilyReversedListIter<'a, T, P> where P: SharedPointerKind {} #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer}; use ::serde::ser::{Serialize, Serializer}; impl Serialize for Queue where T: Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } impl<'de, T, P> Deserialize<'de> for Queue where T: Deserialize<'de>, P: SharedPointerKind, { fn deserialize>(deserializer: D) -> Result, D::Error> { Deserialize::deserialize(deserializer) .map(|list| Queue { out_list: list, in_list: List::new_with_ptr_kind() }) } } } #[cfg(test)] mod test; rpds-1.1.0/src/queue/test.rs000064400000000000000000000267661046102023000140540ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(QueueSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_queue_sync_is_send_and_sync() -> impl Send + Sync { queue_sync!(0) } mod lazily_reversed_list_iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter() { let list = list![0, 1, 2]; let mut iterator = LazilyReversedListIter::new(&list); assert_eq!(iterator.next().map(|v| **v), Some(2)); assert_eq!(iterator.next().map(|v| **v), Some(1)); assert_eq!(iterator.next().map(|v| **v), Some(0)); assert_eq!(iterator.next(), None); let empty_list: List = List::new(); let mut iterator = LazilyReversedListIter::new(&empty_list); assert_eq!(iterator.next(), None); } #[test] fn test_iter_size_hint() { let list = list![0, 1, 2]; let mut iterator = LazilyReversedListIter::new(&list); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); let empty_list: List = List::new(); let iterator = LazilyReversedListIter::new(&empty_list); assert_eq!(iterator.size_hint(), (0, Some(0))); } } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter() { let mut queue = Queue::new(); queue.enqueue_mut(0); queue.enqueue_mut(1); queue.dequeue_mut(); queue.enqueue_mut(2); queue.enqueue_mut(3); let mut iterator = queue.iter(); assert_eq!(iterator.next(), Some(&1)); assert_eq!(iterator.next(), Some(&2)); assert_eq!(iterator.next(), Some(&3)); assert_eq!(iterator.next(), None); } #[test] fn test_iter_size_hint() { let mut queue = Queue::new(); queue.enqueue_mut(0); queue.enqueue_mut(1); queue.dequeue_mut(); queue.enqueue_mut(2); queue.enqueue_mut(3); let mut iterator = queue.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_into_iterator() { let mut queue = Queue::new(); queue.enqueue_mut(0); queue.enqueue_mut(1); queue.dequeue_mut(); queue.enqueue_mut(2); queue.enqueue_mut(3); let mut expected = 1; let mut left = 3; for n in &queue { left -= 1; assert!(left >= 0); assert_eq!(*n, expected); expected += 1; } assert_eq!(left, 0); } } mod internal { use super::*; use pretty_assertions::assert_eq; #[test] fn test_enqueue_dequeue_mut() { let queue = queue![0, 1, 2, 3]; let mut queue_2 = Queue::new(); queue_2.enqueue_mut(0); queue_2.enqueue_mut(1); queue_2.dequeue_mut(); queue_2.enqueue_mut(2); queue_2.enqueue_mut(3); let mut queue_3 = Queue::new(); queue_3.enqueue_mut(0); queue_3.enqueue_mut(1); queue_3.enqueue_mut(2); queue_3.enqueue_mut(3); queue_3.dequeue_mut(); let list_3_2_1_0 = list![3, 2, 1, 0]; let list_3_2 = list![3, 2]; let list_1 = list![1]; let list_1_2_3 = list![1, 2, 3]; assert!(queue.out_list.is_empty()); assert_eq!(queue.in_list, list_3_2_1_0); assert_eq!(queue_2.out_list, list_1); assert_eq!(queue_2.in_list, list_3_2); assert_eq!(queue_3.out_list, list_1_2_3); assert!(queue_3.in_list.is_empty()); } } #[test] fn test_new() { let empty_queue: Queue = Queue::new(); assert!(empty_queue.in_list.is_empty()); assert!(empty_queue.out_list.is_empty()); assert_eq!(empty_queue.len(), 0); assert!(empty_queue.is_empty()); } #[test] fn test_macro_queue() { let mut queue_1 = Queue::new(); queue_1.enqueue_mut(1); let mut queue_1_2_3 = Queue::new(); queue_1_2_3.enqueue_mut(1); queue_1_2_3.enqueue_mut(2); queue_1_2_3.enqueue_mut(3); assert_eq!(Queue::::new(), queue![]); assert_eq!(queue_1, queue![1]); assert_eq!(queue_1_2_3, queue![1, 2, 3]); } #[test] fn test_peek() { let empty_queue: Queue = Queue::new(); let singleton_queue = queue!["hello"]; let queue = queue![0, 1, 2, 3]; let mut queue_2 = Queue::new(); queue_2.enqueue_mut(0); queue_2.enqueue_mut(1); queue_2.dequeue_mut(); queue_2.enqueue_mut(2); queue_2.enqueue_mut(3); let mut queue_3 = Queue::new(); queue_3.enqueue_mut(0); queue_3.enqueue_mut(1); queue_3.enqueue_mut(2); queue_3.enqueue_mut(3); queue_3.dequeue_mut(); assert_eq!(empty_queue.peek(), None); assert_eq!(singleton_queue.peek(), Some(&"hello")); assert_eq!(queue.peek(), Some(&0)); assert_eq!(queue_2.peek(), Some(&1)); assert_eq!(queue_3.peek(), Some(&1)); } #[test] fn test_dequeue_mut() { let mut empty_queue: Queue = Queue::new(); let mut singleton_queue = queue!["hello"]; let mut queue = queue![0, 1, 2, 3]; let mut queue_2 = Queue::new(); queue_2.enqueue_mut(0); queue_2.enqueue_mut(1); queue_2.dequeue_mut(); queue_2.enqueue_mut(2); queue_2.enqueue_mut(3); let mut queue_3 = Queue::new(); queue_3.enqueue_mut(0); queue_3.enqueue_mut(1); queue_3.enqueue_mut(2); queue_3.enqueue_mut(3); queue_3.dequeue_mut(); empty_queue.dequeue_mut(); assert!(empty_queue.is_empty()); singleton_queue.dequeue_mut(); assert_eq!(singleton_queue.peek(), None); queue.dequeue_mut(); assert_eq!(queue.peek(), Some(&1)); queue_2.dequeue_mut(); assert_eq!(queue_2.peek(), Some(&2)); queue_3.dequeue_mut(); assert_eq!(queue_3.peek(), Some(&2)); } #[test] fn test_dequeue_mut_maintains_len() { let mut queue = queue![0, 1, 2, 3]; let mut queue_2 = Queue::new(); queue_2.enqueue_mut(0); queue_2.enqueue_mut(1); queue_2.dequeue_mut(); queue_2.enqueue_mut(2); queue_2.enqueue_mut(3); let mut queue_3 = Queue::new(); queue_3.enqueue_mut(0); queue_3.enqueue_mut(1); queue_3.enqueue_mut(2); queue_3.enqueue_mut(3); queue_3.dequeue_mut(); assert_eq!(queue.len(), 4); queue.dequeue_mut(); assert_eq!(queue.len(), 3); assert_eq!(queue_2.len(), 3); queue_2.dequeue_mut(); assert_eq!(queue_2.len(), 2); assert_eq!(queue_3.len(), 3); queue_3.dequeue_mut(); assert_eq!(queue_3.len(), 2); } #[test] fn test_dequeue() { let empty_queue: Queue = Queue::new(); let singleton_queue = Queue::new().enqueue("hello"); let queue = Queue::new().enqueue(0).enqueue(1).enqueue(2).enqueue(3); let queue_2 = Queue::new().enqueue(0).enqueue(1).dequeue().unwrap().enqueue(2).enqueue(3); let queue_3 = Queue::new().enqueue(0).enqueue(1).enqueue(2).enqueue(3).dequeue().unwrap(); assert!(empty_queue.dequeue().is_none()); assert_eq!(singleton_queue.dequeue().unwrap().peek(), None); assert_eq!(queue.dequeue().unwrap().peek(), Some(&1)); assert_eq!(queue_2.dequeue().unwrap().peek(), Some(&2)); assert_eq!(queue_3.dequeue().unwrap().peek(), Some(&2)); assert_eq!(queue.len(), 4); assert_eq!(queue.dequeue().unwrap().len(), 3); assert_eq!(queue_2.len(), 3); assert_eq!(queue_2.dequeue().unwrap().len(), 2); assert_eq!(queue_3.len(), 3); assert_eq!(queue_3.dequeue().unwrap().len(), 2); } #[test] fn test_from_iterator() { let vec: Vec = vec![10, 11, 12, 13]; let queue: Queue = vec.iter().copied().collect(); assert!(vec.iter().eq(queue.iter())); } #[test] fn test_default() { let queue: Queue = Queue::default(); assert_eq!(queue.peek(), None); assert!(queue.is_empty()); } #[test] fn test_display() { let empty_queue: Queue = Queue::new(); let singleton_queue = queue!["hello"]; let queue = queue![0, 1, 2, 3]; assert_eq!(format!("{}", empty_queue), "Queue()"); assert_eq!(format!("{}", singleton_queue), "Queue(hello)"); assert_eq!(format!("{}", queue), "Queue(0, 1, 2, 3)"); } #[test] fn test_eq() { let queue_1 = queue!["a", "a"]; let queue_1_prime = queue!["a", "a"]; let queue_2 = queue!["a", "b"]; assert_ne!(queue_1, queue_2); assert_eq!(queue_1, queue_1); assert_eq!(queue_1, queue_1_prime); assert_eq!(queue_2, queue_2); } #[test] fn test_eq_pointer_kind_consistent() { let queue_a = queue!["a"]; let queue_a_sync = queue_sync!["a"]; let queue_b = queue!["b"]; let queue_b_sync = queue_sync!["b"]; assert!(queue_a == queue_a_sync); assert!(queue_a != queue_b_sync); assert!(queue_b == queue_b_sync); } #[test] fn test_partial_ord() { let queue_1 = queue!["a"]; let queue_1_prime = queue!["a"]; let queue_2 = queue!["b"]; let queue_3 = queue![0.0]; let queue_4 = queue![core::f32::NAN]; assert_eq!(queue_1.partial_cmp(&queue_1_prime), Some(Ordering::Equal)); assert_eq!(queue_1.partial_cmp(&queue_2), Some(Ordering::Less)); assert_eq!(queue_2.partial_cmp(&queue_1), Some(Ordering::Greater)); assert_eq!(queue_3.partial_cmp(&queue_4), None); } #[test] fn test_ord() { let queue_1 = queue!["a"]; let queue_1_prime = queue!["a"]; let queue_2 = queue!["b"]; assert_eq!(queue_1.cmp(&queue_1_prime), Ordering::Equal); assert_eq!(queue_1.cmp(&queue_2), Ordering::Less); assert_eq!(queue_2.cmp(&queue_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let queue_a = queue!["a"]; let queue_a_sync = queue_sync!["a"]; let queue_b = queue!["b"]; let queue_b_sync = queue_sync!["b"]; assert!(queue_a <= queue_a_sync); assert!(queue_a < queue_b_sync); assert!(queue_b >= queue_b_sync); assert!(queue_a_sync >= queue_a); assert!(queue_b_sync > queue_a); assert!(queue_b_sync <= queue_b); } fn hash(queue: &Queue) -> u64 { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); queue.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let queue_1 = queue!["a"]; let queue_1_prime = queue!["a"]; let queue_2 = queue!["a", "b"]; assert_eq!(hash(&queue_1), hash(&queue_1)); assert_eq!(hash(&queue_1), hash(&queue_1_prime)); assert_ne!(hash(&queue_1), hash(&queue_2)); } #[test] fn test_hash_pointer_kind_consistent() { let queue = queue!["a"]; let queue_sync = queue_sync!["a"]; assert_eq!(hash(&queue), hash(&queue_sync)); } #[test] fn test_clone() { let queue = queue!["hello", "there"]; let clone = queue.clone(); assert!(clone.iter().eq(queue.iter())); assert_eq!(clone.len(), queue.len()); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let queue: Queue = queue![5, 6, 7, 8]; let encoded = serialize(&queue).unwrap(); let decoded: Queue = deserialize(&encoded).unwrap(); assert_eq!(queue, decoded); } rpds-1.1.0/src/set/hash_trie_set/mod.rs000064400000000000000000000246471046102023000161400ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::map::hash_trie_map; use crate::utils::DefaultBuildHasher; use crate::HashTrieMap; use archery::{ArcTK, RcK, SharedPointerKind}; use core::borrow::Borrow; use core::fmt::Display; use core::hash::BuildHasher; use core::hash::Hash; use core::iter::FromIterator; // TODO Use impl trait instead of this when available. pub type Iter<'a, T, P> = hash_trie_map::IterKeys<'a, T, (), P>; /// Creates a [`HashTrieSet`](crate::HashTrieSet) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let s = HashTrieSet::new() /// .insert(1) /// .insert(2) /// .insert(3); /// /// assert_eq!(ht_set![1, 2, 3], s); /// ``` #[macro_export] macro_rules! ht_set { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::HashTrieSet::new(); $( s.insert_mut($e); )* s } }; } /// Creates a [`HashTrieSet`](crate::HashTrieSet) that implements `Sync`, containing the given /// arguments: /// /// ``` /// # use rpds::*; /// # /// let s = HashTrieSet::new_sync() /// .insert(1) /// .insert(2) /// .insert(3); /// /// assert_eq!(ht_set_sync![1, 2, 3], s); /// ``` #[macro_export] macro_rules! ht_set_sync { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::HashTrieSet::new_sync(); $( s.insert_mut($e); )* s } }; } /// A persistent set with structural sharing. This implementation uses a /// [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie). /// /// # Complexity /// /// Let *n* be the number of elements in the set. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:----------------- | -------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `insert()` | Θ(1) | Θ(n) | /// | `remove()` | Θ(1) | Θ(n) | /// | `get()` | Θ(1) | Θ(n) | /// | `contains()` | Θ(1) | Θ(n) | /// | `size()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(1) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This is a thin wrapper around a [`HashTrieMap`](crate::HashTrieMap). #[derive(Debug)] pub struct HashTrieSet where T: Eq + Hash, H: Clone, P: SharedPointerKind, { map: HashTrieMap, } pub type HashTrieSetSync = HashTrieSet; impl HashTrieSet where T: Eq + Hash, { #[must_use] pub fn new() -> HashTrieSet { HashTrieSet { map: HashTrieMap::new_with_hasher_and_ptr_kind(DefaultBuildHasher::default()), } } #[must_use] pub fn new_with_degree(degree: u8) -> HashTrieSet { HashTrieSet::new_with_hasher_and_degree_and_ptr_kind(DefaultBuildHasher::default(), degree) } } impl HashTrieSetSync where T: Eq + Hash, { #[must_use] pub fn new_sync() -> HashTrieSetSync { HashTrieSet { map: HashTrieMap::new_with_hasher_and_ptr_kind(DefaultBuildHasher::default()), } } #[must_use] pub fn new_with_degree_sync(degree: u8) -> HashTrieSetSync { HashTrieSet::new_with_hasher_and_degree_and_ptr_kind(DefaultBuildHasher::default(), degree) } } impl HashTrieSet where T: Eq + Hash, H: Clone, P: SharedPointerKind, { #[must_use] pub fn new_with_hasher_with_ptr_kind(hasher_builder: H) -> HashTrieSet { HashTrieSet { map: HashTrieMap::new_with_hasher_and_ptr_kind(hasher_builder) } } #[must_use] pub fn new_with_hasher_and_degree_and_ptr_kind( hasher_builder: H, degree: u8, ) -> HashTrieSet { HashTrieSet { map: HashTrieMap::new_with_hasher_and_degree_and_ptr_kind(hasher_builder, degree), } } #[must_use] pub fn insert(&self, v: T) -> HashTrieSet { HashTrieSet { map: self.map.insert(v, ()) } } pub fn insert_mut(&mut self, v: T) { self.map.insert_mut(v, ()); } #[must_use] pub fn remove(&self, v: &V) -> HashTrieSet where T: Borrow, V: Hash + Eq, { HashTrieSet { map: self.map.remove(v) } } pub fn remove_mut(&mut self, v: &V) -> bool where T: Borrow, V: Hash + Eq, { self.map.remove_mut(v) } #[must_use] pub fn get(&self, v: &V) -> Option<&T> where T: Borrow, V: Hash + Eq, { self.map.get_key_value(v).map(|(k, _)| k) } #[must_use] pub fn contains(&self, v: &V) -> bool where T: Borrow, V: Hash + Eq, { self.map.contains_key(v) } #[must_use] pub fn is_disjoint(&self, other: &HashTrieSet) -> bool { self.iter().all(|v| !other.contains(v)) } /// Test whether the two sets refer to the same content in memory. /// /// This would return true if you’re comparing a set to itself, /// or if you’re comparing a set to a fresh clone of itself. fn ptr_eq( &self, other: &HashTrieSet, ) -> bool { self.map.ptr_eq(&other.map) } #[must_use] pub fn is_subset(&self, other: &HashTrieSet) -> bool { if self.ptr_eq(other) { return true; } self.size() <= other.size() && self.iter().all(|v| other.contains(v)) } #[must_use] pub fn is_superset(&self, other: &HashTrieSet) -> bool { other.is_subset(self) } #[must_use] #[inline] pub fn size(&self) -> usize { self.map.size() } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.size() == 0 } pub fn iter(&self) -> Iter<'_, T, P> { self.map.keys() } } impl Clone for HashTrieSet where T: Eq + Hash, H: Clone, P: SharedPointerKind, { fn clone(&self) -> HashTrieSet { HashTrieSet { map: self.map.clone() } } } impl Default for HashTrieSet where T: Eq + Hash, H: Default + Clone, P: SharedPointerKind, { fn default() -> HashTrieSet { HashTrieSet::new_with_hasher_with_ptr_kind(H::default()) } } impl PartialEq> for HashTrieSet where T: Hash, H: Clone, P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &HashTrieSet) -> bool { self.map.eq(&other.map) } } impl Eq for HashTrieSet where T: Hash, H: Clone, P: SharedPointerKind, { } impl Display for HashTrieSet where T: Eq + Hash + Display, H: Clone, P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("{")?; for v in self.iter() { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str("}") } } impl<'a, T, P, H: BuildHasher> IntoIterator for &'a HashTrieSet where T: Eq + Hash, H: Default + Clone, P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } impl FromIterator for HashTrieSet where T: Eq + Hash, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn from_iter>(into_iter: I) -> HashTrieSet { let mut set = HashTrieSet::new_with_hasher_with_ptr_kind(Default::default()); for v in into_iter { set.insert_mut(v); } set } } #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for HashTrieSet where T: Eq + Hash + Serialize, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } impl<'de, T, P, H> Deserialize<'de> for HashTrieSet where T: Eq + Hash + Deserialize<'de>, H: BuildHasher + Clone + Default, P: SharedPointerKind, { fn deserialize>( deserializer: D, ) -> Result, D::Error> { deserializer.deserialize_seq(HashTrieSetVisitor { _phantom_t: PhantomData, _phantom_h: PhantomData, _phantom_p: PhantomData, }) } } struct HashTrieSetVisitor { _phantom_t: PhantomData, _phantom_h: PhantomData, _phantom_p: PhantomData

, } impl<'de, T, P, H> Visitor<'de> for HashTrieSetVisitor where T: Eq + Hash + Deserialize<'de>, H: BuildHasher + Clone + Default, P: SharedPointerKind, { type Value = HashTrieSet; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { let mut set = HashTrieSet::new_with_hasher_with_ptr_kind(Default::default()); while let Some(value) = seq.next_element()? { set.insert_mut(value); } Ok(set) } } } #[cfg(test)] mod test; rpds-1.1.0/src/set/hash_trie_set/test.rs000064400000000000000000000207121046102023000163250ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use alloc::vec::Vec; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(HashTrieSetSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_hash_trie_set_sync_is_send_and_sync() -> impl Send + Sync { ht_set_sync!(0) } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter_empty() { let set: HashTrieSet = HashTrieSet::new(); for _ in set.iter() { panic!("iterator should be empty"); } } #[test] fn test_iter() { let mut set = HashTrieSet::new(); let limit: usize = 100; for i in 0..limit { set = set.insert(i); } let mut touched = vec![false; limit]; for v in set.iter() { assert!(!touched[*v]); touched[*v] = true; } assert!(touched.iter().all(|b| *b)); } #[test] fn test_iter_size_hint() { let set = ht_set![0, 1, 2]; let mut iterator = set.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_into_iterator() { let set = ht_set![0, 1, 2]; let mut left = 3; for _ in &set { left -= 1; assert!(left >= 0); } assert_eq!(left, 0); } } #[test] fn test_macro_ht_set() { let set_1 = HashTrieSet::new().insert(1); let set_1_2_3 = HashTrieSet::new().insert(1).insert(2).insert(3); assert_eq!(HashTrieSet::::new(), ht_set![]); assert_eq!(set_1, ht_set![1]); assert_eq!(set_1_2_3, ht_set![1, 2, 3]); } #[test] fn test_insert() { let mut set = HashTrieSet::new(); assert_eq!(set.size(), 0); set = set.insert("foo"); assert_eq!(set.size(), 1); assert!(set.contains("foo")); set = set.insert("bar"); assert_eq!(set.size(), 2); assert!(set.contains("foo")); assert!(set.contains("bar")); set = set.insert("baz"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); set = set.insert("foo"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); } #[test] fn test_insert_mut() { let mut set = HashTrieSet::new(); assert_eq!(set.size(), 0); set.insert_mut("foo"); assert_eq!(set.size(), 1); assert!(set.contains("foo")); set.insert_mut("bar"); assert_eq!(set.size(), 2); assert!(set.contains("foo")); assert!(set.contains("bar")); set.insert_mut("baz"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); set.insert_mut("foo"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); } #[test] fn test_remove() { let mut set = ht_set!["foo", "bar", "mumble", "baz"]; let empty_set: HashTrieSet = HashTrieSet::new(); assert_eq!(empty_set.remove(&3), empty_set); assert_eq!(set.size(), 4); set = set.remove("not-there"); assert_eq!(set.size(), 4); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("mumble")); assert!(set.contains("baz")); set = set.remove("mumble"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(!set.contains("mumble")); assert!(set.contains("baz")); set = set.remove("foo"); assert_eq!(set.size(), 2); assert!(!set.contains("foo")); set = set.remove("baz"); assert_eq!(set.size(), 1); assert!(!set.contains("baz")); set = set.remove("bar"); assert_eq!(set.size(), 0); assert!(!set.contains("bar")); } #[test] fn test_remove_mut() { let mut set = ht_set!["foo", "bar", "mumble", "baz"]; assert_eq!(set.size(), 4); assert!(!set.remove_mut("not-there")); assert_eq!(set.size(), 4); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("mumble")); assert!(set.contains("baz")); assert!(set.remove_mut("mumble")); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(!set.contains("mumble")); assert!(set.contains("baz")); assert!(set.remove_mut("foo")); assert_eq!(set.size(), 2); assert!(!set.contains("foo")); assert!(set.remove_mut("baz")); assert_eq!(set.size(), 1); assert!(!set.contains("baz")); assert!(set.remove_mut("bar")); assert_eq!(set.size(), 0); assert!(!set.contains("bar")); } #[test] fn test_get() { let mut set = HashTrieSet::new(); set = set.insert("foo"); assert_eq!(set.get("foo"), Some(&"foo")); set = set.insert("bar"); assert_eq!(set.get("foo"), Some(&"foo")); assert_eq!(set.get("bar"), Some(&"bar")); } #[test] fn test_is_disjoint() { assert!(!HashTrieSet::is_disjoint(&ht_set![1, 2, 3], &ht_set![1, 2, 3])); assert!(!HashTrieSet::is_disjoint(&ht_set![1, 2, 3], &ht_set![0, 1])); assert!(HashTrieSet::is_disjoint(&ht_set![1, 2, 3, 7, 16], &ht_set![0, 4, 17])); } #[test] fn test_is_subset() { let set = ht_set![1, 2, 3]; assert!(set.is_subset(&set)); assert!(HashTrieSet::is_subset(&ht_set![], &ht_set![1, 2, 3])); assert!(HashTrieSet::is_subset(&ht_set![1, 2, 3], &ht_set![1, 2, 3])); assert!(!HashTrieSet::is_subset(&ht_set![1, 2, 3], &ht_set![1, 2, 5, 6])); assert!(HashTrieSet::is_subset(&ht_set![1, 2, 3], &ht_set![1, 2, 3, 5, 6])); assert!(!HashTrieSet::is_subset(&ht_set![1, 2, 3, 5, 6], &ht_set![1, 2, 3])); } #[test] fn test_is_superset() { let set = ht_set![1, 2, 3]; assert!(set.is_superset(&set)); assert!(HashTrieSet::is_superset(&ht_set![1, 2, 3], &ht_set![])); assert!(HashTrieSet::is_superset(&ht_set![1, 2, 3], &ht_set![1, 2, 3])); assert!(!HashTrieSet::is_superset(&ht_set![1, 2, 5, 6], &ht_set![1, 2, 3])); assert!(HashTrieSet::is_superset(&ht_set![1, 2, 3, 5, 6], &ht_set![1, 2, 3])); assert!(!HashTrieSet::is_superset(&ht_set![1, 2, 3], &ht_set![1, 2, 3, 5, 6])); } #[test] fn test_from_iterator() { let vec: Vec<&str> = vec![("two"), ("five")]; let set: HashTrieSet<&str> = vec.iter().copied().collect(); let expected_set = ht_set!["two", "five"]; assert_eq!(set, expected_set); } #[test] fn test_default() { let set: HashTrieSet = HashTrieSet::default(); assert_eq!(set.size(), 0); assert!(set.is_empty()); } #[test] fn test_display() { let empty_set: HashTrieSet = HashTrieSet::new(); let singleton_set = ht_set!["hi"]; let set = ht_set![5, 12]; assert_eq!(format!("{}", empty_set), "{}"); assert_eq!(format!("{}", singleton_set), "{hi}"); assert!(format!("{set}") == "{5, 12}" || format!("{set}") == "{12, 5}"); } #[test] fn test_eq() { let set_1 = ht_set!["a", "b"]; let set_1_prime = ht_set!["a", "b"]; let set_1_prime_2 = ht_set!["a", "b", "b"]; let set_2 = ht_set!["a", "b", "c"]; assert_eq!(set_1, set_1_prime); assert_eq!(set_1, set_1_prime_2); assert_eq!(set_1, set_1); assert_eq!(set_2, set_2); // We also check this since `assert_ne!()` does not call `ne`. assert!(set_1.ne(&set_2)); } #[test] fn test_eq_pointer_kind_consistent() { let set_a = ht_set!["a"]; let set_a_sync = ht_set_sync!["a"]; let set_b = ht_set!["b"]; let set_b_sync = ht_set_sync!["b"]; assert!(set_a == set_a_sync); assert!(set_a != set_b_sync); assert!(set_b == set_b_sync); } #[test] fn test_clone() { let set = ht_set!["hello", "there"]; let clone = set.clone(); assert_eq!(clone.size(), set.size()); assert!(clone.contains("hello")); assert!(clone.contains("there")); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let set: HashTrieSet = ht_set![5, 6, 7, 8]; let encoded = serialize(&set).unwrap(); let decoded: HashTrieSet = deserialize(&encoded).unwrap(); assert_eq!(set, decoded); } rpds-1.1.0/src/set/mod.rs000064400000000000000000000004021046102023000132760ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ pub mod hash_trie_set; pub mod red_black_tree_set; rpds-1.1.0/src/set/red_black_tree_set/mod.rs000064400000000000000000000266561046102023000171210ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::map::red_black_tree_map; use crate::RedBlackTreeMap; use archery::{ArcTK, RcK, SharedPointerKind}; use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt::Display; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; use core::ops::RangeBounds; // TODO Use impl trait instead of this when available. pub type Iter<'a, T, P> = red_black_tree_map::IterKeys<'a, T, (), P>; pub type RangeIter<'a, T, RB, Q, P> = core::iter::Map, fn((&'a T, &())) -> &'a T>; /// Creates a [`RedBlackTreeSet`](crate::RedBlackTreeSet) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let s = RedBlackTreeSet::new() /// .insert(1) /// .insert(2) /// .insert(3); /// /// assert_eq!(rbt_set![1, 2, 3], s); /// ``` #[macro_export] macro_rules! rbt_set { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::RedBlackTreeSet::new(); $( s.insert_mut($e); )* s } }; } /// Creates a [`RedBlackTreeSet`](crate::RedBlackTreeSet) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let s = RedBlackTreeSet::new_sync() /// .insert(1) /// .insert(2) /// .insert(3); /// /// assert_eq!(rbt_set_sync![1, 2, 3], s); /// ``` #[macro_export] macro_rules! rbt_set_sync { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::RedBlackTreeSet::new_sync(); $( s.insert_mut($e); )* s } }; } /// A persistent set with structural sharing. This implementation uses a /// [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree). /// /// # Complexity /// /// Let *n* be the number of elements in the set. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:-------------------------- | ---------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `insert()` | Θ(log(n)) | Θ(log(n)) | /// | `remove()` | Θ(log(n)) | Θ(log(n)) | /// | `get()` | Θ(log(n)) | Θ(log(n)) | /// | `contains()` | Θ(log(n)) | Θ(log(n)) | /// | `size()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(log(n)) | Θ(log(n)) | /// | iterator step | Θ(1) | Θ(log(n)) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This is a thin wrapper around a [`RedBlackTreeMap`](crate::RedBlackTreeMap). #[derive(Debug)] pub struct RedBlackTreeSet where T: Ord, P: SharedPointerKind, { map: RedBlackTreeMap, } pub type RedBlackTreeSetSync = RedBlackTreeSet; impl RedBlackTreeSetSync where T: Ord, { #[must_use] pub fn new_sync() -> RedBlackTreeSetSync { RedBlackTreeSet::new_with_ptr_kind() } } impl RedBlackTreeSet where T: Ord, { #[must_use] pub fn new() -> RedBlackTreeSet { RedBlackTreeSet { map: RedBlackTreeMap::new_with_ptr_kind() } } } impl RedBlackTreeSet where T: Ord, P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> RedBlackTreeSet { RedBlackTreeSet { map: RedBlackTreeMap::new_with_ptr_kind() } } #[must_use] pub fn insert(&self, v: T) -> RedBlackTreeSet { RedBlackTreeSet { map: self.map.insert(v, ()) } } pub fn insert_mut(&mut self, v: T) { self.map.insert_mut(v, ()); } #[must_use] pub fn remove(&self, v: &V) -> RedBlackTreeSet where T: Borrow, V: Ord, { RedBlackTreeSet { map: self.map.remove(v) } } pub fn remove_mut(&mut self, v: &V) -> bool where T: Borrow, V: Ord, { self.map.remove_mut(v) } #[must_use] pub fn get(&self, v: &V) -> Option<&T> where T: Borrow, V: Ord, { self.map.get_key_value(v).map(|(k, _)| k) } #[must_use] pub fn contains(&self, v: &V) -> bool where T: Borrow, V: Ord, { self.map.contains_key(v) } #[must_use] pub fn first(&self) -> Option<&T> { self.map.first().map(|(k, _)| k) } #[must_use] pub fn last(&self) -> Option<&T> { self.map.last().map(|(k, _)| k) } #[must_use] pub fn is_disjoint(&self, other: &RedBlackTreeSet) -> bool where PO: SharedPointerKind, { let mut self_it = self.iter(); let mut other_it = other.iter(); let mut v_opt = self_it.next(); let mut u_opt = other_it.next(); while let (Some(v), Some(u)) = (v_opt, u_opt) { match v.cmp(u) { Ordering::Less => v_opt = self_it.next(), Ordering::Equal => return false, Ordering::Greater => u_opt = other_it.next(), } } true } /// Test whether the two sets refer to the same content in memory. /// /// This would return true if you’re comparing a set to itself, /// or if you’re comparing a set to a fresh clone of itself. fn ptr_eq(&self, other: &RedBlackTreeSet) -> bool { self.map.ptr_eq(&other.map) } #[must_use] pub fn is_subset(&self, other: &RedBlackTreeSet) -> bool where PO: SharedPointerKind, { if self.ptr_eq(other) { return true; } if self.size() > other.size() { return false; } let mut other_it = other.iter(); for v in self { loop { match other_it.next() { Some(u) => match u.cmp(v) { Ordering::Less => (), Ordering::Equal => break, Ordering::Greater => return false, }, None => return false, } } } true } #[must_use] pub fn is_superset(&self, other: &RedBlackTreeSet) -> bool where PO: SharedPointerKind, { other.is_subset(self) } #[must_use] #[inline] pub fn size(&self) -> usize { self.map.size() } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.size() == 0 } pub fn iter(&self) -> Iter<'_, T, P> { self.map.keys() } pub fn range(&self, range: RB) -> RangeIter<'_, T, RB, Q, P> where T: Borrow, Q: Ord + ?Sized, RB: RangeBounds, { self.map.range(range).map(|(k, _)| k) } } impl Clone for RedBlackTreeSet where T: Ord, P: SharedPointerKind, { fn clone(&self) -> RedBlackTreeSet { RedBlackTreeSet { map: self.map.clone() } } } impl Default for RedBlackTreeSet where T: Ord, P: SharedPointerKind, { fn default() -> RedBlackTreeSet { RedBlackTreeSet::new_with_ptr_kind() } } impl PartialEq> for RedBlackTreeSet where T: Ord, P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &RedBlackTreeSet) -> bool { self.map.eq(&other.map) } } impl Eq for RedBlackTreeSet where T: Ord, P: SharedPointerKind, { } impl PartialOrd> for RedBlackTreeSet where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &RedBlackTreeSet) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for RedBlackTreeSet where P: SharedPointerKind, { fn cmp(&self, other: &RedBlackTreeSet) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for RedBlackTreeSet where T: Ord, { fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it // doesn't hash to the same thing as a single collection with the same elements in the same // order. self.size().hash(state); for e in self { e.hash(state); } } } impl Display for RedBlackTreeSet where T: Ord + Display, P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("{")?; for v in self { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str("}") } } impl<'a, T, P> IntoIterator for &'a RedBlackTreeSet where T: Ord, P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } // TODO This can be improved to create a perfectly balanced tree. impl FromIterator for RedBlackTreeSet where T: Ord, P: SharedPointerKind, { fn from_iter>(into_iter: I) -> RedBlackTreeSet { let mut set = RedBlackTreeSet::new_with_ptr_kind(); for v in into_iter { set.insert_mut(v); } set } } #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for RedBlackTreeSet where T: Ord + Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } impl<'de, T, P> Deserialize<'de> for RedBlackTreeSet where T: Ord + Deserialize<'de>, P: SharedPointerKind, { fn deserialize>( deserializer: D, ) -> Result, D::Error> { deserializer.deserialize_seq(RedBlackTreeSetVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData, }) } } struct RedBlackTreeSetVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData

, } impl<'de, T, P> Visitor<'de> for RedBlackTreeSetVisitor where T: Ord + Deserialize<'de>, P: SharedPointerKind, { type Value = RedBlackTreeSet; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { let mut set = RedBlackTreeSet::new_with_ptr_kind(); while let Some(value) = seq.next_element()? { set.insert_mut(value); } Ok(set) } } } #[cfg(test)] mod test; rpds-1.1.0/src/set/red_black_tree_set/test.rs000064400000000000000000000267501046102023000173140ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use alloc::vec::Vec; use core::hash::{Hash, Hasher}; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(RedBlackTreeSetSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_red_black_tree_set_sync_is_send_and_sync() -> impl Send + Sync { rbt_set_sync!(0) } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter_empty() { let set: RedBlackTreeSet = RedBlackTreeSet::new(); for _ in set.iter() { panic!("iterator should be empty"); } } #[test] fn test_iter() { let mut set = RedBlackTreeSet::new(); let limit: usize = 100; for i in 0..limit { set = set.insert(i); } let mut touched = vec![false; limit]; for v in set.iter() { assert!(!touched[*v]); touched[*v] = true; } assert!(touched.iter().all(|b| *b)); } #[test] fn test_iter_size_hint() { let set = rbt_set![0, 1, 2]; let mut iterator = set.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_iter_sorted() { let set = rbt_set![5, 6, 2, 1]; let mut iterator = set.iter(); assert_eq!(iterator.next(), Some(&1)); assert_eq!(iterator.next(), Some(&2)); assert_eq!(iterator.next(), Some(&5)); assert_eq!(iterator.next(), Some(&6)); assert_eq!(iterator.next(), None); } #[test] fn test_into_iterator() { let set = rbt_set![0, 1, 2]; let mut left = 3; for _ in &set { left -= 1; assert!(left >= 0); } assert_eq!(left, 0); } #[test] fn test_range_iterator() { let set = rbt_set![-20, -12, -8, 0, 2, -10, -7, -3, 5, 8, 10, 13, 17, 20]; let mut iterator = set.range(-7..=13); assert_eq!(iterator.next(), Some(&-7)); assert_eq!(iterator.next(), Some(&-3)); assert_eq!(iterator.next_back(), Some(&13)); assert_eq!(iterator.next_back(), Some(&10)); assert_eq!(iterator.next_back(), Some(&8)); assert_eq!(iterator.next(), Some(&0)); assert_eq!(iterator.next(), Some(&2)); assert_eq!(iterator.next(), Some(&5)); assert_eq!(iterator.next_back(), None); assert_eq!(iterator.next(), None); } } #[test] fn test_macro_rbt_set() { let set_1 = RedBlackTreeSet::new().insert(1); let set_1_2_3 = RedBlackTreeSet::new().insert(1).insert(2).insert(3); assert_eq!(RedBlackTreeSet::::new(), rbt_set![]); assert_eq!(set_1, rbt_set![1]); assert_eq!(set_1_2_3, rbt_set![1, 2, 3]); } #[test] fn test_insert() { let mut set = RedBlackTreeSet::new(); assert_eq!(set.size(), 0); set = set.insert("foo"); assert_eq!(set.size(), 1); assert!(set.contains("foo")); set = set.insert("bar"); assert_eq!(set.size(), 2); assert!(set.contains("foo")); assert!(set.contains("bar")); set = set.insert("baz"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); set = set.insert("foo"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); } #[test] fn test_insert_mut() { let mut set = RedBlackTreeSet::new(); assert_eq!(set.size(), 0); set.insert_mut("foo"); assert_eq!(set.size(), 1); assert!(set.contains("foo")); set.insert_mut("bar"); assert_eq!(set.size(), 2); assert!(set.contains("foo")); assert!(set.contains("bar")); set.insert_mut("baz"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); set.insert_mut("foo"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("baz")); } #[test] fn test_first() { let set = rbt_set![3, 2, 1]; assert_eq!(set.first(), Some(&1)); } #[test] fn test_last() { let set = rbt_set![3, 2, 1]; assert_eq!(set.last(), Some(&3)); } #[test] fn test_remove() { let mut set = rbt_set!["foo", "bar", "mumble", "baz"]; let empty_set: RedBlackTreeSet = RedBlackTreeSet::new(); assert_eq!(empty_set.remove(&3), empty_set); assert_eq!(set.size(), 4); set = set.remove("not-there"); assert_eq!(set.size(), 4); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("mumble")); assert!(set.contains("baz")); set = set.remove("mumble"); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(!set.contains("mumble")); assert!(set.contains("baz")); set = set.remove("foo"); assert_eq!(set.size(), 2); assert!(!set.contains("foo")); set = set.remove("baz"); assert_eq!(set.size(), 1); assert!(!set.contains("baz")); set = set.remove("bar"); assert_eq!(set.size(), 0); assert!(!set.contains("bar")); } #[test] fn test_remove_mut() { let mut set = rbt_set!["foo", "bar", "mumble", "baz"]; assert_eq!(set.size(), 4); assert!(!set.remove_mut("not-there")); assert_eq!(set.size(), 4); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(set.contains("mumble")); assert!(set.contains("baz")); assert!(set.remove_mut("mumble")); assert_eq!(set.size(), 3); assert!(set.contains("foo")); assert!(set.contains("bar")); assert!(!set.contains("mumble")); assert!(set.contains("baz")); assert!(set.remove_mut("foo")); assert_eq!(set.size(), 2); assert!(!set.contains("foo")); assert!(set.remove_mut("baz")); assert_eq!(set.size(), 1); assert!(!set.contains("baz")); assert!(set.remove_mut("bar")); assert_eq!(set.size(), 0); assert!(!set.contains("bar")); } #[test] fn test_get() { let mut set = RedBlackTreeSet::new(); set = set.insert("foo"); assert_eq!(set.get("foo"), Some(&"foo")); set = set.insert("bar"); assert_eq!(set.get("foo"), Some(&"foo")); assert_eq!(set.get("bar"), Some(&"bar")); } #[test] fn test_is_disjoint() { assert!(!RedBlackTreeSet::is_disjoint(&rbt_set![1, 2, 3], &rbt_set![1, 2, 3])); assert!(!RedBlackTreeSet::is_disjoint(&rbt_set![1, 2, 3], &rbt_set![0, 1])); assert!(RedBlackTreeSet::is_disjoint(&rbt_set![1, 2, 3, 7, 16], &rbt_set![0, 4, 17])); } #[test] fn test_is_subset() { let set = rbt_set![1, 2, 3]; assert!(set.is_subset(&set)); assert!(RedBlackTreeSet::is_subset(&rbt_set![], &rbt_set![1, 2, 3])); assert!(RedBlackTreeSet::is_subset(&rbt_set![1, 2, 3], &rbt_set![1, 2, 3])); assert!(!RedBlackTreeSet::is_subset(&rbt_set![1, 2, 3], &rbt_set![1, 2, 5, 6])); assert!(RedBlackTreeSet::is_subset(&rbt_set![1, 2, 3], &rbt_set![1, 2, 3, 5, 6])); assert!(!RedBlackTreeSet::is_subset(&rbt_set![1, 2, 3, 5, 6], &rbt_set![1, 2, 3])); } #[test] fn test_is_superset() { let set = rbt_set![1, 2, 3]; assert!(set.is_superset(&set)); assert!(RedBlackTreeSet::is_superset(&rbt_set![1, 2, 3], &rbt_set![])); assert!(RedBlackTreeSet::is_superset(&rbt_set![1, 2, 3], &rbt_set![1, 2, 3])); assert!(!RedBlackTreeSet::is_superset(&rbt_set![1, 2, 5, 6], &rbt_set![1, 2, 3])); assert!(RedBlackTreeSet::is_superset(&rbt_set![1, 2, 3, 5, 6], &rbt_set![1, 2, 3])); assert!(!RedBlackTreeSet::is_superset(&rbt_set![1, 2, 3], &rbt_set![1, 2, 3, 5, 6])); } #[test] fn test_from_iterator() { let vec: Vec<&str> = vec![("two"), ("five")]; let set: RedBlackTreeSet<&str> = vec.iter().copied().collect(); let expected_set = rbt_set!["two", "five"]; assert_eq!(set, expected_set); } #[test] fn test_default() { let set: RedBlackTreeSet = RedBlackTreeSet::default(); assert_eq!(set.size(), 0); assert!(set.is_empty()); } #[test] fn test_display() { let empty_set: RedBlackTreeSet = RedBlackTreeSet::new(); let singleton_set = rbt_set!["hi"]; let set = rbt_set![5, 12]; assert_eq!(format!("{}", empty_set), "{}"); assert_eq!(format!("{}", singleton_set), "{hi}"); assert_eq!(format!("{}", set), "{5, 12}"); } #[test] fn test_eq() { let set_1 = rbt_set!["a", "b"]; let set_1_prime = rbt_set!["a", "b"]; let set_1_prime_2 = rbt_set!["a", "b", "b"]; let set_2 = rbt_set!["a", "b", "c"]; assert_eq!(set_1, set_1_prime); assert_eq!(set_1, set_1_prime_2); assert_eq!(set_1, set_1); assert_eq!(set_2, set_2); // We also check this since `assert_ne!()` does not call `ne`. assert!(set_1.ne(&set_2)); } #[test] fn test_eq_pointer_kind_consistent() { let set_a = rbt_set!["a"]; let set_a_sync = rbt_set_sync!["a"]; let set_b = rbt_set!["b"]; let set_b_sync = rbt_set_sync!["b"]; assert!(set_a == set_a_sync); assert!(set_a != set_b_sync); assert!(set_b == set_b_sync); } #[test] fn test_partial_ord() { let set_1 = rbt_set!["a"]; let set_1_prime = rbt_set!["a"]; let set_2 = rbt_set!["b"]; assert_eq!(set_1.partial_cmp(&set_1_prime), Some(Ordering::Equal)); assert_eq!(set_1.partial_cmp(&set_2), Some(Ordering::Less)); assert_eq!(set_2.partial_cmp(&set_1), Some(Ordering::Greater)); } #[test] fn test_ord() { let set_1 = rbt_set!["a"]; let set_1_prime = rbt_set!["a"]; let set_2 = rbt_set!["b"]; assert_eq!(set_1.cmp(&set_1_prime), Ordering::Equal); assert_eq!(set_1.cmp(&set_2), Ordering::Less); assert_eq!(set_2.cmp(&set_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let set_a = rbt_set!["a"]; let set_a_sync = rbt_set_sync!["a"]; let set_b = rbt_set!["b"]; let set_b_sync = rbt_set_sync!["b"]; assert!(set_a <= set_a_sync); assert!(set_a < set_b_sync); assert!(set_b >= set_b_sync); assert!(set_a_sync >= set_a); assert!(set_b_sync > set_a); assert!(set_b_sync <= set_b); } fn hash(set: &RedBlackTreeSet) -> u64 where P: SharedPointerKind, { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); set.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let set_1 = rbt_set!["a"]; let set_1_prime = rbt_set!["a"]; let set_2 = rbt_set!["b", "a"]; assert_eq!(hash(&set_1), hash(&set_1)); assert_eq!(hash(&set_1), hash(&set_1_prime)); assert_ne!(hash(&set_1), hash(&set_2)); } #[test] fn test_hash_pointer_kind_consistent() { let set = rbt_set!["a"]; let set_sync = rbt_set_sync!["a"]; assert_eq!(hash(&set), hash(&set_sync)); } #[test] fn test_clone() { let set = rbt_set!["hello", "there"]; let clone = set.clone(); assert_eq!(clone.size(), set.size()); assert!(clone.contains("hello")); assert!(clone.contains("there")); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let set: RedBlackTreeSet = rbt_set![5, 6, 7, 8]; let encoded = serialize(&set).unwrap(); let decoded: RedBlackTreeSet = deserialize(&encoded).unwrap(); assert_eq!(set, decoded); } rpds-1.1.0/src/stack/mod.rs000064400000000000000000000147431046102023000136250ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::List; use archery::*; use core::cmp::Ordering; use core::fmt::Display; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; // TODO Use impl trait for return value when available pub type Iter<'a, T, P> = crate::list::Iter<'a, T, P>; /// Creates a [`Stack`](crate::Stack) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let mut s = Stack::new() /// .push(1) /// .push(2) /// .push(3); /// /// assert_eq!(stack![1, 2, 3], s); /// ``` #[macro_export] macro_rules! stack { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::Stack::new(); $( s.push_mut($e); )* s } }; } /// Creates a [`Stack`](crate::Stack) that implements `Sync`, containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let mut s = Stack::new_sync() /// .push(1) /// .push(2) /// .push(3); /// /// assert_eq!(stack_sync![1, 2, 3], s); /// /// fn is_sync() -> impl Sync { /// stack_sync![0, 1, 1, 2, 3, 5, 8] /// } /// ``` #[macro_export] macro_rules! stack_sync { ($($e:expr),*) => { { #[allow(unused_mut)] let mut s = $crate::Stack::new_sync(); $( s.push_mut($e); )* s } }; } /// A persistent stack with structural sharing. /// /// # Complexity /// /// Let *n* be the number of elements in the stack. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:----------------- | -------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `push()` | Θ(1) | Θ(1) | /// | `pop()` | Θ(1) | Θ(1) | /// | `peek()` | Θ(1) | Θ(1) | /// | `size()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(1) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This is a thin wrapper around a [`List`](crate::List). #[derive(Debug)] pub struct Stack where P: SharedPointerKind, { list: List, } pub type StackSync = Stack; impl StackSync { #[must_use] pub fn new_sync() -> StackSync { Stack::new_with_ptr_kind() } } impl Stack { #[must_use] pub fn new() -> Stack { Stack::new_with_ptr_kind() } } impl Stack where P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> Stack { Stack { list: List::new_with_ptr_kind() } } #[must_use] pub fn peek(&self) -> Option<&T> { self.list.first() } #[must_use] pub fn pop(&self) -> Option> { let mut new_stack = self.clone(); if new_stack.pop_mut() { Some(new_stack) } else { None } } pub fn pop_mut(&mut self) -> bool { self.list.drop_first_mut() } #[must_use] pub fn push(&self, v: T) -> Stack { let mut new_stack = self.clone(); new_stack.push_mut(v); new_stack } pub fn push_mut(&mut self, v: T) { self.list.push_front_mut(v); } #[must_use] #[inline] pub fn size(&self) -> usize { self.list.len() } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.list.is_empty() } pub fn iter(&self) -> Iter<'_, T, P> { self.list.iter() } } impl Default for Stack where P: SharedPointerKind, { fn default() -> Stack { Stack::new_with_ptr_kind() } } impl PartialEq> for Stack where P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &Stack) -> bool { self.list.eq(&other.list) } } impl Eq for Stack where P: SharedPointerKind {} impl, P, PO> PartialOrd> for Stack where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &Stack) -> Option { self.list.partial_cmp(&other.list) } } impl Ord for Stack where P: SharedPointerKind, { fn cmp(&self, other: &Stack) -> Ordering { self.list.cmp(&other.list) } } impl Hash for Stack where P: SharedPointerKind, { fn hash(&self, state: &mut H) { self.list.hash(state); } } impl Clone for Stack where P: SharedPointerKind, { fn clone(&self) -> Stack { Stack { list: List::clone(&self.list) } } } impl Display for Stack where P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("Stack(")?; for v in self { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str(")") } } impl<'a, T, P> IntoIterator for &'a Stack where P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.list.iter() } } impl FromIterator for Stack where P: SharedPointerKind, { fn from_iter>(into_iter: I) -> Stack { Stack { list: List::from_iter(into_iter) } } } #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer}; use ::serde::ser::{Serialize, Serializer}; impl Serialize for Stack where T: Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { self.list.serialize(serializer) } } impl<'de, T, P> Deserialize<'de> for Stack where T: Deserialize<'de>, P: SharedPointerKind, { fn deserialize>(deserializer: D) -> Result, D::Error> { Deserialize::deserialize(deserializer).map(|list| Stack { list }) } } } #[cfg(test)] mod test; rpds-1.1.0/src/stack/test.rs000064400000000000000000000152401046102023000140160ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use alloc::vec::Vec; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(StackSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_stack_sync_is_send_and_sync() -> impl Send + Sync { stack_sync!(0) } mod iter { #[test] fn test_iter() { let stack = stack![2, 1, 0]; let mut iterator = stack.iter(); assert_eq!(iterator.next(), Some(&0)); assert_eq!(iterator.next(), Some(&1)); assert_eq!(iterator.next(), Some(&2)); assert_eq!(iterator.next(), None); } #[test] fn test_iter_size_hint() { let stack = stack![2, 1, 0]; let mut iterator = stack.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_into_iterator() { let stack = stack![3, 2, 1, 0]; let mut left = 4; for (expected, n) in stack.into_iter().enumerate() { left -= 1; assert!(left >= 0); assert_eq!(*n, expected); } assert_eq!(left, 0); } } #[test] fn test_new() { let empty_stack: Stack = Stack::new(); assert!(empty_stack.list.is_empty()); assert_eq!(empty_stack.size(), 0); assert!(empty_stack.is_empty()); } #[test] fn test_macro_stack() { let mut stack_1 = Stack::new(); stack_1.push_mut(1); let mut stack_1_2_3 = Stack::new(); stack_1_2_3.push_mut(1); stack_1_2_3.push_mut(2); stack_1_2_3.push_mut(3); assert_eq!(Stack::::new(), stack![]); assert_eq!(stack_1, stack![1]); assert_eq!(stack_1_2_3, stack![1, 2, 3]); } #[test] fn test_peek() { let empty_stack: Stack = Stack::new(); let singleton_stack = stack!["hello"]; let stack = stack![3, 2, 1, 0]; assert_eq!(empty_stack.peek(), None); assert_eq!(singleton_stack.peek(), Some(&"hello")); assert_eq!(stack.peek(), Some(&0)); } #[test] fn test_pop_mut() { let mut empty_stack: Stack = Stack::new(); let mut singleton_stack = stack!["hello"]; let mut stack = stack![3, 2, 1, 0]; empty_stack.pop_mut(); assert!(empty_stack.is_empty()); singleton_stack.pop_mut(); assert_eq!(singleton_stack.peek(), None); stack.pop_mut(); assert_eq!(stack.peek(), Some(&1)); assert_eq!(stack.size(), 3); } #[test] fn test_pop() { let empty_stack: Stack = Stack::new(); let singleton_stack = Stack::new().push("hello"); let stack = Stack::new().push(3).push(2).push(1).push(0); assert!(empty_stack.pop().is_none()); assert_eq!(singleton_stack.pop().unwrap().peek(), None); assert_eq!(stack.pop().unwrap().peek(), Some(&1)); assert_eq!(stack.size(), 4); assert_eq!(stack.pop().unwrap().size(), 3); } #[test] fn test_from_iterator() { let vec: Vec = vec![10, 11, 12, 13]; let stack: Stack = vec.iter().copied().collect(); assert!(vec.iter().eq(stack.iter())); } #[test] fn test_default() { let stack: Stack = Stack::default(); assert_eq!(stack.peek(), None); assert!(stack.is_empty()); } #[test] fn test_display() { let empty_stack: Stack = Stack::new(); let singleton_stack = stack!["hello"]; let stack = stack![3, 2, 1, 0]; assert_eq!(format!("{}", empty_stack), "Stack()"); assert_eq!(format!("{}", singleton_stack), "Stack(hello)"); assert_eq!(format!("{}", stack), "Stack(0, 1, 2, 3)"); } #[test] fn test_eq() { let stack_1 = stack!["a", "a"]; let stack_1_prime = stack!["a", "a"]; let stack_2 = stack!["b", "a"]; assert_ne!(stack_1, stack_2); assert_eq!(stack_1, stack_1); assert_eq!(stack_1, stack_1_prime); assert_eq!(stack_2, stack_2); } #[test] fn test_eq_pointer_kind_consistent() { let stack_a = stack!["a"]; let stack_a_sync = stack_sync!["a"]; let stack_b = stack!["b"]; let stack_b_sync = stack_sync!["b"]; assert!(stack_a == stack_a_sync); assert!(stack_a != stack_b_sync); assert!(stack_b == stack_b_sync); } #[test] fn test_partial_ord() { let stack_1 = stack!["a"]; let stack_1_prime = stack!["a"]; let stack_2 = stack!["b"]; let stack_3 = stack![0.0]; let stack_4 = stack![core::f32::NAN]; assert_eq!(stack_1.partial_cmp(&stack_1_prime), Some(Ordering::Equal)); assert_eq!(stack_1.partial_cmp(&stack_2), Some(Ordering::Less)); assert_eq!(stack_2.partial_cmp(&stack_1), Some(Ordering::Greater)); assert_eq!(stack_3.partial_cmp(&stack_4), None); } #[test] fn test_ord() { let stack_1 = stack!["a"]; let stack_1_prime = stack!["a"]; let stack_2 = stack!["b"]; assert_eq!(stack_1.cmp(&stack_1_prime), Ordering::Equal); assert_eq!(stack_1.cmp(&stack_2), Ordering::Less); assert_eq!(stack_2.cmp(&stack_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let stack_a = stack!["a"]; let stack_a_sync = stack_sync!["a"]; let stack_b = stack!["b"]; let stack_b_sync = stack_sync!["b"]; assert!(stack_a <= stack_a_sync); assert!(stack_a < stack_b_sync); assert!(stack_b >= stack_b_sync); assert!(stack_a_sync >= stack_a); assert!(stack_b_sync > stack_a); assert!(stack_b_sync <= stack_b); } fn hash(stack: &Stack) -> u64 { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); stack.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let stack_1 = stack!["a"]; let stack_1_prime = stack!["a"]; let stack_2 = stack!["b", "a"]; assert_eq!(hash(&stack_1), hash(&stack_1)); assert_eq!(hash(&stack_1), hash(&stack_1_prime)); assert_ne!(hash(&stack_1), hash(&stack_2)); } #[test] fn test_hash_pointer_kind_consistent() { let stack = stack!["a"]; let stack_sync = stack_sync!["a"]; assert_eq!(hash(&stack), hash(&stack_sync)); } #[test] fn test_clone() { let stack = stack!["there", "hello"]; let clone = stack.clone(); assert!(clone.iter().eq(stack.iter())); assert_eq!(clone.size(), stack.size()); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let stack: Stack = stack![5, 6, 7, 8]; let encoded = serialize(&stack).unwrap(); let decoded: Stack = deserialize(&encoded).unwrap(); assert_eq!(stack, decoded); } rpds-1.1.0/src/utils/mod.rs000064400000000000000000000015521046102023000136520ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use archery::{SharedPointer, SharedPointerKind}; #[cfg(feature = "std")] pub type DefaultBuildHasher = std::collections::hash_map::RandomState; #[cfg(not(feature = "std"))] #[allow(deprecated)] pub type DefaultBuildHasher = core::hash::BuildHasherDefault; /// Assigns the content of `src` to `dest`. pub fn replace(dest: &mut T, mut src: SharedPointer) where P: SharedPointerKind, { // To avoid unnecessary cloning we do this trick. If we have exclusive ownership of the // data pointed to by `src` then no clone is done. core::mem::swap(dest, SharedPointer::make_mut(&mut src)); } #[cfg(test)] mod test; rpds-1.1.0/src/utils/test.rs000064400000000000000000000006721046102023000140540ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use archery::RcK; use pretty_assertions::assert_eq; #[test] fn test_replace() { let src: SharedPointer<_, RcK> = SharedPointer::new(3); let mut dest = 0; replace(&mut dest, src); assert_eq!(dest, 3); } rpds-1.1.0/src/vector/mod.rs000064400000000000000000000556311046102023000140230ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use alloc::borrow::Borrow; use alloc::fmt::Display; use alloc::vec::Vec; use archery::*; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; use core::ops::Index; use core::ops::IndexMut; // TODO Use impl trait instead of this when available. pub type Iter<'a, T, P> = core::iter::Map, fn(&SharedPointer) -> &T>; const DEFAULT_BITS: u8 = 5; /// Creates a [`Vector`](crate::Vector) containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let v = Vector::new() /// .push_back(1) /// .push_back(2) /// .push_back(3); /// /// assert_eq!(vector![1, 2, 3], v); /// ``` #[macro_export] macro_rules! vector { ($($e:expr),*) => { { #[allow(unused_mut)] let mut v = $crate::Vector::new(); $( v.push_back_mut($e); )* v } }; } /// Creates a [`Vector`](crate::Vector) that implements `Sync`, containing the given arguments: /// /// ``` /// # use rpds::*; /// # /// let v = Vector::new_sync() /// .push_back(1) /// .push_back(2) /// .push_back(3); /// /// assert_eq!(vector_sync![1, 2, 3], v); /// ``` #[macro_export] macro_rules! vector_sync { ($($e:expr),*) => { { #[allow(unused_mut)] let mut v = $crate::Vector::new_sync(); $( v.push_back_mut($e); )* v } }; } /// A persistent vector with structural sharing. /// /// # Complexity /// /// Let *n* be the number of elements in the vector. /// /// ## Temporal complexity /// /// | Operation | Average | Worst case | /// |:-------------------------- | ---------:| -----------:| /// | `new()` | Θ(1) | Θ(1) | /// | `set()` | Θ(log(n)) | Θ(log(n)) | /// | `push_back()` | Θ(log(n)) | Θ(log(n)) | /// | `drop_last()` | Θ(log(n)) | Θ(log(n)) | /// | `first()`/`last()`/`get()` | Θ(log(n)) | Θ(log(n)) | /// | `len()` | Θ(1) | Θ(1) | /// | `clone()` | Θ(1) | Θ(1) | /// | iterator creation | Θ(1) | Θ(1) | /// | iterator step | Θ(1) | Θ(log(n)) | /// | iterator full | Θ(n) | Θ(n) | /// /// # Implementation details /// /// This implementation uses a bitmapped vector trie as described in /// [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1) /// and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2). #[derive(Debug)] pub struct Vector where P: SharedPointerKind, { root: SharedPointer, P>, bits: u8, length: usize, } pub type VectorSync = Vector; #[derive(Debug)] enum Node where P: SharedPointerKind, { Branch(Vec, P>>), Leaf(Vec>), } impl Node where P: SharedPointerKind, { fn new_empty_branch() -> Node { Node::Branch(Vec::new()) } fn new_empty_leaf() -> Node { Node::Leaf(Vec::new()) } fn get usize>(&self, index: usize, height: usize, bucket: F) -> &T { let b = bucket(index, height); match self { Node::Branch(a) => a[b].get(index, height - 1, bucket), Node::Leaf(a) => { debug_assert_eq!(height, 0); a[b].as_ref() } } } fn assoc usize>(&mut self, value: T, height: usize, bucket: F) { let b = bucket(height); match self { Node::Leaf(a) => { debug_assert_eq!(height, 0, "cannot have a leaf at this height"); if a.len() == b { a.push(SharedPointer::new(value)); } else { a[b] = SharedPointer::new(value); } } Node::Branch(a) => { debug_assert!(height > 0, "cannot have a branch at this height"); match a.get_mut(b) { Some(subtree) => { SharedPointer::make_mut(subtree).assoc(value, height - 1, bucket); } None => { let mut subtree = if height > 1 { Node::new_empty_branch() } else { Node::new_empty_leaf() }; subtree.assoc(value, height - 1, bucket); a.push(SharedPointer::new(subtree)); } } } } } #[inline] fn is_empty(&self) -> bool { self.used() == 0 } #[inline] fn is_singleton(&self) -> bool { self.used() == 1 } fn used(&self) -> usize { match self { Node::Leaf(a) => a.len(), Node::Branch(a) => a.len(), } } /// Drops the last element. /// /// This will return `true` if the subtree after drop becomes empty (or it already was empty). /// Note that this will prune irrelevant branches, i.e. there will be no branches without /// elements under it. fn drop_last(&mut self) -> bool { if self.is_empty() { return true; } match self { Node::Leaf(a) => { a.pop(); } Node::Branch(a) => { if SharedPointer::make_mut(a.last_mut().unwrap()).drop_last() { a.pop(); } } } self.is_empty() } } impl Node where P: SharedPointerKind, { fn get_mut usize>( &mut self, index: usize, height: usize, bucket: F, ) -> &mut T { let b = bucket(index, height); match *self { Node::Branch(ref mut a) => { SharedPointer::make_mut(&mut a[b]).get_mut(index, height - 1, bucket) } Node::Leaf(ref mut a) => { debug_assert_eq!(height, 0); SharedPointer::make_mut(&mut a[b]) } } } } impl Clone for Node where P: SharedPointerKind, { fn clone(&self) -> Node { match self { Node::Branch(a) => Node::Branch(Vec::clone(a)), Node::Leaf(a) => Node::Leaf(Vec::clone(a)), } } } mod vector_utils { #[inline] pub fn degree(bits: u8) -> usize { 1 << bits } #[inline] pub fn mask(bits: u8) -> usize { degree(bits) - 1 } #[inline] pub fn bucket(bits: u8, index: usize, height: usize) -> usize { (index >> (height * bits as usize)) & mask(bits) } } impl VectorSync { #[must_use] pub fn new_sync() -> VectorSync { Vector::new_with_ptr_kind() } } impl Vector { #[must_use] pub fn new() -> Vector { Vector::new_with_ptr_kind() } } impl Vector where P: SharedPointerKind, { #[must_use] pub fn new_with_ptr_kind() -> Vector { Vector::new_with_bits(DEFAULT_BITS) } #[must_use] pub fn new_with_bits(bits: u8) -> Vector { assert!(bits > 0, "number of bits for the vector must be positive"); Vector { root: SharedPointer::new(Node::new_empty_leaf()), bits, length: 0 } } #[must_use] #[inline] pub fn first(&self) -> Option<&T> { self.get(0) } #[must_use] pub fn last(&self) -> Option<&T> { match self.length { 0 => None, n => self.get(n - 1), } } #[inline] fn height(&self) -> usize { if self.length > 1 { let u: usize = self.length - 1; let c: usize = usize::BITS as usize - u.leading_zeros() as usize; let b: usize = self.bits as usize; c / b + usize::from(c % b > 0) - 1 } else { 0 } } #[must_use] pub fn get(&self, index: usize) -> Option<&T> { if index >= self.length { None } else { Some(self.root.get(index, self.height(), |index, height| { vector_utils::bucket(self.bits, index, height) })) } } #[must_use] pub fn set(&self, index: usize, v: T) -> Option> { let mut new_vector = self.clone(); if new_vector.set_mut(index, v) { Some(new_vector) } else { None } } /// Returns `true` if the operation was successful. pub fn set_mut(&mut self, index: usize, v: T) -> bool { if index >= self.length { false } else { self.assoc(index, v); true } } /// Sets the given index to v. /// /// # Panics /// /// This method will panic if the trie's root doesn't have capacity for the given index. fn assoc(&mut self, index: usize, v: T) { debug_assert!( index < self.root_max_capacity(), "This trie's root cannot support this index" ); let height = self.height(); let bits = self.bits; SharedPointer::make_mut(&mut self.root) .assoc(v, height, |height| vector_utils::bucket(bits, index, height)); let adds_item: bool = index >= self.length; if adds_item { self.length += 1; } } #[inline] fn root_max_capacity(&self) -> usize { /* A Trie's root max capacity is * * degree ** (height + 1) * = (2 ** bits) ** (height + 1) (by def. of degree) * = 2 ** (bits * (height + 1)) * = 1 << (bits * (height + 1)) */ 1 << (self.bits as usize * (self.height() + 1)) } #[inline] fn is_root_full(&self) -> bool { self.length == self.root_max_capacity() } #[must_use] pub fn push_back(&self, v: T) -> Vector { let mut new_vector = self.clone(); new_vector.push_back_mut(v); new_vector } pub fn push_back_mut(&mut self, v: T) { if self.is_root_full() { let mut new_root: Node = Node::new_empty_branch(); match new_root { Node::Branch(ref mut values) => values.push(SharedPointer::clone(&self.root)), Node::Leaf(_) => unreachable!("expected a branch"), } let length = self.length; self.root = SharedPointer::new(new_root); self.length += 1; self.assoc(length, v); } else { let length = self.length; self.assoc(length, v); } } /// Compresses a root. A root is compressed if, whenever there is a branch, it has more than /// one child. /// /// The trie must always have a compressed root. fn compress_root(root: &mut Node) -> Option, P>> { let singleton = root.is_singleton(); match root { Node::Leaf(_) => None, Node::Branch(a) if singleton => a.pop(), Node::Branch(_) => None, } } #[must_use] pub fn drop_last(&self) -> Option> { let mut new_vector = self.clone(); if new_vector.drop_last_mut() { Some(new_vector) } else { None } } pub fn drop_last_mut(&mut self) -> bool { if self.length > 0 { let new_root = { let root = SharedPointer::make_mut(&mut self.root); root.drop_last(); self.length -= 1; Vector::compress_root(root) }; if let Some(new_root) = new_root { self.root = new_root; } true } else { false } } #[must_use] #[inline] pub fn len(&self) -> usize { self.length } #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn iter(&self) -> Iter<'_, T, P> { self.iter_ptr().map(|v| v.borrow()) } #[must_use] fn iter_ptr(&self) -> IterPtr<'_, T, P> { IterPtr::new(self) } } impl Vector where P: SharedPointerKind, { /// Gets a mutable reference to an element. If the element is shared, it will be cloned. /// Returns `None` if and only if the given `index` is out of range. #[must_use] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index >= self.length { None } else { let height = self.height(); let bits = self.bits; Some( SharedPointer::make_mut(&mut self.root).get_mut(index, height, |index, height| { vector_utils::bucket(bits, index, height) }), ) } } } impl Index for Vector where P: SharedPointerKind, { type Output = T; fn index(&self, index: usize) -> &T { self.get(index).unwrap_or_else(|| panic!("index out of bounds {index}")) } } impl IndexMut for Vector where P: SharedPointerKind, { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap_or_else(|| panic!("index out of bounds {index}")) } } impl Default for Vector where P: SharedPointerKind, { fn default() -> Vector { Vector::new_with_ptr_kind() } } impl, U, P, PO> PartialEq> for Vector where P: SharedPointerKind, PO: SharedPointerKind, { fn eq(&self, other: &Vector) -> bool { self.length == other.length && self.iter().eq(other.iter()) } } impl Eq for Vector where P: SharedPointerKind {} impl, U, P, PO> PartialOrd> for Vector where P: SharedPointerKind, PO: SharedPointerKind, { fn partial_cmp(&self, other: &Vector) -> Option { self.iter().partial_cmp(other.iter()) } } impl Ord for Vector where P: SharedPointerKind, { fn cmp(&self, other: &Vector) -> Ordering { self.iter().cmp(other.iter()) } } impl Hash for Vector where P: SharedPointerKind, { fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it doesn't // hash to the same thing as a single collection with the same elements in the same order. self.len().hash(state); for e in self { e.hash(state); } } } impl Clone for Vector where P: SharedPointerKind, { fn clone(&self) -> Vector { Vector { root: SharedPointer::clone(&self.root), bits: self.bits, length: self.length } } } impl Display for Vector where P: SharedPointerKind, { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut first = true; fmt.write_str("[")?; for v in self { if !first { fmt.write_str(", ")?; } v.fmt(fmt)?; first = false; } fmt.write_str("]") } } impl<'a, T, P> IntoIterator for &'a Vector where P: SharedPointerKind, { type Item = &'a T; type IntoIter = Iter<'a, T, P>; fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } impl FromIterator for Vector where P: SharedPointerKind, { fn from_iter>(into_iter: I) -> Vector { let mut vector = Vector::new_with_ptr_kind(); vector.extend(into_iter); vector } } impl Extend for Vector where P: SharedPointerKind, { fn extend>(&mut self, iter: I) { for elem in iter { self.push_back_mut(elem); } } } pub struct IterPtr<'a, T, P> where P: SharedPointerKind, { vector: &'a Vector, stack_forward: Option>>, stack_backward: Option>>, left_index: usize, // inclusive right_index: usize, // exclusive } struct IterStackElement<'a, T, P> where P: SharedPointerKind, { node: &'a Node, index: isize, } impl<'a, T, P> IterStackElement<'a, T, P> where P: SharedPointerKind, { fn new(node: &Node, backwards: bool) -> IterStackElement<'_, T, P> { #[allow(clippy::cast_possible_wrap)] IterStackElement { node, index: if backwards { node.used() as isize - 1 } else { 0 } } } #[inline] fn current_node(&self) -> &'a Node { #[allow(clippy::cast_sign_loss)] match self.node { Node::Branch(a) => a[self.index as usize].as_ref(), Node::Leaf(_) => panic!("called current node of a branch"), } } #[inline] fn current_elem(&self) -> &'a SharedPointer { #[allow(clippy::cast_sign_loss)] match self.node { Node::Leaf(a) => &a[self.index as usize], Node::Branch(_) => panic!("called current element of a branch"), } } /// Advance and returns `true` if finished. #[inline] fn advance(&mut self, backwards: bool) -> bool { #[allow(clippy::cast_sign_loss)] if backwards { self.index -= 1; self.index < 0 } else { self.index += 1; self.index as usize >= self.node.used() } } } impl<'a, T, P> IterPtr<'a, T, P> where P: SharedPointerKind, { fn new(vector: &Vector) -> IterPtr<'_, T, P> { IterPtr { vector, stack_forward: None, stack_backward: None, left_index: 0, right_index: vector.len(), } } fn dig(stack: &mut Vec>, backwards: bool) { let next_node: &Node = { let stack_top = stack.last().unwrap(); if let Node::Leaf(_) = *stack_top.node { return; } stack_top.current_node() }; stack.push(IterStackElement::new(next_node, backwards)); IterPtr::dig(stack, backwards); } fn init_if_needed(&mut self, backwards: bool) { let stack_field = if backwards { &mut self.stack_backward } else { &mut self.stack_forward }; if stack_field.is_none() { let mut stack: Vec> = Vec::with_capacity(self.vector.height() + 1); stack.push(IterStackElement::new(self.vector.root.borrow(), backwards)); IterPtr::dig(&mut stack, backwards); *stack_field = Some(stack); } } fn advance(stack: &mut Vec>, backwards: bool) { if let Some(mut stack_element) = stack.pop() { let finished = stack_element.advance(backwards); if finished { IterPtr::advance(stack, backwards); } else { stack.push(stack_element); IterPtr::dig(stack, backwards); } } } #[inline] fn current(stack: &[IterStackElement<'a, T, P>]) -> Option<&'a SharedPointer> { stack.last().map(IterStackElement::current_elem) } #[inline] fn non_empty(&self) -> bool { self.left_index < self.right_index } fn advance_forward(&mut self) { if self.non_empty() { IterPtr::advance(self.stack_forward.as_mut().unwrap(), false); self.left_index += 1; } } fn current_forward(&self) -> Option<&'a SharedPointer> { if self.non_empty() { IterPtr::current(self.stack_forward.as_ref().unwrap()) } else { None } } fn advance_backward(&mut self) { if self.non_empty() { IterPtr::advance(self.stack_backward.as_mut().unwrap(), true); self.right_index -= 1; } } fn current_backward(&self) -> Option<&'a SharedPointer> { if self.non_empty() { IterPtr::current(self.stack_backward.as_ref().unwrap()) } else { None } } } impl<'a, T, P> Iterator for IterPtr<'a, T, P> where P: SharedPointerKind, { type Item = &'a SharedPointer; fn next(&mut self) -> Option<&'a SharedPointer> { self.init_if_needed(false); let current = self.current_forward(); self.advance_forward(); current } fn size_hint(&self) -> (usize, Option) { let len = self.right_index - self.left_index; (len, Some(len)) } } impl<'a, T, P> DoubleEndedIterator for IterPtr<'a, T, P> where P: SharedPointerKind, { fn next_back(&mut self) -> Option<&'a SharedPointer> { self.init_if_needed(true); let current = self.current_backward(); self.advance_backward(); current } } impl<'a, T, P> ExactSizeIterator for IterPtr<'a, T, P> where P: SharedPointerKind {} #[cfg(feature = "serde")] pub mod serde { use super::*; use ::serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; use ::serde::ser::{Serialize, Serializer}; use core::fmt; use core::marker::PhantomData; impl Serialize for Vector where T: Serialize, P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } impl<'de, T, P> Deserialize<'de> for Vector where T: Deserialize<'de>, P: SharedPointerKind, { fn deserialize>(deserializer: D) -> Result, D::Error> { deserializer .deserialize_seq(VectorVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData }) } } struct VectorVisitor { _phantom_t: PhantomData, _phantom_p: PhantomData

, } impl<'de, T, P> Visitor<'de> for VectorVisitor where T: Deserialize<'de>, P: SharedPointerKind, { type Value = Vector; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { let mut vector = Vector::new_with_ptr_kind(); while let Some(value) = seq.next_element()? { vector.push_back_mut(value); } Ok(vector) } } } #[cfg(test)] mod test; rpds-1.1.0/src/vector/test.rs000064400000000000000000000470461046102023000142240ustar 00000000000000/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; use alloc::string::String; use pretty_assertions::assert_eq; use static_assertions::assert_impl_all; assert_impl_all!(VectorSync: Send, Sync); #[allow(dead_code)] fn compile_time_macro_vector_sync_is_send_and_sync() -> impl Send + Sync { vector_sync!(0) } impl PartialEq for Node where P: SharedPointerKind, { fn eq(&self, other: &Node) -> bool { match (self, other) { (Node::Branch(v), Node::Branch(vo)) => v == vo, (Node::Leaf(v), Node::Leaf(vo)) => v == vo, _ => false, } } } impl Eq for Node where P: SharedPointerKind {} mod node { use super::*; use pretty_assertions::assert_eq; #[test] fn test_new_empty_branch() { let node: Node = Node::new_empty_branch(); match node { Node::Branch(a) => { assert_eq!(a.len(), 0); assert_eq!(a.capacity(), 0, "Capacity of the branch array is wasteful"); } Node::Leaf(_) => panic!("Invalid node type"), } } #[test] fn test_new_empty_leaf() { let node: Node = Node::new_empty_leaf(); match node { Node::Leaf(a) => { assert_eq!(a.len(), 0); assert_eq!(a.capacity(), 0, "Capacity of the leaf array is wasteful"); } Node::Branch(_) => panic!("Invalid node type"), } } #[test] fn test_drop_last_single_level() { let mut empty_leaf: Node = Node::new_empty_leaf(); let mut empty_branch: Node = Node::new_empty_branch(); let mut singleton_node: Node = vector![0].root.as_ref().clone(); let mut one_level_node: Node = vector![0, 1].root.as_ref().clone(); assert!(empty_leaf.drop_last()); assert!(empty_branch.drop_last()); assert!(singleton_node.drop_last()); assert!(!one_level_node.drop_last()); assert_eq!(one_level_node.used(), 1); } #[test] fn test_drop_last_multi_level() { let mut node_three: Node = Vector::new_with_bits(1).push_back(0).push_back(1).push_back(2).root.as_ref().clone(); let mut node_four: Node = Vector::new_with_bits(1) .push_back(0) .push_back(1) .push_back(2) .push_back(3) .root .as_ref() .clone(); let node_three_after_drop = { let a_leaf = vec![SharedPointer::new(0), SharedPointer::new(1)]; let leaf = Node::Leaf(a_leaf); let a_branch = vec![SharedPointer::new(leaf)]; Node::Branch(a_branch) }; let node_four_after_drop = { let a_leaf_0 = vec![SharedPointer::new(0), SharedPointer::new(1)]; let leaf_0 = Node::Leaf(a_leaf_0); let a_leaf_1 = { let mut a = Vec::with_capacity(2); a.push(SharedPointer::new(2)); a }; let leaf_1 = Node::Leaf(a_leaf_1); let a_branch = vec![SharedPointer::new(leaf_0), SharedPointer::new(leaf_1)]; Node::Branch(a_branch) }; assert!(!node_three.drop_last()); assert_eq!(node_three, node_three_after_drop); assert!(!node_four.drop_last()); assert_eq!(node_four, node_four_after_drop); } } mod iter { use super::*; use pretty_assertions::assert_eq; #[test] fn test_iter_empty() { let vector: Vector = Vector::new(); for _ in vector.iter() { panic!("iterator should be empty"); } } #[test] fn test_iter_empty_backwards() { let vector: Vector = Vector::new(); for _ in vector.iter().rev() { panic!("iterator should be empty"); } } #[test] fn test_iter_big_vector() { let limit = 32 * 32 * 32 + 1; let mut vector = Vector::new(); let mut expected = 0; let mut left = limit; for i in 0..limit { vector = vector.push_back(i); } for v in vector.iter() { left -= 1; assert!(left >= 0); assert_eq!(*v, expected); expected += 1; } assert_eq!(left, 0); } #[test] fn test_iter_big_vector_backwards() { let limit = 32 * 32 * 32 + 1; let mut vector = Vector::new(); let mut expected = limit - 1; let mut left = limit; for i in 0..limit { vector = vector.push_back(i); } for v in vector.iter().rev() { left -= 1; assert!(left >= 0); assert_eq!(*v, expected); expected -= 1; } assert_eq!(left, 0); } #[test] fn test_iter_backwards() { let vector = vector![0, 1, 2, 3]; let mut expected = 3; let mut left = 4; for n in vector.iter().rev() { left -= 1; assert!(left >= 0); assert_eq!(*n, expected); expected -= 1; } assert_eq!(left, 0); } #[test] fn test_iter_both_directions() { let vector = vector![0, 1, 2, 3, 4, 5]; let mut iterator = vector.iter(); assert_eq!(iterator.next(), Some(&0)); assert_eq!(iterator.next_back(), Some(&5)); assert_eq!(iterator.next_back(), Some(&4)); assert_eq!(iterator.next(), Some(&1)); assert_eq!(iterator.next(), Some(&2)); assert_eq!(iterator.next_back(), Some(&3)); assert_eq!(iterator.next_back(), None); assert_eq!(iterator.next(), None); } #[test] fn test_iter_size_hint() { let vector = vector![0, 1, 2]; let mut iterator = vector.iter(); assert_eq!(iterator.size_hint(), (3, Some(3))); iterator.next(); assert_eq!(iterator.size_hint(), (2, Some(2))); iterator.next_back(); assert_eq!(iterator.size_hint(), (1, Some(1))); iterator.next_back(); assert_eq!(iterator.size_hint(), (0, Some(0))); } #[test] fn test_into_iterator() { let vector = vector![0, 1, 2, 3]; let mut left = 4; for (expected, n) in vector.into_iter().enumerate() { left -= 1; assert!(left >= 0); assert_eq!(*n, expected); } assert_eq!(left, 0); } } mod internal { use super::*; use pretty_assertions::assert_eq; fn dummy_vector_with_length(len: usize) -> Vector { let mut v = Vector::new_with_bits(5); v.length = len; v } #[test] fn test_degree() { use vector_utils::degree; assert_eq!(degree(1), 2); assert_eq!(degree(2), 4); assert_eq!(degree(3), 8); assert_eq!(degree(4), 16); assert_eq!(degree(5), 32); } #[test] fn test_height() { assert_eq!(dummy_vector_with_length(0).height(), 0); assert_eq!(dummy_vector_with_length(5).height(), 0); assert_eq!(dummy_vector_with_length(32).height(), 0); assert_eq!(dummy_vector_with_length(33).height(), 1); assert_eq!(dummy_vector_with_length(64).height(), 1); assert_eq!(dummy_vector_with_length(128).height(), 1); assert_eq!(dummy_vector_with_length(512).height(), 1); assert_eq!(dummy_vector_with_length(1024).height(), 1); assert_eq!(dummy_vector_with_length(1025).height(), 2); assert_eq!(dummy_vector_with_length(32_768).height(), 2); assert_eq!(dummy_vector_with_length(32_769).height(), 3); assert_eq!(dummy_vector_with_length(1_048_576).height(), 3); assert_eq!(dummy_vector_with_length(1_048_577).height(), 4); } #[test] fn test_mask() { use vector_utils::mask; assert_eq!(mask(1), 0b00001); assert_eq!(mask(2), 0b00011); assert_eq!(mask(3), 0b00111); assert_eq!(mask(4), 0b01111); assert_eq!(mask(5), 0b11111); } #[allow(clippy::unusual_byte_groupings)] #[test] fn test_bucket() { use vector_utils::bucket; assert_eq!(bucket(5, 0b_00100_00011_00010_00001, 0), 0b00001); assert_eq!(bucket(5, 0b_00100_00011_00010_00001, 1), 0b00010); assert_eq!(bucket(5, 0b_00100_00011_00010_00001, 2), 0b00011); assert_eq!(bucket(5, 0b_00100_00011_00010_00001, 3), 0b00100); } #[test] fn test_compress_root() { let empty_leaf: Node = Node::new_empty_leaf(); let empty_branch: Node = Node::new_empty_branch(); let singleton_leaf: Node = vector![0].root.as_ref().clone(); let compressed_branch: Node = Vector::new_with_bits(1).push_back(0).push_back(1).push_back(3).root.as_ref().clone(); let (uncompressed_branch, uncompressed_branch_leaf) = { let leaf: Node<_, RcK> = Vector::new_with_bits(1).push_back(0).push_back(1).root.as_ref().clone(); let a_branch = { let mut a = Vec::with_capacity(2); a.push(SharedPointer::new(leaf.clone())); a }; (Node::Branch(a_branch), leaf) }; assert_eq!(Vector::compress_root(&mut empty_leaf.clone()), None); assert_eq!(Vector::compress_root(&mut empty_branch.clone()), None); assert_eq!(Vector::compress_root(&mut singleton_leaf.clone()), None); assert_eq!(Vector::compress_root(&mut compressed_branch.clone()), None); assert_eq!( Vector::compress_root(&mut uncompressed_branch.clone()), Some(SharedPointer::new(uncompressed_branch_leaf)), ); } #[test] fn test_root_max_capacity() { assert_eq!(dummy_vector_with_length(0).root_max_capacity(), 32); assert_eq!(dummy_vector_with_length(5).root_max_capacity(), 32); assert_eq!(dummy_vector_with_length(32).root_max_capacity(), 32); assert_eq!(dummy_vector_with_length(33).root_max_capacity(), 1024); assert_eq!(dummy_vector_with_length(1024).root_max_capacity(), 1024); assert_eq!(dummy_vector_with_length(1025).root_max_capacity(), 32_768); assert_eq!(dummy_vector_with_length(32_768).root_max_capacity(), 32_768); assert_eq!(dummy_vector_with_length(32_769).root_max_capacity(), 1_048_576); } #[test] fn test_is_root_full() { assert!(!dummy_vector_with_length(0).is_root_full()); assert!(!dummy_vector_with_length(5).is_root_full()); assert!(dummy_vector_with_length(32).is_root_full()); assert!(!dummy_vector_with_length(33).is_root_full()); assert!(dummy_vector_with_length(1024).is_root_full()); assert!(!dummy_vector_with_length(1025).is_root_full()); assert!(dummy_vector_with_length(32_768).is_root_full()); assert!(!dummy_vector_with_length(32_769).is_root_full()); } } #[test] fn test_macro_vector() { let vector_1 = Vector::new().push_back(1); let vector_1_2_3 = Vector::new().push_back(1).push_back(2).push_back(3); assert_eq!(Vector::::new(), vector![]); assert_eq!(vector_1, vector![1]); assert_eq!(vector_1_2_3, vector![1, 2, 3]); } #[test] fn test_push_back_adds_element() { let limit = 32 * 32 * 32 + 1; let mut vector: Vector = Vector::new(); for i in 0..limit { vector = vector.push_back(-i); assert_eq!(vector.get(i as usize), Some(&-i)); } } #[test] fn test_push_back_maintains_size() { let limit = 128; let mut vector: Vector = Vector::new(); for i in 0..limit { assert_eq!(vector.len(), i as usize); vector = vector.push_back(-i); } assert_eq!(vector.len(), limit as usize); } #[test] fn test_drop_last_drops_last_element() { let limit = 4 * 4 * 4 * 4 + 1; let mut vector: Vector = Vector::new_with_bits(2); let mut vectors = Vec::with_capacity(limit); for i in 0..limit { vector = vector.push_back(2 * i as i32); vectors.push(vector.clone()); } for _ in 0..limit { let v = vectors.pop().unwrap(); assert_eq!(vector, v); vector = vector.drop_last().unwrap(); } assert_eq!(vector, Vector::new()); } #[test] fn test_drop_last_keeps_vector_consistent() { let limit = 4 * 4 * 4 * 4 * 4 * 4 + 1; let mut vector: Vector = Vector::new_with_bits(2); for i in 0..limit { vector = vector.push_back(2 * i as i32); } for _ in 0..(limit / (4 * 4)) { vector = vector.drop_last().unwrap(); } let new_len = limit - limit / (4 * 4); for i in 0..new_len { assert_eq!(vector.get(i).unwrap(), &(2 * i as i32)); } assert_eq!(vector.get(new_len), None); } #[test] fn test_drop_last_maintains_size() { let limit = 128; let mut vector: Vector = Vector::new(); for i in 0..limit { vector = vector.push_back(-i); } for i in 0..limit { assert_eq!(vector.len(), (limit - i) as usize); vector = vector.drop_last().unwrap(); } assert_eq!(vector.len(), 0); } #[test] fn test_drop_last_on_empty_vector() { let vector: Vector = Vector::new(); assert_eq!(vector.drop_last(), None); } #[test] fn test_set_overwrites() { let limit = 32 * 32 + 1; let mut vector: Vector = Vector::new(); for i in 0..limit { vector = vector.push_back(-i); } vector = vector.set(834, 0).unwrap(); assert_eq!(vector.get(833), Some(&-833)); assert_eq!(vector.get(834), Some(&0)); assert_eq!(vector.get(835), Some(&-835)); assert_eq!(vector.get(limit as usize), None); } #[test] fn test_set_maintains_size() { let limit = 32 * 32 * 32; let mut vector: Vector = Vector::new(); for i in 0..limit { vector = vector.push_back(-i); } for i in 0..limit { vector = vector.set(i as usize, i * i).unwrap(); assert_eq!(vector.len(), limit as usize); } } #[test] fn test_set_out_of_bounds() { let empty_vector: Vector = Vector::new(); let singleton_vector: Vector = vector![0]; assert_eq!(empty_vector.set(0, 0), None); assert_eq!(singleton_vector.set(1, 0), None); } #[test] fn test_get() { let limit = 32 * 32 * 32 + 1; let mut vector = Vector::new(); for i in 0..limit { vector = vector.push_back(i + 1); } assert_eq!(vector.get(0), Some(&1)); assert_eq!(vector.get(2020), Some(&2021)); assert_eq!(vector.get(limit - 1), Some(&limit)); assert_eq!(vector.get(limit), None); } #[test] fn test_index() { let vector = vector![10, 11, 12]; assert_eq!(vector[0], 10); assert_eq!(vector[1], 11); assert_eq!(vector[2], 12); } #[test] fn test_first() { let empty_vector: Vector = Vector::new(); let vector = vector![1]; assert_eq!(empty_vector.first(), None); assert_eq!(vector.first(), Some(&1)); } #[test] fn test_last() { let empty_vector: Vector = Vector::new(); let vector = vector![1, 2]; assert_eq!(empty_vector.last(), None); assert_eq!(vector.last(), Some(&2)); } #[test] fn test_from_iterator() { let vec: Vec = vec![10, 11, 12, 13]; let vector: Vector = vec.iter().copied().collect(); assert!(vec.iter().eq(vector.iter())); } #[test] fn test_default() { let vector: Vector = Vector::default(); assert_eq!(vector.len(), 0); } #[test] fn test_display() { let empty_vector: Vector = Vector::new(); let singleton_vector = vector!["hello"]; let vector = vector![0, 1, 2, 3]; assert_eq!(format!("{}", empty_vector), "[]"); assert_eq!(format!("{}", singleton_vector), "[hello]"); assert_eq!(format!("{}", vector), "[0, 1, 2, 3]"); } #[test] fn test_eq() { let vector_1 = vector!["a", "a"]; let vector_1_prime = vector!["a", "a"]; let vector_2 = vector!["a", "b"]; let vector_3 = vector!["a", "b", "c"]; assert_ne!(vector_1, vector_2); assert_ne!(vector_2, vector_3); assert_eq!(vector_1, vector_1); assert_eq!(vector_1, vector_1_prime); assert_eq!(vector_2, vector_2); // We also check this since `assert_ne!()` does not call `ne`. assert!(vector_1.ne(&vector_2)); assert!(vector_2.ne(&vector_3)); } #[test] fn test_eq_pointer_kind_consistent() { let vector_a = vector!["a"]; let vector_a_sync = vector_sync!["a"]; let vector_b = vector!["b"]; let vector_b_sync = vector_sync!["b"]; assert!(vector_a == vector_a_sync); assert!(vector_a != vector_b_sync); assert!(vector_b == vector_b_sync); } #[test] fn test_partial_ord() { let vector_1 = vector!["a"]; let vector_1_prime = vector!["a"]; let vector_2 = vector!["b"]; let vector_3 = vector![0.0]; let vector_4 = vector![core::f32::NAN]; assert_eq!(vector_1.partial_cmp(&vector_1_prime), Some(Ordering::Equal)); assert_eq!(vector_1.partial_cmp(&vector_2), Some(Ordering::Less)); assert_eq!(vector_2.partial_cmp(&vector_1), Some(Ordering::Greater)); assert_eq!(vector_3.partial_cmp(&vector_4), None); } #[test] fn test_ord() { let vector_1 = vector!["a"]; let vector_1_prime = vector!["a"]; let vector_2 = vector!["b"]; assert_eq!(vector_1.cmp(&vector_1_prime), Ordering::Equal); assert_eq!(vector_1.cmp(&vector_2), Ordering::Less); assert_eq!(vector_2.cmp(&vector_1), Ordering::Greater); } #[test] fn test_ord_pointer_kind_consistent() { let vector_a = vector!["a"]; let vector_a_sync = vector_sync!["a"]; let vector_b = vector!["b"]; let vector_b_sync = vector_sync!["b"]; assert!(vector_a <= vector_a_sync); assert!(vector_a < vector_b_sync); assert!(vector_b >= vector_b_sync); assert!(vector_a_sync >= vector_a); assert!(vector_b_sync > vector_a); assert!(vector_b_sync <= vector_b); } fn hash(vector: &Vector) -> u64 { #[allow(deprecated)] let mut hasher = core::hash::SipHasher::new(); vector.hash(&mut hasher); hasher.finish() } #[test] fn test_hash() { let vector_1 = vector!["a"]; let vector_1_prime = vector!["a"]; let vector_2 = vector!["a", "b"]; assert_eq!(hash(&vector_1), hash(&vector_1)); assert_eq!(hash(&vector_1), hash(&vector_1_prime)); assert_ne!(hash(&vector_1), hash(&vector_2)); } #[test] fn test_hash_pointer_kind_consistent() { let vector = vector!["a"]; let vector_sync = vector_sync!["a"]; assert_eq!(hash(&vector), hash(&vector_sync)); } #[test] fn test_clone() { let vector = vector!["hello", "there"]; let clone = vector.clone(); assert_eq!(clone.len(), vector.len()); assert!(clone.iter().eq(vector.iter())); } #[test] fn test_index_mut() { let v1 = vector![ String::from("This"), String::from("is"), String::from("where"), String::from("the"), String::from("fun"), String::from("begins!") ]; let mut v2 = v1.clone(); let expected1 = vector!["This", "is", "where", "the", "fun", "begins!"]; let expected2 = vector!["That", "is", "where", "the", "cloning", "BEGINS!"]; v2[0] = "That".into(); v2[4] = String::from("cloning"); v2[5].make_ascii_uppercase(); let len = v2.len(); assert_eq!(v2.get_mut(len), None); assert_eq!(v1, expected1); assert_eq!(v2, expected2); } #[cfg(feature = "serde")] #[test] fn test_serde() { use bincode::{deserialize, serialize}; let vector: Vector = vector![5, 6, 7, 8]; let encoded = serialize(&vector).unwrap(); let decoded: Vector = deserialize(&encoded).unwrap(); assert_eq!(vector, decoded); }