├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches └── hashmap.rs ├── src ├── hashentry.rs ├── hashiter.rs ├── hashmap.rs ├── hashmap_serde.rs ├── leapiter.rs ├── leapmap.rs ├── leapmap_serde.rs ├── leapref.rs ├── lib.rs └── util.rs └── tests ├── basic.rs ├── cuckoo.rs └── hashmap.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | # 15 | # already existing elements were commented out 16 | 17 | /target 18 | #Cargo.lock 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leapfrog" 3 | version = "0.3.2" 4 | edition = "2021" 5 | authors = ["Rob Clucas "] 6 | license = "MIT OR Apache-2.0" 7 | 8 | readme = "README.md" 9 | description = "A fast, lock-free concurrent hash map" 10 | repository = "https://github.com/robclu/leapfrog" 11 | 12 | keywords = ["hashmap","concurrent", "map"] 13 | categories = ["concurrency", "data-structures"] 14 | 15 | [features] 16 | default = [] 17 | serde = ["dep:serde_crate"] 18 | stable_alloc = ["dep:allocator-api2"] 19 | 20 | [profile.release] 21 | debug = false 22 | lto = true 23 | 24 | [dependencies] 25 | allocator-api2 = { version = "0.2.15", optional = true } 26 | atomic = "0.5.3" 27 | serde_crate = { package = "serde", version = "1.0.136", features = ["derive"], optional = true } 28 | 29 | [dev-dependencies] 30 | core_affinity = "0.8.1" 31 | criterion = "0.5.1" 32 | rand = "0.8.4" 33 | serde_json = "1.0.50" 34 | 35 | [[bench]] 36 | name = "hashmap" 37 | harness = false 38 | 39 | [package.metadata.docs.rs] 40 | features = ["serde", "stable_alloc"] 41 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Jon Gjengset 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Rob Clucas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![version](https://img.shields.io/crates/v/leapfrog)](https://crates.io/crates/leapfrog) [![documentation](https://docs.rs/leapfrog/badge.svg)](https://docs.rs/leapfrog) 2 | 3 | # Leapfrog 4 | 5 | The leapfrog crate contains two hash map implementations, `HashMap`, which is a single-threaded HashMap, and `LeapMap`, which is fast, concurrent version of the `HashMap`, where all operations can be performed concurrently from any number of threads. If the key and value types support atomic operations then the map is lock-free, and when there is no atomic support for either the key or value, then operations within the map on that non-atomic type use an efficient spinlock and the locking is very fine-grained. 6 | 7 | The performance of both maps is good on the benchmarks it's been tested on. The `HashMap` is between 1.2 and 1.5x the `std::collections::HashMap`. Compared to `RwLock`, on 16 cores the `LeapMap` is around 13.5x faster. It also scales very well up to high core counts, for multiple workloads. Benchmark results can be found at [rust hashmap benchmarks](https://github.com/robclu/conc-map-bench). These bechmarks, however, are limited in the use cases that they cover, and should be extended to cover a much wider scope. Nevertheless, for those bechmarks, the `LeapMap` is the fastest map. 8 | 9 | For more details on the API and the differences compared to `std::collections::HashMap`, please see the crate documentation. 10 | 11 | # Recent Changes 12 | 13 | The implementations of these maps have recently been updated to store keys, so the API for both is much closer to `std::collections::HashMap`. There is now also support for serde with the optional "serde" feature. The maps also now have iterator support. The default hasher has been changed to be the DOS resistant hasher from the standard library. 14 | 15 | The "stable_alloc" feature flag has been added to allow the crate to be used with the stable toolchain. 16 | 17 | # Current Limitations 18 | 19 | These is not yet support for rayon, and there are not yet `Set` versions of the `Map`s. 20 | 21 | # Performance 22 | 23 | A snapshot of the results for the LeapMap on a read heavy workload using a 16 core AMD 3950x CPU are the following (with throughput in Mops/second, cores in brackets, and performance realtive to `std::collections::HashMap` with RwLock): 24 | 25 | | Map | Throughput (1) | Relative (1) | Throughput (16) | Relative (16) | 26 | |------------------|----------------|--------------|-----------------|---------------| 27 | | RwLock + HashMap | 19.4 | 1.0 | 11.7 | 0.60 | 28 | | Flurry | 11.2 | 0.58 | 80.8 | 4.16 | 29 | | DashMap | 14.1 | 0.72 | 87.5 | 4.51 | 30 | | LeapMap | 17.8 | 0.92 | 148.0 | 7.62 | 31 | 32 | On a 12-core M2 Max the results are the following (again values are all relative to `std::collections::HashMap`): 33 | 34 | | Map | Throughput (1) | Relative (1) | Throughput (12) | Relative (12) | 35 | |------------------|----------------|--------------|-----------------|---------------| 36 | | RwLock + HashMap | 21.2 | 1.0 | 3.4 | 0.16 | 37 | | Flurry | 8.0 | 0.37 | 64.5 | 3.04 | 38 | | DashMap | 10.4 | 0.49 | 42.2 | 2.00 | 39 | | Evmap | 13.5 | 0.64 | 18.7 | 0.88 | 40 | | LeapMap | 13.9 | 0.66 | 106.1 | 5.00 | 41 | 42 | On benchmarks of other use cases, leapfrog's `LeapMap` is faster by even higher factors. 43 | 44 | For the single threaded leapfrog `HashMap`, a benchmark of random inserts and deletes (a port of [this](https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-03-03-result-RandomInsertErase/) benchmark of C++ hashmaps) has the rust std HashMap at around 40Mops/s and the leapfrog HashMap at around 50Mops/s, with default hasher from the standard library, which are competitive with the C++ hash maps from the linked benchmark. 45 | 46 | # Probing 47 | 48 | Both maps use the same leapfrog probing strategy, for which you can find more information on Jeff Preshing's [leapfrog probing blog post](https://preshing.com/20160314/leapfrog-probing/). A c++ implementation of the map can be found at his [Junction library](https://github.com/preshing/junction), which was the starting point for this implementation. Those maps don't have support for keys and therefore collisions, which these maps do. The probing strategy requires 8 bytes per key-value pair and and additional 8 bytes are stored for the hashed key so that it doesn't have to be rehashed for comparisons. 49 | 50 | # Features 51 | 52 | There is optional support for serde, via the `"serde"` feature. 53 | 54 | ## Usage with stable 55 | 56 | This crate requires the `allocator_api` feature, which is only available on nightly. To enable use of the crate with the stable toolchain, the `"stable_alloc"` feature has been added. 57 | 58 | If/when the `allocator_api` feature is no longer experimental, this feature flag will be removed. 59 | 60 | # License 61 | 62 | This crate is licensed under either the Apache License, Version 2.0, see [here](LICENSE-APACHE) or [here](http://www.apache.org/licenses/LICENSE-2.0) or the MIT license see [here](LICENSE-MIT) or [here](http://opensource.org/licenses/MIT), at your chosing. It can be used freely for both commercial and non-commercial applications, so long as you publish the contents of your chosen license file somewhere in your documentation. -------------------------------------------------------------------------------- /benches/hashmap.rs: -------------------------------------------------------------------------------- 1 | use criterion::Criterion; 2 | use criterion::Throughput; 3 | use criterion::{criterion_group, criterion_main}; 4 | use rand::{thread_rng, Rng}; 5 | use std::hash::BuildHasherDefault; 6 | 7 | const NUM_KEYS: usize = 1 << 14; 8 | const NUM_OPS: u64 = 10_000_000; 9 | 10 | type HashFn = std::collections::hash_map::DefaultHasher; 11 | 12 | fn bench_leapfrog_hashmap(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("leapfrog_hashmap"); 14 | group.throughput(Throughput::Elements(NUM_OPS * 6 * 2_u64)); 15 | group.sample_size(10); 16 | group.bench_function("insert_and_remove", |b| { 17 | let mut map = leapfrog::HashMap::with_capacity_and_hasher( 18 | NUM_KEYS, 19 | BuildHasherDefault::::default(), 20 | ); 21 | 22 | let mut rng = thread_rng(); 23 | let mut bits: u64 = rng.gen(); 24 | let mut mask = 0u64; 25 | 26 | b.iter(|| { 27 | for _ in 0..6 { 28 | // Add 4 random bits 29 | mask <<= 4; 30 | mask |= bits & 0b00001111; 31 | bits >>= 4; 32 | 33 | for i in 0..NUM_OPS { 34 | let key: u64 = rng.gen::() & mask; 35 | map.insert(key, i); 36 | let key: u64 = rng.gen::() & mask; 37 | map.remove(&key); 38 | } 39 | } 40 | }) 41 | }); 42 | group.finish(); 43 | } 44 | 45 | fn bench_std_hashmap(c: &mut Criterion) { 46 | let mut group = c.benchmark_group("std_hashmap"); 47 | group.throughput(Throughput::Elements(NUM_OPS * 6 * 2_u64)); 48 | group.sample_size(10); 49 | group.bench_function("insert_and_remove", |b| { 50 | let mut map = std::collections::HashMap::with_capacity_and_hasher( 51 | NUM_KEYS, 52 | BuildHasherDefault::::default(), 53 | ); 54 | 55 | let mut rng = thread_rng(); 56 | let mut bits: u64 = rng.gen(); 57 | let mut mask = 0u64; 58 | 59 | b.iter(|| { 60 | for _ in 0..6 { 61 | // Add 4 random bits 62 | mask <<= 4; 63 | mask |= bits & 0b00001111; 64 | bits >>= 4; 65 | 66 | for i in 0..NUM_OPS { 67 | let key: u64 = rng.gen::() & mask; 68 | map.insert(key, i); 69 | let key: u64 = rng.gen::() & mask; 70 | map.remove(&key); 71 | } 72 | } 73 | }) 74 | }); 75 | group.finish(); 76 | } 77 | 78 | fn bench_std_hashmap_rwlock(c: &mut Criterion) { 79 | let mut group = c.benchmark_group("std_hashmap_rw_lock"); 80 | group.throughput(Throughput::Elements(NUM_OPS * 6 * 2_u64)); 81 | group.sample_size(10); 82 | group.bench_function("insert_and_remove", |b| { 83 | let map = std::sync::RwLock::new(std::collections::HashMap::with_capacity_and_hasher( 84 | NUM_KEYS, 85 | BuildHasherDefault::::default(), 86 | )); 87 | 88 | let mut rng = thread_rng(); 89 | let mut bits: u64 = rng.gen(); 90 | let mut mask = 0u64; 91 | 92 | b.iter(|| { 93 | let mut map_write = map.write().unwrap(); 94 | for _ in 0..6 { 95 | // Add 4 random bits 96 | mask <<= 4; 97 | mask |= bits & 0b00001111; 98 | bits >>= 4; 99 | 100 | for i in 0..NUM_OPS { 101 | let key: u64 = rng.gen::() & mask; 102 | map_write.insert(key, i); 103 | let key: u64 = rng.gen::() & mask; 104 | map_write.remove(&key); 105 | } 106 | } 107 | }) 108 | }); 109 | group.finish(); 110 | } 111 | 112 | criterion_group!( 113 | benches, 114 | bench_leapfrog_hashmap, 115 | bench_std_hashmap, 116 | bench_std_hashmap_rwlock 117 | ); 118 | criterion_main!(benches); 119 | -------------------------------------------------------------------------------- /src/hashentry.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | hashmap::{DefaultHash, HashMap}, 3 | Value, 4 | }; 5 | 6 | use core::hash::{BuildHasher, BuildHasherDefault, Hash}; 7 | 8 | #[cfg(feature = "stable_alloc")] 9 | use allocator_api2::alloc::{Allocator, Global}; 10 | #[cfg(not(feature = "stable_alloc"))] 11 | use core::alloc::Allocator; 12 | #[cfg(not(feature = "stable_alloc"))] 13 | use std::alloc::Global; 14 | 15 | /// A view into a single entry in a map, which may either be vacant or occupied. 16 | /// 17 | /// This enum is constructed from the `entry` method on a [`HashMap`]. 18 | pub enum Entry<'a, K, V, H = BuildHasherDefault, A: Allocator = Global> { 19 | /// An entry for which the cell is occupied. 20 | Occupied(OccupiedEntry<'a, K, V, H, A>), 21 | /// An entry for which the cell is vacant. 22 | Vacant(VacantEntry<'a, K, V, H, A>), 23 | } 24 | 25 | impl<'a, K, V, H, A> Entry<'a, K, V, H, A> 26 | where 27 | K: Eq + Hash + Clone + 'a, 28 | V: Value + 'a, 29 | H: BuildHasher + Default, 30 | A: Allocator, 31 | { 32 | /// Ensures a value is in the entry by inserting the default if empty, and 33 | /// returns a mutable reference to the value in the entry. 34 | /// 35 | /// # Examples 36 | /// 37 | /// ``` 38 | /// use leapfrog::HashMap; 39 | /// let mut map: HashMap = HashMap::new(); 40 | /// 41 | /// map.entry(1).or_insert(3); 42 | /// assert_eq!(map.get(&1), Some(&3)); 43 | /// 44 | /// *map.entry(1).or_insert(10) *= 2; 45 | /// assert_eq!(map.get(&1), Some(&6)); 46 | /// ``` 47 | pub fn or_insert(self, default: V) -> &'a mut V { 48 | match self { 49 | Entry::Occupied(o) => o.into_mut(), 50 | Entry::Vacant(mut v) => v.insert(default), 51 | } 52 | } 53 | 54 | /// Ensures a value is in the entry by inserting the result of the default 55 | /// function if empty, and returns a mutable reference to the value in the entry. 56 | /// 57 | /// # Examples 58 | /// 59 | /// ``` 60 | /// use leapfrog::HashMap; 61 | /// let mut map: HashMap = HashMap::new(); 62 | /// let new = 12u32; 63 | /// 64 | /// map.entry(1).or_insert_with(|| new); 65 | /// assert_eq!(map.get(&1), Some(&12)); 66 | /// ``` 67 | pub fn or_insert_with V>(self, default: F) -> &'a mut V { 68 | match self { 69 | Entry::Occupied(o) => o.into_mut(), 70 | Entry::Vacant(mut v) => v.insert(default()), 71 | } 72 | } 73 | 74 | /// Ensures a value is in the entry by inserting, if empty, the result of the 75 | /// default function. This method allows for generating key-derived values for 76 | /// insertion by providing the default function a reference to the key that was 77 | /// moved during the `.entry(key)` method call. 78 | /// 79 | /// The reference to the moved key is provided so that cloning or copying the 80 | /// key is unnecessary, unlike with `.or_insert_with(|| ... )`. 81 | /// 82 | /// # Examples 83 | /// 84 | /// ``` 85 | /// use leapfrog::HashMap; 86 | /// let mut map: HashMap = HashMap::new(); 87 | /// 88 | /// map.entry(1).or_insert_with_key(|key| *key * 2); 89 | /// 90 | /// assert_eq!(map.get(&1), Some(&2)); 91 | /// ``` 92 | pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { 93 | match self { 94 | Entry::Occupied(o) => o.into_mut(), 95 | Entry::Vacant(mut v) => v.insert(default(v.key())), 96 | } 97 | } 98 | 99 | /// Returns a reference to this entry's key. 100 | /// 101 | /// # Examples 102 | /// 103 | /// ``` 104 | /// use leapfrog::HashMap; 105 | /// let mut map: HashMap = HashMap::new(); 106 | /// 107 | /// assert_eq!(map.entry(1).key(), &1); 108 | /// ``` 109 | pub fn key(&self) -> &K { 110 | match self { 111 | Entry::Occupied(o) => o.key(), 112 | Entry::Vacant(v) => v.key(), 113 | } 114 | } 115 | 116 | /// Provides in-place mutable access to an occupied entry before any potential 117 | /// inserts into the map. 118 | /// 119 | /// # Examples 120 | /// 121 | /// ``` 122 | /// use leapfrog::HashMap; 123 | /// let mut map: HashMap = HashMap::new(); 124 | /// 125 | /// map.entry(1) 126 | /// .and_modify(|e| *e += 1) 127 | /// .or_insert(42); 128 | /// assert_eq!(map.get(&1), Some(&42)); 129 | /// 130 | /// map.entry(1) 131 | /// .and_modify(|e| *e += 1) 132 | /// .or_insert(42); 133 | /// assert_eq!(map.get(&1), Some(&43)); 134 | /// ``` 135 | pub fn and_modify(self, f: F) -> Self { 136 | match self { 137 | Entry::Occupied(mut o) => { 138 | f(o.get_mut()); 139 | Entry::Occupied(o) 140 | } 141 | Entry::Vacant(v) => Entry::Vacant(v), 142 | } 143 | } 144 | 145 | /// Sets the value of the entry, and returns an [`OccupiedEntry`]. 146 | /// 147 | /// # Examples 148 | /// 149 | /// ``` 150 | /// use leapfrog::HashMap; 151 | /// use leapfrog::hashmap::Entry; 152 | /// 153 | /// let mut map = HashMap::::new(); 154 | /// let entry = map.entry(1).insert_entry(12); 155 | /// 156 | /// assert_eq!(entry.key(), &1); 157 | /// ``` 158 | pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, H, A> { 159 | match self { 160 | Entry::Occupied(mut o) => { 161 | let _old = o.insert(value); 162 | o 163 | } 164 | Entry::Vacant(v) => v.insert_entry(value), 165 | } 166 | } 167 | 168 | /// Ensures a value is in the entry by inserting the default value if empty, 169 | /// and returns a mutable reference to the value in the entry. 170 | /// 171 | /// # Examples 172 | /// 173 | /// ``` 174 | /// use leapfrog::HashMap; 175 | /// let mut map: HashMap = HashMap::new(); 176 | /// map.entry(1).or_default(); 177 | /// 178 | /// assert_eq!(*map.get(&1).unwrap(), u32::default()); 179 | /// ``` 180 | pub fn or_default(self) -> &'a mut V 181 | where 182 | V: Default, 183 | { 184 | self.or_insert(V::default()) 185 | } 186 | } 187 | 188 | /// A view into an occupied entry in a [`HashMap`]. It is part of the [`Entry`] enum. 189 | pub struct OccupiedEntry<'a, K, V, H, A: Allocator> { 190 | pub(crate) map: &'a mut HashMap, 191 | pub(crate) key: K, 192 | pub(crate) value: &'a mut V, 193 | } 194 | 195 | impl<'a, K, V, H, A> OccupiedEntry<'a, K, V, H, A> 196 | where 197 | K: Eq + Hash + Clone + 'a, 198 | V: Value + 'a, 199 | H: BuildHasher + Default, 200 | A: Allocator, 201 | { 202 | /// Gets a reference to the key in the entry. 203 | /// 204 | /// # Examples 205 | /// 206 | /// ``` 207 | /// let mut map = leapfrog::HashMap::::new(); 208 | /// map.entry(1).or_insert(12); 209 | /// assert_eq!(map.entry(1).key(), &1); 210 | /// ``` 211 | pub fn key(&self) -> &K { 212 | &self.key 213 | } 214 | 215 | /// Takes ownership of the key and value from the map. 216 | /// 217 | /// # Examples 218 | /// 219 | /// ``` 220 | /// use leapfrog::HashMap; 221 | /// use leapfrog::hashmap::Entry; 222 | /// 223 | /// let mut map = HashMap::::new(); 224 | /// map.entry(1).or_insert(12); 225 | /// 226 | /// if let Entry::Occupied(o) = map.entry(1) { 227 | /// // Delete the entry from the map 228 | /// o.remove_entry(); 229 | /// } 230 | /// 231 | /// assert_eq!(map.contains_key(&1), false); 232 | /// ``` 233 | pub fn remove_entry(self) -> (K, V) { 234 | let value = self.map.remove(&self.key).unwrap(); 235 | (self.key, value) 236 | } 237 | 238 | /// Gets a reference to the value in the [`Entry`]. 239 | /// 240 | /// # Examples 241 | /// 242 | /// ``` 243 | /// use leapfrog::HashMap; 244 | /// use leapfrog::hashmap::Entry; 245 | /// 246 | /// let mut map = HashMap::::new(); 247 | /// map.entry(1).or_insert(12); 248 | /// 249 | /// if let Entry::Occupied(o) = map.entry(1) { 250 | /// assert_eq!(o.get(), &12); 251 | /// } 252 | /// ``` 253 | pub fn get(&self) -> &V { 254 | self.value 255 | } 256 | 257 | /// Gets a mutable reference to the value in the [`Entry`]. 258 | /// 259 | /// If you need a reference to an `OccupiedEntry` which may outlive the destruction 260 | /// of the [`Entry`] value, see `into_mut`. 261 | /// 262 | /// # Examples 263 | /// 264 | /// ``` 265 | /// use leapfrog::HashMap; 266 | /// use leapfrog::hashmap::Entry; 267 | /// 268 | /// let mut map = HashMap::::new(); 269 | /// map.entry(1).or_insert(12); 270 | /// 271 | /// assert_eq!(map.get(&1), Some(&12)); 272 | /// 273 | /// if let Entry::Occupied(mut o) = map.entry(1) { 274 | /// *o.get_mut() += 10; 275 | /// assert_eq!(*o.get(), 22); 276 | /// 277 | /// // We can use the same entry multiple times 278 | /// *o.get_mut() += 2; 279 | /// } 280 | /// 281 | /// assert_eq!(map.get(&1), Some(&24)); 282 | /// ``` 283 | pub fn get_mut(&mut self) -> &mut V { 284 | self.value 285 | } 286 | 287 | /// Converts the [`OccupiedEntry`] into a mutable reference to the value in 288 | /// the entry with a lifetime bound to the map itself. 289 | /// 290 | /// If you need multiple references to the `OccupiedEntry`, see `get_mut`. 291 | /// 292 | /// # Examples 293 | /// 294 | /// ``` 295 | /// use leapfrog::HashMap; 296 | /// use leapfrog::hashmap::Entry; 297 | /// 298 | /// let mut map = HashMap::::new(); 299 | /// map.entry(1).or_insert(12); 300 | /// 301 | /// assert_eq!(map.get(&1), Some(&12)); 302 | /// 303 | /// if let Entry::Occupied(o) = map.entry(1) { 304 | /// *o.into_mut() += 10; 305 | /// } 306 | /// 307 | /// assert_eq!(map.get(&1), Some(&22)); 308 | /// ``` 309 | pub fn into_mut(self) -> &'a mut V { 310 | self.value 311 | } 312 | 313 | /// Sets the value of the entry, and returns the entry's old value. 314 | /// 315 | /// # Examples 316 | /// 317 | /// ``` 318 | /// use leapfrog::HashMap; 319 | /// use leapfrog::hashmap::Entry; 320 | /// 321 | /// let mut map = HashMap::::new(); 322 | /// map.entry(1).or_insert(12); 323 | /// 324 | /// if let Entry::Occupied(mut o) = map.entry(1) { 325 | /// assert_eq!(o.insert(15), 12); 326 | /// } 327 | /// 328 | /// assert_eq!(map.get(&1), Some(&15)); 329 | /// ``` 330 | pub fn insert(&mut self, value: V) -> V { 331 | let old = *self.value; 332 | *self.value = value; 333 | old 334 | } 335 | 336 | /// Takes the value out of the entry and returns it. 337 | /// 338 | /// ``` 339 | /// use leapfrog::HashMap; 340 | /// use leapfrog::hashmap::Entry; 341 | /// 342 | /// let mut map = HashMap::::new(); 343 | /// map.entry(1).or_insert(12); 344 | /// 345 | /// if let Entry::Occupied(mut o) = map.entry(1) { 346 | /// assert_eq!(o.remove(), 12); 347 | /// } 348 | /// 349 | /// assert_eq!(map.contains_key(&1), false); 350 | /// ``` 351 | pub fn remove(self) -> V { 352 | self.map.remove(&self.key).unwrap() 353 | } 354 | } 355 | 356 | /// A view into a vacant entry in a [`HashMap`]. It is part of the [`Entry`] enum. 357 | pub struct VacantEntry<'a, K, V, H, A: Allocator> { 358 | pub(crate) map: &'a mut HashMap, 359 | pub(crate) key: K, 360 | } 361 | 362 | impl<'a, K, V, H, A> VacantEntry<'a, K, V, H, A> 363 | where 364 | K: Eq + Hash + Clone + 'a, 365 | V: Value + 'a, 366 | H: BuildHasher + Default, 367 | A: Allocator, 368 | { 369 | /// Gets a reference to the key in the entry. 370 | /// 371 | /// # Examples 372 | /// 373 | /// ``` 374 | /// let mut map = leapfrog::HashMap::::new(); 375 | /// assert_eq!(map.entry(1).key(), &1); 376 | /// ``` 377 | pub fn key(&self) -> &K { 378 | &self.key 379 | } 380 | 381 | /// Takes ownership of the key. 382 | /// 383 | /// # Examples 384 | /// 385 | /// ``` 386 | /// use leapfrog::HashMap; 387 | /// use leapfrog::hashmap::Entry; 388 | /// 389 | /// let mut map = HashMap::::new(); 390 | /// 391 | /// if let Entry::Vacant(v) = map.entry(1) { 392 | /// v.into_key(); 393 | /// } 394 | /// ``` 395 | pub fn into_key(self) -> K { 396 | self.key 397 | } 398 | 399 | /// Sets the value of the entry with the [`VacantEntry`]'s key, and returns a 400 | /// mutable reference to it. 401 | /// 402 | /// ``` 403 | /// use leapfrog::HashMap; 404 | /// use leapfrog::hashmap::Entry; 405 | /// 406 | /// let mut map = HashMap::::new(); 407 | /// 408 | /// if let Entry::Vacant(mut o) = map.entry(1) { 409 | /// o.insert(37); 410 | /// } 411 | /// 412 | /// assert_eq!(map.get(&1), Some(&37)); 413 | /// ``` 414 | pub fn insert(&mut self, value: V) -> &'a mut V { 415 | let _old = self.map.insert(self.key.clone(), value); 416 | self.map.get_mut(&self.key).unwrap() 417 | } 418 | 419 | /// Sets the value of the entry with the [`VacantEntry`]'s key, and returns an 420 | /// [`OccupiedEntry`]. 421 | /// 422 | /// ``` 423 | /// use leapfrog::HashMap; 424 | /// use leapfrog::hashmap::Entry; 425 | /// 426 | /// let mut map = HashMap::::new(); 427 | /// 428 | /// if let Entry::Vacant(o) = map.entry(1) { 429 | /// o.insert_entry(37); 430 | /// } 431 | /// 432 | /// assert_eq!(map.get(&1), Some(&37)); 433 | /// ``` 434 | pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, H, A> { 435 | let _old = self.map.insert(self.key.clone(), value); 436 | let value = self.map.get_mut(&self.key).unwrap(); 437 | OccupiedEntry { 438 | map: self.map, 439 | key: self.key, 440 | value, 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/hashiter.rs: -------------------------------------------------------------------------------- 1 | use crate::{hashmap::HashMap, Value}; 2 | 3 | use core::hash::{BuildHasher, Hash}; 4 | 5 | #[cfg(feature = "stable_alloc")] 6 | use allocator_api2::alloc::Allocator; 7 | #[cfg(not(feature = "stable_alloc"))] 8 | use std::alloc::Allocator; 9 | 10 | /// Iterator over a [`HashMap`] which yields key-value pairs. 11 | /// 12 | /// # Examples 13 | /// 14 | /// ``` 15 | /// use leapfrog::HashMap; 16 | /// 17 | /// let mut map = HashMap::new(); 18 | /// 19 | /// map.insert(12u32, 17u64); 20 | /// map.insert(42u32, 92u64); 21 | /// 22 | /// let pairs = map.into_iter().collect::>(); 23 | /// assert_eq!(pairs.len(), 2); 24 | /// ``` 25 | pub struct OwnedIter { 26 | map: HashMap, 27 | current: usize, 28 | } 29 | 30 | impl OwnedIter 31 | where 32 | K: Eq + Hash + Clone, 33 | V: Value, 34 | H: BuildHasher, 35 | A: Allocator, 36 | { 37 | pub(crate) fn new(map: HashMap) -> Self { 38 | Self { 39 | map, 40 | current: 0usize, 41 | } 42 | } 43 | } 44 | 45 | impl Iterator for OwnedIter 46 | where 47 | K: Eq + Hash + Clone, 48 | V: Value, 49 | H: BuildHasher + Default, 50 | A: Allocator, 51 | { 52 | type Item = (K, V); 53 | 54 | fn next(&mut self) -> Option { 55 | loop { 56 | let current = self.current; 57 | if let Some(cell) = self.map.get_cell_at_index(current) { 58 | self.current = current + 1; 59 | if cell.is_empty() || cell.value.is_null() { 60 | continue; 61 | } 62 | 63 | return Some((cell.key.clone(), cell.value)); 64 | } else { 65 | return None; 66 | } 67 | } 68 | } 69 | } 70 | 71 | unsafe impl Send for OwnedIter 72 | where 73 | K: Send, 74 | V: Send, 75 | H: BuildHasher + Send, 76 | A: Allocator + Send, 77 | { 78 | } 79 | 80 | unsafe impl Sync for OwnedIter 81 | where 82 | K: Sync, 83 | V: Sync, 84 | H: BuildHasher + Sync, 85 | A: Allocator + Sync, 86 | { 87 | } 88 | 89 | /// Iterator over a [`HashMap`] which yields immutable key-value reference pairs. 90 | /// 91 | /// # Examples 92 | /// 93 | /// ``` 94 | /// use leapfrog::HashMap; 95 | /// 96 | /// let mut map = HashMap::new(); 97 | /// 98 | /// map.insert(12, 17); 99 | /// map.insert(42, 23); 100 | /// 101 | /// assert_eq!(map.iter().count(), 2); 102 | /// ``` 103 | pub struct Iter<'a, K, V, H: BuildHasher, A: Allocator> { 104 | map: &'a HashMap, 105 | current: usize, 106 | } 107 | 108 | impl<'a, K, V, H, A> Clone for Iter<'a, K, V, H, A> 109 | where 110 | K: Eq + Hash + Clone, 111 | V: Value, 112 | H: BuildHasher + Default, 113 | A: Allocator, 114 | { 115 | fn clone(&self) -> Self { 116 | Iter::new(self.map) 117 | } 118 | } 119 | 120 | impl<'a, K, V, H, A> Iter<'a, K, V, H, A> 121 | where 122 | K: Eq + Hash + Clone, 123 | V: Value, 124 | H: BuildHasher + 'a, 125 | A: Allocator + 'a, 126 | { 127 | pub(crate) fn new(map: &'a HashMap) -> Self { 128 | Self { 129 | map, 130 | current: 0usize, 131 | } 132 | } 133 | } 134 | 135 | impl<'a, K, V, H, A> Iterator for Iter<'a, K, V, H, A> 136 | where 137 | K: Eq + Hash + Clone, 138 | V: Value, 139 | H: BuildHasher + Default + 'a, 140 | A: Allocator + 'a, 141 | { 142 | type Item = (&'a K, &'a V); 143 | 144 | fn next(&mut self) -> Option { 145 | loop { 146 | let current = self.current; 147 | if let Some(cell) = self.map.get_cell_at_index(current) { 148 | self.current = current + 1; 149 | if cell.is_empty() || cell.value.is_null() { 150 | continue; 151 | } 152 | 153 | return Some((&cell.key, &cell.value)); 154 | } else { 155 | return None; 156 | } 157 | } 158 | } 159 | } 160 | 161 | unsafe impl<'a, K, V, H, A> Send for Iter<'a, K, V, H, A> 162 | where 163 | K: Send, 164 | V: Send, 165 | H: BuildHasher + Send + 'a, 166 | A: Allocator + Send + 'a, 167 | { 168 | } 169 | 170 | unsafe impl<'a, K, V, H, A> Sync for Iter<'a, K, V, H, A> 171 | where 172 | K: Sync, 173 | V: Sync, 174 | H: BuildHasher + Sync + 'a, 175 | A: Allocator + Sync + 'a, 176 | { 177 | } 178 | 179 | /// Iterator over a [`HashMap`] which yields mutable key-value reference pairs. 180 | /// 181 | /// # Examples 182 | /// 183 | /// ``` 184 | /// use leapfrog::HashMap; 185 | /// 186 | /// let mut map = HashMap::new(); 187 | /// 188 | /// map.insert(12, 17); 189 | /// map.insert(42, 23); 190 | /// 191 | /// assert_eq!(map.iter_mut().count(), 2); 192 | /// ``` 193 | pub struct IterMut<'a, K, V, H: BuildHasher, A: Allocator> { 194 | map: &'a mut HashMap, 195 | current: usize, 196 | } 197 | 198 | impl<'a, K, V, H, A> IterMut<'a, K, V, H, A> 199 | where 200 | K: Eq + Hash + Clone, 201 | V: Value, 202 | H: BuildHasher + 'a, 203 | A: Allocator + 'a, 204 | { 205 | pub(crate) fn new(map: &'a mut HashMap) -> Self { 206 | Self { 207 | map, 208 | current: 0usize, 209 | } 210 | } 211 | } 212 | 213 | impl<'a, K, V, H, A> Iterator for IterMut<'a, K, V, H, A> 214 | where 215 | K: Eq + Hash + Clone, 216 | V: Value, 217 | H: BuildHasher + Default + 'a, 218 | A: Allocator + 'a, 219 | { 220 | type Item = (&'a K, &'a mut V); 221 | 222 | fn next(&mut self) -> Option { 223 | loop { 224 | let current = self.current; 225 | if let Some(cell) = self.map.get_cell_at_index_mut(current) { 226 | self.current = current + 1; 227 | if cell.is_empty() || cell.value.is_null() { 228 | continue; 229 | } 230 | 231 | return Some((&cell.key, &mut cell.value)); 232 | } else { 233 | return None; 234 | } 235 | } 236 | } 237 | } 238 | 239 | unsafe impl<'a, K, V, H, A> Send for IterMut<'a, K, V, H, A> 240 | where 241 | K: Send, 242 | V: Send, 243 | H: BuildHasher + Send + 'a, 244 | A: Allocator + Send + 'a, 245 | { 246 | } 247 | 248 | unsafe impl<'a, K, V, H, A> Sync for IterMut<'a, K, V, H, A> 249 | where 250 | K: Sync, 251 | V: Sync, 252 | H: BuildHasher + Sync + 'a, 253 | A: Allocator + Sync + 'a, 254 | { 255 | } 256 | -------------------------------------------------------------------------------- /src/hashmap.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the [`HashMap`] struct, which is an implementation of an efficient 2 | //! single-threaded hash map. 3 | 4 | use crate::{ 5 | hashiter::{Iter, IterMut, OwnedIter}, 6 | make_hash, 7 | util::{allocate, deallocate, round_to_pow2, AllocationKind}, 8 | Value, 9 | }; 10 | 11 | use core::{ 12 | borrow::Borrow, 13 | default::Default, 14 | hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}, 15 | }; 16 | 17 | #[cfg(feature = "stable_alloc")] 18 | use allocator_api2::alloc::{Allocator, Global}; 19 | #[cfg(not(feature = "stable_alloc"))] 20 | use core::alloc::Allocator; 21 | #[cfg(not(feature = "stable_alloc"))] 22 | use std::alloc::Global; 23 | 24 | // Re export the entry api. 25 | pub use crate::hashentry::{Entry, OccupiedEntry, VacantEntry}; 26 | 27 | /// The default hasher for a [`HashMap`]. 28 | pub(crate) type DefaultHash = std::collections::hash_map::DefaultHasher; 29 | 30 | /// A [`HashMap`] implementation which uses a modified form of RobinHood/Hopscotch 31 | /// probing. This implementation is efficient, roughly 2x the performance of 32 | /// the default hash map *when using a the same fast hasher*, and a lot more 33 | /// when using the default hasher. It has also been benchmarked to be as fast/ 34 | /// faster than other hashmaps which are known to be very efficient. The 35 | /// implementation is also fully safe. 36 | /// 37 | /// # Limitations 38 | /// 39 | /// This hash map *does not* store keys, so if that is required, then another 40 | /// map would be better. This does, however, save space when the key is large. 41 | /// 42 | /// Currently, moving the table *does not* use a custom allocator, which would 43 | /// further improve the implementation. 44 | /// 45 | /// The number of elements in the map must be a power of two. 46 | /// 47 | /// # Threading 48 | /// 49 | /// This version is *not* thread-safe. [`LeapMap`] is a thread-safe version of 50 | /// the map. 51 | pub struct HashMap, A: Allocator = Global> { 52 | /// Table for the map. 53 | table: *mut Table, 54 | /// The hasher for the map. 55 | hash_builder: H, 56 | /// Allocator for the buckets. 57 | allocator: A, 58 | /// The number of elements in the map. 59 | size: usize, 60 | } 61 | 62 | impl<'a, K, V, H, A: Allocator> HashMap { 63 | /// Gets the capacity of the hash map. 64 | pub fn capacity(&self) -> usize { 65 | self.get_table().size() 66 | } 67 | 68 | /// Gets a reference to the map's table, using the specified `ordering` to 69 | /// load the table reference. 70 | fn get_table(&self) -> &'a Table { 71 | unsafe { &*self.table } 72 | } 73 | 74 | /// Gets a mutable reference to the map's table, using the specified 75 | /// `ordering` to load the table reference. 76 | fn get_table_mut(&self) -> &'a mut Table { 77 | unsafe { &mut *self.table } 78 | } 79 | 80 | /// Returns true if the hash map has been allocated. 81 | fn is_allocated(&self) -> bool { 82 | !self.table.is_null() 83 | } 84 | } 85 | 86 | impl HashMap, Global> 87 | where 88 | K: Eq + Hash + Clone, 89 | V: Value, 90 | { 91 | /// Creates the hash map with space for the default number of elements, which 92 | /// will use the global allocator for allocation of the map data. 93 | pub fn new() -> HashMap, Global> { 94 | Self::new_in(Global) 95 | } 96 | 97 | /// Creates the hash map with space for `capacity` elements, which will use 98 | /// the global allocator for allocation of the map data. 99 | pub fn with_capacity( 100 | capacity: usize, 101 | ) -> HashMap, Global> { 102 | Self::with_capacity_and_hasher_in( 103 | capacity, 104 | BuildHasherDefault::::default(), 105 | Global, 106 | ) 107 | } 108 | } 109 | 110 | impl HashMap 111 | where 112 | K: Eq + Hash + Clone, 113 | V: Value, 114 | H: BuildHasher + Default, 115 | { 116 | /// Creates the hash map with space for `capacity` elements, using the 117 | /// `builder` to create the hasher, which will use the global allocator for 118 | /// allocation of the map data. 119 | pub fn with_capacity_and_hasher(capacity: usize, builder: H) -> HashMap { 120 | Self::with_capacity_and_hasher_in(capacity, builder, Global) 121 | } 122 | } 123 | 124 | impl<'a, K, V, H, A> HashMap 125 | where 126 | K: Eq + Hash + Clone + 'a, 127 | V: Value + 'a, 128 | H: BuildHasher + Default, 129 | A: Allocator, 130 | { 131 | /// The default initial size of the map. 132 | const INITIAL_SIZE: usize = 8; 133 | 134 | /// The max number of elements to search through when having to fallback 135 | /// to using linear search to try to find a cell. 136 | const LINEAR_SEARCH_LIMIT: usize = 128; 137 | 138 | /// The number of cells to use to estimate if the map is full. 139 | const CELLS_IN_USE: usize = Self::LINEAR_SEARCH_LIMIT >> 1; 140 | 141 | /// Creates the hash map with space for the default number of elements, using 142 | /// the provided `allocator` to allocate data for the map. 143 | pub fn new_in(allocator: A) -> HashMap { 144 | Self::with_capacity_and_hasher_in(Self::INITIAL_SIZE, H::default(), allocator) 145 | } 146 | 147 | /// Creates the hash map with space for the `capacity` number of elements 148 | /// using the provided `builder` to build the hasher, and `allocator` for 149 | /// allocating the map data. 150 | pub fn with_capacity_and_hasher_in( 151 | capacity: usize, 152 | builder: H, 153 | allocator: A, 154 | ) -> HashMap { 155 | let capacity = round_to_pow2(capacity.max(Self::INITIAL_SIZE)); 156 | let table = Self::allocate_and_init_table(&allocator, capacity); 157 | HashMap { 158 | table, 159 | hash_builder: builder, 160 | allocator, 161 | size: 0, 162 | } 163 | } 164 | 165 | /// Returns the hashed value for the `key` as usize. 166 | pub fn hash_usize(&self, key: &Q) -> usize 167 | where 168 | K: Borrow, 169 | Q: Hash + Eq, 170 | { 171 | make_hash::(&self.hash_builder, key) as usize 172 | } 173 | 174 | /// Inserts a key-value pair into the map. 175 | /// 176 | /// If the map did not have this key present, `None` is returned. 177 | /// 178 | /// If the map did have this key present, the value is updated and the old 179 | /// value is returned. 180 | /// 181 | /// # Examples 182 | /// 183 | /// ``` 184 | /// let mut map = leapfrog::HashMap::new(); 185 | /// assert_eq!(map.insert(37, 12), None); 186 | /// assert_eq!(map.insert(37, 14), Some(12)); 187 | /// ``` 188 | pub fn insert(&mut self, key: K, value: V) -> Option { 189 | let mut state = self.hash_builder.build_hasher(); 190 | key.hash(&mut state); 191 | let hash = state.finish(); 192 | debug_assert!(hash != null_hash()); 193 | loop { 194 | let size_mask = self.get_table().size_mask; 195 | let buckets = self.get_table_mut().bucket_slice_mut(); 196 | match Self::insert_or_find(hash, &key, value, buckets, size_mask) { 197 | InsertResult::Overflow(overflow_index) => { 198 | // Resize and move into a new map, then try again. 199 | // If this happens on the first iteration, then deleted cells 200 | // will be removed. 201 | // If this happens on the second iteration, this will double 202 | // the size of the map. 203 | // If this happens on the third iteration, the insert will 204 | // succeed. 205 | self.move_to_new_buckets(overflow_index); 206 | } 207 | InsertResult::Found(old_value) => { 208 | return Some(old_value); 209 | } 210 | InsertResult::NewInsert => { 211 | self.size += 1; 212 | return None; 213 | } 214 | } 215 | } 216 | } 217 | 218 | /// Returns an optional reference type to the value corresponding to the key. 219 | /// 220 | /// # Examples 221 | /// 222 | /// ``` 223 | /// let mut map = leapfrog::HashMap::new(); 224 | /// map.insert(0, 12); 225 | /// if let Some(value) = map.get(&0) { 226 | /// assert_eq!(*value, 12); 227 | /// } 228 | /// assert!(map.get(&2).is_none()); 229 | ///``` 230 | pub fn get(&'a self, key: &Q) -> Option<&'a V> 231 | where 232 | K: Borrow, 233 | Q: Hash + Eq, 234 | { 235 | self.find(make_hash::(&self.hash_builder, key), key) 236 | .and_then(|(_k, v)| if v.is_null() { None } else { Some(v) }) 237 | } 238 | 239 | /// Returns a mutable reference type to the value corresponding to the `key`. 240 | /// 241 | /// # Examples 242 | /// 243 | /// ``` 244 | /// let mut map = leapfrog::HashMap::new(); 245 | /// map.insert(1, 12); 246 | /// if let Some(value) = map.get_mut(&1) { 247 | /// *value = 14; 248 | /// } 249 | /// assert_eq!(*map.get(&1).unwrap(), 14); 250 | /// assert!(map.get(&2).is_none()); 251 | ///``` 252 | pub fn get_mut(&mut self, key: &Q) -> Option<&'a mut V> 253 | where 254 | K: Borrow, 255 | Q: Hash + Eq, 256 | { 257 | let table = self.get_table_mut(); 258 | let size_mask = table.size_mask; 259 | let buckets = table.bucket_slice_mut(); 260 | let hash = make_hash::(&self.hash_builder, key); 261 | Self::find_mut(buckets, hash, key, size_mask).and_then(|old| { 262 | if old.is_null() { 263 | None 264 | } else { 265 | Some(old) 266 | } 267 | }) 268 | } 269 | 270 | /// Returns the key-value pair corresponding to the supplied key. 271 | /// 272 | /// The supplied key may be any borrower form of the map's key type, but 273 | /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for the key 274 | /// type. 275 | /// 276 | /// # Examples 277 | /// 278 | /// ``` 279 | /// let mut map = leapfrog::HashMap::new(); 280 | /// map.insert(1, 12); 281 | /// assert_eq!(map.get_key_value(&1), Some((&1, &12))); 282 | /// assert!(map.get(&2).is_none()); 283 | /// ``` 284 | pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> 285 | where 286 | K: Borrow, 287 | Q: Hash + Eq, 288 | { 289 | self.find(make_hash::(&self.hash_builder, key), key) 290 | } 291 | 292 | /// Gets the given key's corresponding entry in the map for in-place manipulation. 293 | /// 294 | /// # Examples 295 | /// 296 | /// ``` 297 | /// let mut map = leapfrog::HashMap::::new(); 298 | /// 299 | /// map.insert(1, 10); 300 | /// map.insert(2, 20); 301 | /// 302 | /// for i in 1..5 { 303 | /// let value = map.entry(i).or_insert(i * 10); 304 | /// *value += 1; 305 | /// } 306 | /// 307 | /// assert_eq!(map.get(&1), Some(&11)); 308 | /// assert_eq!(map.get(&2), Some(&21)); 309 | /// assert_eq!(map.get(&3), Some(&31)); 310 | /// assert_eq!(map.get(&4), Some(&41)); 311 | /// ``` 312 | pub fn entry(&mut self, key: K) -> Entry<'_, K, V, H, A> { 313 | if let Some(v_ref) = self.get_mut(&key) { 314 | Entry::Occupied(OccupiedEntry { 315 | map: self, 316 | key, 317 | value: v_ref, 318 | }) 319 | } else { 320 | Entry::Vacant(VacantEntry { map: self, key }) 321 | } 322 | } 323 | 324 | /// Returns true if the map contains the specified `key`. 325 | /// 326 | /// # Examples 327 | /// 328 | /// ``` 329 | /// let mut map = leapfrog::HashMap::new(); 330 | /// map.insert(1, 47u64); 331 | /// assert_eq!(map.contains_key(&1), true); 332 | /// assert_eq!(map.contains_key(&2), false); 333 | /// ``` 334 | pub fn contains_key(&self, key: &Q) -> bool 335 | where 336 | K: Borrow, 337 | Q: Hash + Eq, 338 | { 339 | self.get(key).is_some() 340 | } 341 | 342 | /// Removes the key from the map, returning the value at the key if the key 343 | /// was present in the map. 344 | /// 345 | /// # Examples 346 | /// 347 | /// ``` 348 | /// let mut map = leapfrog::HashMap::new(); 349 | /// map.insert(2, 17); 350 | /// assert_eq!(map.remove(&2), Some(17)); 351 | /// assert_eq!(map.remove(&2), None); 352 | /// ``` 353 | pub fn remove(&mut self, key: &Q) -> Option 354 | where 355 | K: Borrow, 356 | Q: Hash + Eq, 357 | { 358 | // Here, we could shuffle the existing cells so that the gaps left 359 | // by this cell are filled in. However, we rather just leave them as 360 | // deleted, and the next time the map is resized they will be removed. 361 | if let Some(v) = self.get_mut(key) { 362 | let old_value = *v; 363 | if old_value.is_null() { 364 | return None; 365 | } 366 | *v = V::null(); 367 | self.size -= 1; 368 | Some(old_value) 369 | } else { 370 | None 371 | } 372 | } 373 | 374 | /// Creates an iterator over a [`HashMap`] which yields immutable key-value 375 | /// reference pairs. 376 | /// 377 | /// # Examples 378 | /// 379 | /// ``` 380 | /// use leapfrog::HashMap; 381 | /// 382 | /// let mut map = HashMap::new(); 383 | /// map.insert(12, 27); 384 | /// assert_eq!(map.iter().count(), 1); 385 | /// ``` 386 | pub fn iter(&'a self) -> Iter<'a, K, V, H, A> { 387 | Iter::new(self) 388 | } 389 | 390 | /// Creates an iterator over a [`HashMap`] which yields mutable key-value 391 | /// reference pairs. 392 | /// 393 | /// # Examples 394 | /// 395 | /// ``` 396 | /// use leapfrog::HashMap; 397 | /// 398 | /// let mut map = HashMap::new(); 399 | /// map.insert(12, 27); 400 | /// map.iter_mut().for_each(|(_k, v)| *v += 1); 401 | /// assert_eq!(map.get(&12), Some(&28)); 402 | /// assert_eq!(map.iter_mut().count(), 1); 403 | /// ``` 404 | pub fn iter_mut(&'a mut self) -> IterMut<'a, K, V, H, A> { 405 | IterMut::new(self) 406 | } 407 | 408 | /// Returns the length of the map. 409 | /// 410 | /// # Examples 411 | /// 412 | /// ``` 413 | /// let mut map = leapfrog::HashMap::new(); 414 | /// map.insert(2, 17); 415 | /// assert_eq!(map.len(), 1); 416 | /// map.insert(4, 17); 417 | /// assert_eq!(map.len(), 2); 418 | /// map.remove(&2); 419 | /// assert_eq!(map.len(), 1); 420 | /// map.remove(&4); 421 | /// assert_eq!(map.len(), 0); 422 | /// map.remove(&4); 423 | /// assert_eq!(map.len(), 0); 424 | /// ``` 425 | pub fn len(&self) -> usize { 426 | self.size 427 | } 428 | 429 | /// Returns `true` if the map contains no elements. 430 | pub fn is_empty(&self) -> bool { 431 | self.len() == 0 432 | } 433 | 434 | /// Inserts a new cell into the given `buckets`, where the max number of 435 | /// buckets is `size_mask` + 1. 436 | /// 437 | /// # Returns 438 | /// 439 | /// This returns an [InsertResult] which specifies what happened during 440 | /// the insertion procedure. 441 | /// 442 | /// If the hash was found in the map, then [InsertResult::Found] will be 443 | /// returned with the value set to the old value for the found cell. 444 | /// 445 | /// If the hash was not found, then [InsertResult::NewInsert] will be returned 446 | /// to specify that the hash was newly inserted. 447 | /// 448 | /// If the hash was not found in the map, and it was not possible to insert 449 | /// the hash into the map, then [InsertResult::Overflow] will be returned 450 | /// with the index of the overflow. 451 | fn insert_or_find( 452 | hash: HashedKey, 453 | key: &K, 454 | value: V, 455 | buckets: &mut [Bucket], 456 | size_mask: usize, 457 | ) -> InsertResult { 458 | let mut index = hash as usize; 459 | { 460 | let cell = Self::get_cell_mut(buckets, index, size_mask); 461 | if cell.hash == hash && cell.key == *key { 462 | let old_value = cell.value; 463 | cell.value = value; 464 | return InsertResult::Found(old_value); 465 | } else if cell.hash == null_hash() { 466 | cell.key = key.clone(); 467 | cell.hash = hash; 468 | cell.value = value; 469 | return InsertResult::NewInsert; 470 | } 471 | } 472 | 473 | // Here, we get the offset delta which will allow us to find the desired 474 | // bucket. After that, we need to use the second set of delta values to 475 | // walk along the chain. At each point, we check if the hash is a match; 476 | // if we find a match, then we have found the cell. 477 | let mut delta = Self::get_first_delta(buckets, index, size_mask); 478 | let first_delta = delta == 0; 479 | while delta != 0 { 480 | index += delta as usize; 481 | delta = Self::get_second_delta(buckets, index, size_mask); 482 | 483 | let cell = Self::get_cell_mut(buckets, index, size_mask); 484 | if hash == cell.hash && cell.key == *key { 485 | let old_value = cell.value; 486 | cell.value = value; 487 | return InsertResult::Found(old_value); 488 | } 489 | } 490 | 491 | // If we are here, then we have reached the end of the chain for this 492 | // bucket. The key does not exist, so we switch here to linear probing 493 | // to find a free cell to insert into. 494 | let max_index = index + size_mask; 495 | debug_assert!(max_index as i64 - index as i64 >= 0); 496 | 497 | let prev_link_index = index; 498 | let mut probes_remaining = Self::LINEAR_SEARCH_LIMIT.min(max_index - index); 499 | while probes_remaining > 0 { 500 | index += 1; 501 | 502 | // We found an empty cell, so reserve it and link it 503 | // to the previous cell in the same bucket. 504 | let cell = Self::get_cell_mut(buckets, index, size_mask); 505 | if cell.hash == null_hash() { 506 | cell.hash = hash; 507 | cell.key = key.clone(); 508 | cell.value = value; 509 | 510 | let offset = (index - prev_link_index) as u8; 511 | if first_delta { 512 | *Self::get_first_delta_mut(buckets, prev_link_index, size_mask) = offset; 513 | } else { 514 | *Self::get_second_delta_mut(buckets, prev_link_index, size_mask) = offset; 515 | } 516 | return InsertResult::NewInsert; 517 | } 518 | 519 | // This is single threaded map, so it's impossible for the hash 520 | // which is a match to appear outside of the probe chain. 521 | debug_assert!(cell.key != *key); 522 | probes_remaining -= 1; 523 | } 524 | 525 | // Table is too full, we need to grow it. 526 | InsertResult::Overflow(index + 1) 527 | } 528 | 529 | /// Tries to find the value for the `hash`, without inserting into the map. 530 | /// This will reuturn a Some(&v) if the find succeeded, otherwise this will 531 | /// return None. 532 | fn find(&self, hash: HashedKey, key: &Q) -> Option<(&K, &V)> 533 | where 534 | K: Borrow, 535 | { 536 | debug_assert!(hash != null_hash()); 537 | 538 | let buckets = self.get_table().bucket_slice(); 539 | let size_mask = self.get_table().size_mask; 540 | 541 | let mut index = hash as usize; 542 | let cell = Self::get_cell(buckets, index, size_mask); 543 | if cell.hash == hash && key.eq(cell.key.borrow()) { 544 | return Some((&cell.key, &cell.value)); 545 | } 546 | 547 | // Now we need to follow the probe chain for the bucket. 548 | let mut delta = Self::get_first_delta(buckets, index, size_mask); 549 | while delta != 0 { 550 | index += delta as usize; 551 | let cell = Self::get_cell(buckets, index, size_mask); 552 | if cell.hash == hash && key.eq(cell.key.borrow()) { 553 | return Some((&cell.key, &cell.value)); 554 | } 555 | 556 | delta = Self::get_second_delta(buckets, index, size_mask); 557 | } 558 | 559 | None 560 | } 561 | 562 | /// Tries to find the value for the `hash` without inserting into the 563 | /// `buckets`. This will reuturn a reference to the old value if the find 564 | /// was successful. 565 | fn find_mut( 566 | buckets: &'a mut [Bucket], 567 | hash: HashedKey, 568 | key: &Q, 569 | size_mask: usize, 570 | ) -> Option<&'a mut V> 571 | where 572 | K: Borrow, 573 | { 574 | debug_assert!(hash != null_hash()); 575 | 576 | let mut index = hash as usize; 577 | let mut delta = 0u8; 578 | let mut found = false; 579 | 580 | let cell = Self::get_cell(buckets, index, size_mask); 581 | if cell.hash == hash && key.eq(cell.key.borrow()) { 582 | found = true; 583 | } else { 584 | delta = Self::get_first_delta(buckets, index, size_mask); 585 | } 586 | 587 | // If we have found the correct hash, then we will just skip this, 588 | // otherwise we need to search the probe chain and see if we find 589 | // something 590 | while !found && delta != 0 { 591 | index += delta as usize; 592 | let cell = Self::get_cell(buckets, index, size_mask); 593 | if cell.hash == hash && key.eq(cell.key.borrow()) { 594 | found = true; 595 | break; 596 | } 597 | delta = Self::get_second_delta(buckets, index, size_mask); 598 | } 599 | 600 | if found { 601 | Some(&mut Self::get_cell_mut(buckets, index, size_mask).value) 602 | } else { 603 | None 604 | } 605 | } 606 | 607 | /// Moves the current map buckets into a larger container of buckets, where 608 | /// `overflow_index` is the index at which an overflow of the delta value 609 | /// would have happened. 610 | fn move_to_new_buckets(&mut self, overflow_index: usize) { 611 | // Estimate the number of cells 612 | let mut index = overflow_index - Self::CELLS_IN_USE; 613 | let mut cells_in_use = 0; 614 | 615 | let buckets = self.get_table().bucket_slice(); 616 | let size_mask = self.get_table().size_mask; 617 | for _ in 0..Self::CELLS_IN_USE { 618 | let cell = Self::get_cell(buckets, index, size_mask); 619 | if !cell.value.is_null() { 620 | cells_in_use += 1; 621 | } 622 | index += 1; 623 | } 624 | 625 | // Estimate how much we need to resize by: 626 | let ratio = cells_in_use as f32 / Self::CELLS_IN_USE as f32; 627 | let in_use_estimated = (size_mask + 1) as f32 * ratio; 628 | let estimated = round_to_pow2((in_use_estimated * 2.0) as usize); 629 | let mut new_table_size = estimated.max(Self::INITIAL_SIZE); 630 | 631 | loop { 632 | if self.try_move_to_new_buckets(new_table_size) { 633 | return; // Succeeded, we can return 634 | } 635 | 636 | // Increase the size and try again. It's currently expensive to 637 | // try to move the buckets to new buckets, however, this very 638 | // rarely happens. 639 | new_table_size *= 2; 640 | } 641 | } 642 | 643 | /// Tries to move the current map buckets into new buckets to support `size` 644 | /// number of elements in the map. 645 | /// 646 | /// # Limitations 647 | /// 648 | /// Currently this doesn't use a custom allocator, which needs to be changed 649 | /// to improve performance. 650 | fn try_move_to_new_buckets(&mut self, size: usize) -> bool { 651 | let source_buckets = self.get_table().bucket_slice(); 652 | let source_size_mask = self.get_table().size_mask; 653 | let source_size = source_size_mask + 1; 654 | 655 | // This is very bad for performance, we need to allocate this from 656 | // somewhere else ... 657 | let dst_table_ptr = Self::allocate_and_init_table(&self.allocator, size); 658 | let dst_table = unsafe { &mut *dst_table_ptr }; 659 | let dst_size_mask = dst_table.size_mask; 660 | let dst_buckets = dst_table.bucket_slice_mut(); 661 | 662 | for source_index in 0..source_size { 663 | let cell = Self::get_cell(source_buckets, source_index, source_size_mask); 664 | if !cell.value.is_null() { 665 | match Self::insert_or_find( 666 | cell.hash, 667 | &cell.key, 668 | cell.value, 669 | dst_buckets, 670 | dst_size_mask, 671 | ) { 672 | InsertResult::Overflow(_) => { 673 | // New bucekts are too small, failed to move. 674 | Self::deallocate_table(&self.allocator, dst_table_ptr); 675 | return false; 676 | } 677 | InsertResult::Found(_) => { 678 | // Shouldn't find a value in the new table ... 679 | debug_assert!(false); 680 | } 681 | InsertResult::NewInsert => { 682 | // Success, continue ... 683 | } 684 | } 685 | } 686 | } 687 | 688 | // Succeeded in moving all buckets, so update the map table: 689 | Self::deallocate_table(&self.allocator, self.table); 690 | self.table = dst_table_ptr; 691 | 692 | true 693 | } 694 | 695 | /// Allocates and initializes buckets which will hold `cells` number of 696 | /// cells, using the provided `allocator`. 697 | fn allocate_and_init_table(allocator: &A, cells: usize) -> *mut Table { 698 | assert!(cells >= 4 && (cells % 2 == 0)); 699 | let bucket_count = cells >> 2; 700 | let bucket_ptr = 701 | allocate::, A>(allocator, bucket_count, AllocationKind::Uninitialized); 702 | let buckets = unsafe { std::slice::from_raw_parts_mut(bucket_ptr, bucket_count) }; 703 | 704 | // Since AtomicU8 and AtomicU64 are the same as u8 and u64 in memory, 705 | // we can write them as zero, rather than calling the atomic versions 706 | for i in 0..bucket_count { 707 | unsafe { 708 | let bucket_deltas = &mut buckets[i].deltas as *mut u8; 709 | std::ptr::write_bytes(bucket_deltas, 0, 8); 710 | }; 711 | 712 | for cell in 0..4 { 713 | // FIXME: How to initialize keys? 714 | unsafe { 715 | let cell_hash: *mut HashedKey = &mut buckets[i].cells[cell].hash; 716 | std::ptr::write_bytes(cell_hash, 0, 1); 717 | }; 718 | 719 | // FIXME: We should check if the stored type is directly writable .. 720 | let cell_value = &mut buckets[i].cells[cell].value; 721 | *cell_value = V::null(); 722 | } 723 | } 724 | 725 | let table_ptr = allocate::, A>(allocator, 1, AllocationKind::Uninitialized); 726 | let table = unsafe { &mut *table_ptr }; 727 | 728 | table.buckets = bucket_ptr; 729 | table.size_mask = cells - 1; 730 | 731 | table_ptr 732 | } 733 | 734 | /// Deallocates the table pointed to by `table_ptr` using the `allocator`. 735 | fn deallocate_table(allocator: &A, table_ptr: *mut Table) { 736 | let table = unsafe { &mut *table_ptr }; 737 | let bucket_ptr = table.buckets; 738 | let bucket_count = table.size() >> 2; 739 | deallocate::, A>(allocator, bucket_ptr, bucket_count); 740 | deallocate::, A>(allocator, table_ptr, 1); 741 | } 742 | 743 | pub(crate) fn get_cell_at_index(&self, index: usize) -> Option<&Cell> { 744 | let table = self.get_table(); 745 | if index >= table.size() { 746 | return None; 747 | } 748 | 749 | let buckets = table.bucket_slice(); 750 | Some(Self::get_cell(buckets, index, table.size_mask)) 751 | } 752 | 753 | pub(crate) fn get_cell_at_index_mut(&self, index: usize) -> Option<&'a mut Cell> { 754 | let table = self.get_table_mut(); 755 | if index >= table.size() { 756 | return None; 757 | } 758 | 759 | let size_mask = table.size_mask; 760 | let buckets = table.bucket_slice_mut(); 761 | Some(Self::get_cell_mut(buckets, index, size_mask)) 762 | } 763 | 764 | /// Gets a reference to a cell for the `index` from the `buckets`. 765 | #[inline] 766 | fn get_cell(buckets: &[Bucket], index: usize, size_mask: usize) -> &Cell { 767 | let bucket_index = Self::get_bucket_index(index, size_mask); 768 | let cell_index = Self::get_cell_index(index); 769 | &buckets[bucket_index].cells[cell_index] 770 | } 771 | 772 | /// Gets a mutable reference mutable to a cell for the `index` from the 773 | /// `buckets`. 774 | #[inline] 775 | fn get_cell_mut( 776 | buckets: &mut [Bucket], 777 | index: usize, 778 | size_mask: usize, 779 | ) -> &mut Cell { 780 | let bucket_index = Self::get_bucket_index(index, size_mask); 781 | let cell_index = Self::get_cell_index(index); 782 | &mut buckets[bucket_index].cells[cell_index] 783 | } 784 | 785 | /// Gets the first delta value for a cell with the given `index`. 786 | #[inline] 787 | fn get_first_delta(buckets: &[Bucket], index: usize, size_mask: usize) -> u8 { 788 | let bucket_index = Self::get_bucket_index(index, size_mask); 789 | let cell_index = Self::get_cell_index(index); 790 | buckets[bucket_index].deltas[cell_index] 791 | } 792 | 793 | /// Gets a mutable reference to the first delta value for a cell with the 794 | /// given `index`. 795 | #[inline] 796 | fn get_first_delta_mut( 797 | buckets: &mut [Bucket], 798 | index: usize, 799 | size_mask: usize, 800 | ) -> &mut u8 { 801 | let bucket_index = Self::get_bucket_index(index, size_mask); 802 | let cell_index = Self::get_cell_index(index); 803 | &mut buckets[bucket_index].deltas[cell_index] 804 | } 805 | 806 | /// Gets the second delta value for a cell with the given `index`. 807 | #[inline] 808 | fn get_second_delta(buckets: &[Bucket], index: usize, size_mask: usize) -> u8 { 809 | let bucket_index = Self::get_bucket_index(index, size_mask); 810 | let cell_index = Self::get_cell_index(index); 811 | buckets[bucket_index].deltas[cell_index + 4] 812 | } 813 | 814 | /// Gets a mutable reference to the second delta value for a cell with the 815 | /// given `index`. 816 | #[inline] 817 | fn get_second_delta_mut( 818 | buckets: &mut [Bucket], 819 | index: usize, 820 | size_mask: usize, 821 | ) -> &mut u8 { 822 | let bucket_index = Self::get_bucket_index(index, size_mask); 823 | let cell_index = Self::get_cell_index(index); 824 | &mut buckets[bucket_index].deltas[cell_index + 4] 825 | } 826 | 827 | /// Gets the index of the bucket for the spcified `index` and `size_mask`. 828 | #[inline] 829 | const fn get_bucket_index(index: usize, size_mask: usize) -> usize { 830 | (index & size_mask) >> 2 831 | } 832 | 833 | /// Gets the `index` of the cell within a bucket for the `index`. 834 | #[inline] 835 | const fn get_cell_index(index: usize) -> usize { 836 | index & 3 837 | } 838 | } 839 | 840 | // This is safe to be send if the hasher and allocator are send, since the pointer 841 | // to the table is not thread-local and can be sent between threads. 842 | unsafe impl Send for HashMap 843 | where 844 | H: BuildHasher + Send, 845 | A: Allocator + Send, 846 | { 847 | } 848 | 849 | impl Default for HashMap, Global> 850 | where 851 | K: Eq + Hash + Clone, 852 | V: Value, 853 | { 854 | fn default() -> Self { 855 | Self::new_in(Global) 856 | } 857 | } 858 | 859 | impl<'a, K, V, H, A> Clone for HashMap 860 | where 861 | K: Eq + Hash + Clone + 'a, 862 | V: Value + 'a, 863 | H: BuildHasher + Default, 864 | A: Allocator + Clone, 865 | { 866 | fn clone(&self) -> Self { 867 | let capacity = self.capacity(); 868 | let builder = H::default(); 869 | let mut new_map = 870 | Self::with_capacity_and_hasher_in(capacity, builder, self.allocator.clone()); 871 | 872 | for (key, value) in self.into_iter() { 873 | let _old = new_map.insert(key.clone(), *value); 874 | } 875 | 876 | new_map 877 | } 878 | } 879 | 880 | impl Drop for HashMap { 881 | fn drop(&mut self) { 882 | if !self.is_allocated() { 883 | return; 884 | } 885 | 886 | let table = self.get_table_mut(); 887 | 888 | let bucket_ptr = table.buckets; 889 | let bucket_count = table.size() >> 2; 890 | deallocate::, A>(&self.allocator, bucket_ptr, bucket_count); 891 | 892 | let table_ptr = self.table; 893 | deallocate::, A>(&self.allocator, table_ptr, 1); 894 | } 895 | } 896 | 897 | impl IntoIterator for HashMap 898 | where 899 | K: Eq + Hash + Clone, 900 | V: Value, 901 | H: BuildHasher + Default, 902 | A: Allocator, 903 | { 904 | type Item = (K, V); 905 | type IntoIter = OwnedIter; 906 | 907 | fn into_iter(self) -> Self::IntoIter { 908 | OwnedIter::new(self) 909 | } 910 | } 911 | 912 | impl<'a, K, V, H, A> IntoIterator for &'a HashMap 913 | where 914 | K: Eq + Hash + Clone, 915 | V: Value, 916 | H: BuildHasher + Default + 'a, 917 | A: Allocator + 'a, 918 | { 919 | type Item = (&'a K, &'a V); 920 | type IntoIter = Iter<'a, K, V, H, A>; 921 | 922 | fn into_iter(self) -> Self::IntoIter { 923 | self.iter() 924 | } 925 | } 926 | 927 | /// Returns the null hash value. 928 | #[inline] 929 | pub(crate) const fn null_hash() -> HashedKey { 930 | 0_u64 931 | } 932 | 933 | /// The type used for hashed keys. 934 | pub(crate) type HashedKey = u64; 935 | 936 | /// Struct which stores a cell in a hash map. A cell is simply a hash (rather 937 | /// than the key iteself) and the value associated with the key for which the 938 | /// hash is associated. 939 | pub(crate) struct Cell { 940 | /// The hashed value of the cell. This adds 8 bytes of overhead to per key-value 941 | /// pair for the cell, which is a lot, but it improves performance, so we 942 | /// make the tradeoff. 943 | /// FIXME: Reduce this overhead ... 944 | hash: HashedKey, 945 | // The key for the cell. 946 | pub(crate) key: K, 947 | /// The value assosciated with the hash. 948 | pub(crate) value: V, 949 | } 950 | 951 | impl Cell { 952 | /// Returns true if the cell is empty. 953 | pub(super) fn is_empty(&self) -> bool { 954 | self.hash == null_hash() 955 | } 956 | } 957 | 958 | /// Underlying table for a [HashMap]. This stores a pointer to the buckets and 959 | /// the mask for the number of cells which are stored in the table. 960 | struct Table { 961 | /// Pointer to the buckets for the table. 962 | buckets: *mut Bucket, 963 | /// The mask for indexing into the buckets. 964 | size_mask: usize, 965 | } 966 | 967 | impl Table { 968 | /// Gets a mutable slice of the table buckets. 969 | fn bucket_slice_mut(&mut self) -> &mut [Bucket] { 970 | unsafe { std::slice::from_raw_parts_mut(self.buckets, self.size()) } 971 | } 972 | 973 | /// Gets a slice of the table buckets. 974 | fn bucket_slice(&self) -> &[Bucket] { 975 | unsafe { std::slice::from_raw_parts(self.buckets, self.size()) } 976 | } 977 | 978 | /// Returns the number of cells in the table. 979 | fn size(&self) -> usize { 980 | self.size_mask + 1 981 | } 982 | } 983 | 984 | /// Struct which stores buckets of cells. Each bucket stores four cells 985 | /// and two delta values per cell. The first delta value for a cell provides 986 | /// the offset to the cell which is the start of the probe chain for the cell. 987 | /// The second delta value provides the offset to the next link in the probe 988 | /// chain once a search along the probe chain has started. 989 | struct Bucket { 990 | /// Delta values for the cells. The first 4 values are the delta values to 991 | /// the start of the probe chain, and the second 4 values are the delta 992 | /// values to the next probe in the chain, for each cell, respectively. 993 | deltas: [u8; 8], 994 | /// Cells for the bucket. 995 | cells: [Cell; 4], 996 | /// Placeholder for the key 997 | _key: std::marker::PhantomData, 998 | } 999 | 1000 | /// Defines the result of an insert into the [HashMap]. 1001 | enum InsertResult { 1002 | /// The insertion found a cell for the key, replaced the old value with 1003 | /// a new one, and returned the old value. 1004 | Found(V), 1005 | /// The insertion was performed with a new key, so a new cell was filled. 1006 | NewInsert, 1007 | /// No new cell was found for the key withing the linear search range, 1008 | /// so we overflowed the max delta value and need to move the map to 1009 | /// a larger table. 1010 | Overflow(usize), 1011 | } 1012 | -------------------------------------------------------------------------------- /src/hashmap_serde.rs: -------------------------------------------------------------------------------- 1 | use crate::{HashMap, Value}; 2 | 3 | use core::{ 4 | fmt, 5 | hash::{BuildHasher, Hash}, 6 | marker::PhantomData, 7 | }; 8 | 9 | use serde_crate::{ 10 | de::{Deserialize, MapAccess, Visitor}, 11 | ser::{Serialize, SerializeMap, Serializer}, 12 | Deserializer, 13 | }; 14 | 15 | #[cfg(feature = "stable_alloc")] 16 | use allocator_api2::alloc::{Allocator, Global}; 17 | #[cfg(not(feature = "stable_alloc"))] 18 | use core::alloc::Allocator; 19 | #[cfg(not(feature = "stable_alloc"))] 20 | use std::alloc::Global; 21 | 22 | pub struct HashMapVisitor { 23 | marker: PhantomData HashMap>, 24 | } 25 | 26 | impl HashMapVisitor 27 | where 28 | K: Eq + Hash, 29 | V: Value, 30 | H: BuildHasher + Clone, 31 | { 32 | fn new() -> Self { 33 | HashMapVisitor { 34 | marker: PhantomData, 35 | } 36 | } 37 | } 38 | 39 | impl<'de, K, V, H> Visitor<'de> for HashMapVisitor 40 | where 41 | K: Deserialize<'de> + Eq + Hash + Clone + std::fmt::Debug, 42 | V: Deserialize<'de> + Value, 43 | H: BuildHasher + Clone + Default, 44 | { 45 | type Value = HashMap; 46 | 47 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 48 | formatter.write_str("a HashMap") 49 | } 50 | 51 | fn visit_map(self, mut access: M) -> Result 52 | where 53 | M: MapAccess<'de>, 54 | { 55 | let size = access.size_hint().unwrap_or(4); 56 | let mut map = HashMap::with_capacity_and_hasher(size, Default::default()); 57 | 58 | while let Some((key, value)) = access.next_entry()? { 59 | map.insert(key, value); 60 | } 61 | 62 | Ok(map) 63 | } 64 | } 65 | 66 | impl<'de, K, V, H> Deserialize<'de> for HashMap 67 | where 68 | K: Deserialize<'de> + Eq + Hash + Clone + std::fmt::Debug, 69 | V: Deserialize<'de> + Value, 70 | H: BuildHasher + Clone + Default, 71 | { 72 | fn deserialize(deserializer: D) -> Result 73 | where 74 | D: Deserializer<'de>, 75 | { 76 | deserializer.deserialize_map(HashMapVisitor::::new()) 77 | } 78 | } 79 | 80 | impl Serialize for HashMap 81 | where 82 | K: Serialize + Eq + Hash + Clone, 83 | V: Serialize + Value, 84 | H: BuildHasher + Clone + Default, 85 | A: Allocator, 86 | { 87 | fn serialize(&self, serializer: S) -> Result 88 | where 89 | S: Serializer, 90 | { 91 | let mut map = serializer.serialize_map(Some(self.len()))?; 92 | 93 | for (k, v) in self.iter() { 94 | map.serialize_entry(k, v)?; 95 | } 96 | 97 | map.end() 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod test { 103 | use crate::HashMap; 104 | 105 | #[test] 106 | fn hashmap_serde() { 107 | let mut map = HashMap::::new(); 108 | 109 | map.insert(0, 11); 110 | map.insert(1, 12); 111 | map.insert(2, 13); 112 | map.insert(3, 14); 113 | map.insert(4, 15); 114 | map.insert(5, 16); 115 | 116 | let serialized = serde_json::to_string(&map).expect("Couldn't serialize map"); 117 | let deserialized: HashMap = 118 | serde_json::from_str(&serialized).expect("Couldn't deserialize map"); 119 | 120 | assert_eq!(deserialized.get(&0), Some(&11)); 121 | assert_eq!(deserialized.get(&1), Some(&12)); 122 | assert_eq!(deserialized.get(&2), Some(&13)); 123 | assert_eq!(deserialized.get(&3), Some(&14)); 124 | assert_eq!(deserialized.get(&4), Some(&15)); 125 | assert_eq!(deserialized.get(&5), Some(&16)); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/leapiter.rs: -------------------------------------------------------------------------------- 1 | use crate::{leapmap::null_hash, LeapMap, Ref, RefMut, Value}; 2 | 3 | use core::hash::{BuildHasher, Hash}; 4 | 5 | #[cfg(feature = "stable_alloc")] 6 | use allocator_api2::alloc::Allocator; 7 | #[cfg(not(feature = "stable_alloc"))] 8 | use core::alloc::Allocator; 9 | 10 | /// Iterator over a [`LeapMap`] which yields key-value pairs. 11 | /// 12 | /// # Examples 13 | /// 14 | /// ``` 15 | /// use leapfrog::HashMap; 16 | /// 17 | /// let mut map = HashMap::new(); 18 | /// 19 | /// map.insert(12u32, 17u64); 20 | /// map.insert(42u32, 92u64); 21 | /// 22 | /// let pairs = map.into_iter().collect::>(); 23 | /// assert_eq!(pairs.len(), 2); 24 | /// ``` 25 | pub struct OwnedIter { 26 | map: LeapMap, 27 | current: usize, 28 | } 29 | 30 | impl OwnedIter 31 | where 32 | K: Eq + Hash + Clone, 33 | V: Value, 34 | H: BuildHasher, 35 | A: Allocator, 36 | { 37 | pub(crate) fn new(map: LeapMap) -> Self { 38 | Self { 39 | map, 40 | current: 0usize, 41 | } 42 | } 43 | } 44 | 45 | impl Iterator for OwnedIter 46 | where 47 | K: Eq + Hash + Copy, 48 | V: Value, 49 | H: BuildHasher + Default, 50 | A: Allocator, 51 | { 52 | type Item = (K, V); 53 | 54 | fn next(&mut self) -> Option { 55 | loop { 56 | let current = self.current; 57 | if let Some(mut cell_ref) = self.map.get_cell_at_index(current) { 58 | self.current = current + 1; 59 | if cell_ref.hash == null_hash() { 60 | continue; 61 | } 62 | 63 | // We don't just return cell_ref.key_value() here since if this 64 | // is None then we want to continue until we find a cell which 65 | // is in the map. 66 | if let Some((k, v)) = cell_ref.key_value() { 67 | return Some((k, v)); 68 | } 69 | } else { 70 | return None; 71 | } 72 | } 73 | } 74 | } 75 | 76 | unsafe impl Send for OwnedIter 77 | where 78 | K: Send, 79 | V: Send, 80 | H: BuildHasher + Send, 81 | A: Allocator + Send, 82 | { 83 | } 84 | 85 | unsafe impl Sync for OwnedIter 86 | where 87 | K: Sync, 88 | V: Sync, 89 | H: BuildHasher + Sync, 90 | A: Allocator + Sync, 91 | { 92 | } 93 | 94 | /// Iterator over a [`LeapMap`] which yields immutable key-value pairs. The 95 | /// iterator returns a [`Ref`], which allows safe concurrent access of the key 96 | /// and the value from multiple threads. 97 | /// 98 | /// # Examples 99 | /// 100 | /// ``` 101 | /// use leapfrog::LeapMap; 102 | /// 103 | /// let mut map = LeapMap::new(); 104 | /// 105 | /// map.insert(12, 17); 106 | /// map.insert(42, 23); 107 | /// 108 | /// assert_eq!(map.iter().count(), 2); 109 | /// ``` 110 | pub struct Iter<'a, K, V, H: BuildHasher, A: Allocator> { 111 | map: &'a LeapMap, 112 | current: usize, 113 | } 114 | 115 | impl<'a, K, V, H, A> Clone for Iter<'a, K, V, H, A> 116 | where 117 | K: Eq + Hash + Copy, 118 | V: Value, 119 | H: BuildHasher + Default, 120 | A: Allocator, 121 | { 122 | fn clone(&self) -> Self { 123 | Iter::new(self.map) 124 | } 125 | } 126 | 127 | impl<'a, K, V, H, A> Iter<'a, K, V, H, A> 128 | where 129 | K: Eq + Hash + Copy, 130 | V: Value, 131 | H: BuildHasher + 'a, 132 | A: Allocator + 'a, 133 | { 134 | pub(crate) fn new(map: &'a LeapMap) -> Self { 135 | Self { 136 | map, 137 | current: 0usize, 138 | } 139 | } 140 | } 141 | 142 | impl<'a, K, V, H, A> Iterator for Iter<'a, K, V, H, A> 143 | where 144 | K: Eq + Hash + Copy, 145 | V: Value, 146 | H: BuildHasher + Default + 'a, 147 | A: Allocator + 'a, 148 | { 149 | type Item = Ref<'a, K, V, H, A>; 150 | 151 | fn next(&mut self) -> Option { 152 | loop { 153 | let current = self.current; 154 | if let Some(cell) = self.map.get_cell_at_index(current) { 155 | self.current = current + 1; 156 | if cell.hash == null_hash() { 157 | continue; 158 | } 159 | 160 | return Some(cell); 161 | } else { 162 | return None; 163 | } 164 | } 165 | } 166 | } 167 | 168 | unsafe impl<'a, K, V, H, A> Send for Iter<'a, K, V, H, A> 169 | where 170 | K: Send, 171 | V: Send, 172 | H: BuildHasher + Send, 173 | A: Allocator + Send, 174 | { 175 | } 176 | 177 | unsafe impl<'a, K, V, H, A> Sync for Iter<'a, K, V, H, A> 178 | where 179 | K: Sync, 180 | V: Sync, 181 | H: BuildHasher + Sync, 182 | A: Allocator + Sync, 183 | { 184 | } 185 | 186 | /// Iterator over a [`LeapMap`] which yields mutable key-value pairs. The iterator 187 | /// returns a [`RefMut`], which allows the iterated cell's value to be modified. 188 | /// 189 | /// # Examples 190 | /// 191 | /// ``` 192 | /// use leapfrog::LeapMap; 193 | /// 194 | /// let mut map = LeapMap::new(); 195 | /// 196 | /// map.insert(12, 17); 197 | /// map.insert(42, 23); 198 | /// 199 | /// assert_eq!(map.iter_mut().count(), 2); 200 | /// ``` 201 | pub struct IterMut<'a, K, V, H: BuildHasher, A: Allocator> { 202 | map: &'a LeapMap, 203 | current: usize, 204 | } 205 | 206 | impl<'a, K, V, H, A> Clone for IterMut<'a, K, V, H, A> 207 | where 208 | K: Eq + Hash + Copy, 209 | V: Value, 210 | H: BuildHasher + Default, 211 | A: Allocator, 212 | { 213 | fn clone(&self) -> Self { 214 | IterMut::new(self.map) 215 | } 216 | } 217 | 218 | impl<'a, K, V, H, A> IterMut<'a, K, V, H, A> 219 | where 220 | K: Eq + Hash + Copy, 221 | V: Value, 222 | H: BuildHasher + 'a, 223 | A: Allocator + 'a, 224 | { 225 | pub(crate) fn new(map: &'a LeapMap) -> Self { 226 | Self { 227 | map, 228 | current: 0usize, 229 | } 230 | } 231 | } 232 | 233 | impl<'a, K, V, H, A> Iterator for IterMut<'a, K, V, H, A> 234 | where 235 | K: Eq + Hash + Copy, 236 | V: Value, 237 | H: BuildHasher + Default + 'a, 238 | A: Allocator + 'a, 239 | { 240 | type Item = RefMut<'a, K, V, H, A>; 241 | 242 | fn next(&mut self) -> Option { 243 | loop { 244 | let current = self.current; 245 | if let Some(cell) = self.map.get_cell_at_index_mut(current) { 246 | self.current = current + 1; 247 | if cell.hash == null_hash() { 248 | continue; 249 | } 250 | 251 | return Some(cell); 252 | } else { 253 | return None; 254 | } 255 | } 256 | } 257 | } 258 | 259 | unsafe impl<'a, K, V, H, A> Send for IterMut<'a, K, V, H, A> 260 | where 261 | K: Send, 262 | V: Send, 263 | H: BuildHasher + Send, 264 | A: Allocator + Send, 265 | { 266 | } 267 | 268 | unsafe impl<'a, K, V, H, A> Sync for IterMut<'a, K, V, H, A> 269 | where 270 | K: Sync, 271 | V: Sync, 272 | H: BuildHasher + Sync, 273 | A: Allocator + Sync, 274 | { 275 | } 276 | -------------------------------------------------------------------------------- /src/leapmap_serde.rs: -------------------------------------------------------------------------------- 1 | use crate::{LeapMap, Value}; 2 | 3 | use core::{ 4 | fmt, 5 | hash::{BuildHasher, Hash}, 6 | marker::PhantomData, 7 | }; 8 | 9 | use serde_crate::{ 10 | de::{Deserialize, MapAccess, Visitor}, 11 | ser::{Serialize, SerializeMap, Serializer}, 12 | Deserializer, 13 | }; 14 | 15 | #[cfg(feature = "stable_alloc")] 16 | use allocator_api2::alloc::{Allocator, Global}; 17 | #[cfg(not(feature = "stable_alloc"))] 18 | use core::alloc::Allocator; 19 | #[cfg(not(feature = "stable_alloc"))] 20 | use std::alloc::Global; 21 | 22 | pub struct LeapMapVisitor { 23 | marker: PhantomData LeapMap>, 24 | } 25 | 26 | impl LeapMapVisitor 27 | where 28 | K: Eq + Hash, 29 | V: Value, 30 | H: BuildHasher + Clone, 31 | { 32 | fn new() -> Self { 33 | LeapMapVisitor { 34 | marker: PhantomData, 35 | } 36 | } 37 | } 38 | 39 | impl<'de, K, V, H> Visitor<'de> for LeapMapVisitor 40 | where 41 | K: Deserialize<'de> + Eq + Hash + Copy + std::fmt::Debug, 42 | V: Deserialize<'de> + Value, 43 | H: BuildHasher + Clone + Default, 44 | { 45 | type Value = LeapMap; 46 | 47 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 48 | formatter.write_str("a LeapMap") 49 | } 50 | 51 | fn visit_map(self, mut access: M) -> Result 52 | where 53 | M: MapAccess<'de>, 54 | { 55 | let size = access.size_hint().unwrap_or(4); 56 | let map = LeapMap::with_capacity_and_hasher(size, Default::default()); 57 | 58 | while let Some((key, value)) = access.next_entry()? { 59 | map.insert(key, value); 60 | } 61 | 62 | Ok(map) 63 | } 64 | } 65 | 66 | impl<'de, K, V, H> Deserialize<'de> for LeapMap 67 | where 68 | K: Deserialize<'de> + Eq + Hash + Copy + std::fmt::Debug, 69 | V: Deserialize<'de> + Value, 70 | H: BuildHasher + Clone + Default, 71 | { 72 | fn deserialize(deserializer: D) -> Result 73 | where 74 | D: Deserializer<'de>, 75 | { 76 | deserializer.deserialize_map(LeapMapVisitor::::new()) 77 | } 78 | } 79 | 80 | impl Serialize for LeapMap 81 | where 82 | K: Serialize + Eq + Hash + Copy, 83 | V: Serialize + Value, 84 | H: BuildHasher + Clone + Default, 85 | A: Allocator, 86 | { 87 | fn serialize(&self, serializer: S) -> Result 88 | where 89 | S: Serializer, 90 | { 91 | let mut map = serializer.serialize_map(Some(self.len()))?; 92 | 93 | for mut item_ref in self.iter() { 94 | if let Some((k, v)) = item_ref.key_value() { 95 | map.serialize_entry(&k, &v)?; 96 | } 97 | } 98 | 99 | map.end() 100 | } 101 | } 102 | 103 | #[cfg(test)] 104 | mod test { 105 | use crate::LeapMap; 106 | 107 | #[test] 108 | fn leapmap_serde() { 109 | let map = LeapMap::::new(); 110 | 111 | map.insert(0, 11); 112 | map.insert(1, 12); 113 | map.insert(2, 13); 114 | map.insert(3, 14); 115 | map.insert(4, 15); 116 | map.insert(5, 16); 117 | 118 | let serialized = serde_json::to_string(&map).expect("Couldn't serialize map"); 119 | let deserialized: LeapMap = 120 | serde_json::from_str(&serialized).expect("Couldn't deserialize map"); 121 | 122 | assert_eq!(deserialized.get(&0).unwrap().value(), Some(11)); 123 | assert_eq!(deserialized.get(&1).unwrap().value(), Some(12)); 124 | assert_eq!(deserialized.get(&2).unwrap().value(), Some(13)); 125 | assert_eq!(deserialized.get(&3).unwrap().value(), Some(14)); 126 | assert_eq!(deserialized.get(&4).unwrap().value(), Some(15)); 127 | assert_eq!(deserialized.get(&5).unwrap().value(), Some(16)); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/leapref.rs: -------------------------------------------------------------------------------- 1 | //! This module provides reference functionalty for referencing elements in a [`LeapMap`] 2 | //! is a thread-safe manner. 3 | 4 | use crate::{ 5 | leapmap::{AtomicCell, LeapMap}, 6 | Value, 7 | }; 8 | 9 | use core::{ 10 | hash::{BuildHasher, Hash}, 11 | sync::atomic::Ordering, 12 | }; 13 | 14 | #[cfg(feature = "stable_alloc")] 15 | use allocator_api2::alloc::Allocator; 16 | #[cfg(not(feature = "stable_alloc"))] 17 | use core::alloc::Allocator; 18 | 19 | /// A reference to an atomic cell in a [LeapMap], which cannot mutate 20 | /// the referenced cell value. 21 | pub struct Ref<'a, K, V, H, A: Allocator> { 22 | /// The atomic value which is being referenced. 23 | cell: &'a AtomicCell, 24 | /// Reference to the map in which the cell belongs. 25 | map: &'a LeapMap, 26 | /// The hash for the cell at the point when the cell was found 27 | pub(crate) hash: u64, 28 | } 29 | 30 | impl<'a, K, V, H, A> Ref<'a, K, V, H, A> 31 | where 32 | A: Allocator, 33 | V: Value, 34 | { 35 | /// Creates a new ref type referencing the specified `cell` and `map`. 36 | pub fn new( 37 | map: &'a LeapMap, 38 | cell: &'a AtomicCell, 39 | hash: u64, 40 | ) -> Ref<'a, K, V, H, A> { 41 | Ref { map, cell, hash } 42 | } 43 | 44 | /// Loads the key for the referenced cell. 45 | /// 46 | /// This requires `&mut self` since it's possible that the underlying data 47 | /// for the map has been migrated, and that therefore the referenced cell 48 | /// is out of date, and returning the current value will be incorrect. In 49 | /// such a case, the cell needs to be updated to reference the correct cell, 50 | /// hence `mut self`. This ensures that the returned value is always the 51 | /// most up to date value. 52 | /// 53 | /// It is also possible that the cell has been deleted, in which case the 54 | /// returned value will be `None` 55 | pub fn key(&mut self) -> Option 56 | where 57 | K: Eq + Hash + Copy, 58 | H: BuildHasher + Default, 59 | { 60 | self.key_value().map(|(k, _v)| k) 61 | } 62 | 63 | /// Loads the value for the referenced cell. 64 | /// 65 | /// This requires `&mut self` since it's possible that the underlying data 66 | /// for the map has been migrated, and that therefore the referenced cell 67 | /// is out of date, and returning the current value will be incorrect. In 68 | /// such a case, the cell needs to be updated to reference the correct cell, 69 | /// hence `mut self`. This ensures that the returned value is always the 70 | /// most up to date value. 71 | /// 72 | /// It is also possible that the cell has been deleted, in which case the 73 | /// returned value will be `None` 74 | pub fn value(&mut self) -> Option 75 | where 76 | K: Eq + Hash + Copy, 77 | H: BuildHasher + Default, 78 | { 79 | self.key_value().map(|(_k, v)| v) 80 | } 81 | 82 | /// Loads the key-value pair for the referenced cell. 83 | /// 84 | /// This requires `&mut self` since it's possible that the underlying data 85 | /// for the map has been migrated, and that therefore the referenced cell 86 | /// is out of date, and returning the current value will be incorrect. In 87 | /// such a case, the cell needs to be updated to reference the correct cell, 88 | /// hence `mut self`. This ensures that the returned value is always the 89 | /// most up to date value. 90 | /// 91 | /// It is also possible that the cell has been deleted, in which case the 92 | /// returned value will be `None`. 93 | pub fn key_value(&mut self) -> Option<(K, V)> 94 | where 95 | K: Eq + Hash + Copy, 96 | H: BuildHasher + Default, 97 | { 98 | loop { 99 | let value = self.cell.value.load(Ordering::Relaxed); 100 | if value.is_null() { 101 | return None; 102 | } 103 | let key = self.cell.key.load(Ordering::Relaxed); 104 | if value.is_redirect() || self.hash != self.cell.hash.load(Ordering::Relaxed) { 105 | // Map has/is being migrated, help and then try again ... 106 | self.map.participate_in_migration(); 107 | if let Some(new_cell) = self.map.find(&key, self.hash) { 108 | self.cell = new_cell; 109 | } else { 110 | // Migration caused removal of cell: 111 | return None; 112 | } 113 | } else { 114 | return Some((key, value)); 115 | } 116 | } 117 | } 118 | } 119 | 120 | /// A reference type to a cell in a [`LeapMap`] which can mutate the referenced 121 | /// cell value. 122 | pub struct RefMut<'a, K, V, H, A: Allocator> { 123 | /// The atomic value which is being referenced. 124 | cell: &'a AtomicCell, 125 | /// Reference to the map in which the cell belongs. 126 | map: &'a LeapMap, 127 | /// The hash for the cell at the point when the cell was found 128 | pub(crate) hash: u64, 129 | } 130 | 131 | impl<'a, K, V, H, A> RefMut<'a, K, V, H, A> 132 | where 133 | A: Allocator, 134 | V: Value, 135 | { 136 | /// Creates a new mutable reference type referencing the specified `cell` 137 | /// and `map`. 138 | pub fn new( 139 | map: &'a LeapMap, 140 | cell: &'a AtomicCell, 141 | hash: u64, 142 | ) -> RefMut<'a, K, V, H, A> { 143 | RefMut { map, cell, hash } 144 | } 145 | 146 | /// Loads the key for the referenced cell. 147 | /// 148 | /// This requires `&mut self` since it's possible that the underlying data 149 | /// for the map has been migrated, and that therefore the referenced cell 150 | /// is out of date, and returning the current value will be incorrect. In 151 | /// such a case, the cell needs to be updated to reference the correct cell, 152 | /// hence `mut self`. This ensures that the returned value is always the 153 | /// most up to date value. 154 | /// 155 | /// It is also possible that the cell has been deleted, in which case the 156 | /// returned value will be `None` 157 | pub fn key(&mut self) -> Option 158 | where 159 | K: Eq + Hash + Copy, 160 | H: BuildHasher + Default, 161 | { 162 | self.key_value().map(|(k, _v)| k) 163 | } 164 | 165 | /// Loads the value for the referenced cell. 166 | /// 167 | /// This requires `&mut self` since it's possible that the underlying data 168 | /// for the map has been migrated, and that therefore the referenced cell 169 | /// is out of date, and returning the current value will be incorrect. In 170 | /// such a case, the cell needs to be updated to reference the correct cell, 171 | /// hence `mut self`. This ensures that the returned value is always the 172 | /// most up to date value. 173 | /// 174 | /// It is also possible that the cell has been deleted, in which case the 175 | /// returned value will be `None` 176 | pub fn value(&mut self) -> Option 177 | where 178 | K: Eq + Hash + Copy, 179 | H: BuildHasher + Default, 180 | { 181 | self.key_value().map(|(_k, v)| v) 182 | } 183 | 184 | /// Loads the key-value pair for the referenced cell. 185 | /// 186 | /// This requires `&mut self` since it's possible that the underlying data 187 | /// for the map has been migrated, and that therefore the referenced cell 188 | /// is out of date, and returning the current value will be incorrect. In 189 | /// such a case, the cell needs to be updated to reference the correct cell, 190 | /// hence `mut self`. This ensures that the returned value is always the 191 | /// most up to date value. 192 | /// 193 | /// It is also possible that the cell has been deleted, in which case the 194 | /// returned value will be `None`. 195 | pub fn key_value(&mut self) -> Option<(K, V)> 196 | where 197 | K: Eq + Hash + Copy, 198 | H: BuildHasher + Default, 199 | { 200 | loop { 201 | let value = self.cell.value.load(Ordering::Relaxed); 202 | if value.is_null() { 203 | return None; 204 | } 205 | let key = self.cell.key.load(Ordering::Relaxed); 206 | if value.is_redirect() || self.hash != self.cell.hash.load(Ordering::Relaxed) { 207 | // Map has/is being migrated, help and then try again ... 208 | self.map.participate_in_migration(); 209 | if let Some(new_cell) = self.map.find(&key, self.hash) { 210 | self.cell = new_cell; 211 | } else { 212 | // Migration caused removal of cell: 213 | return None; 214 | } 215 | } else { 216 | return Some((key, value)); 217 | } 218 | } 219 | } 220 | 221 | /// Sets the value for the referenced cell, returning the old value if the 222 | /// cell is still valid, or `None` if the cell has been deleted. 223 | /// 224 | /// This requires `&mut self` since it's possible that the underlying data 225 | /// for the map has been migrated, and that therefore the referenced cell 226 | /// is out of date, and returning the current value will be incorrect. In 227 | /// such a case, the cell needs to be updated to reference the correct cell, 228 | /// hence `mut self`. This ensures that the store is only performed if the 229 | /// referenced cell is still valid. 230 | pub fn set_value(&mut self, value: V) -> Option 231 | where 232 | K: Eq + Hash + Copy, 233 | H: BuildHasher + Default, 234 | { 235 | loop { 236 | let current = self.cell.value.load(Ordering::Relaxed); 237 | 238 | if current.is_redirect() || self.hash != self.cell.hash.load(Ordering::Relaxed) { 239 | // Map has/is being migrated, help and then try again ... 240 | self.map.participate_in_migration(); 241 | let key = self.cell.key.load(Ordering::Relaxed); 242 | if let Some(new_cell) = self.map.find(&key, self.hash) { 243 | self.cell = new_cell; 244 | } else { 245 | // Cell has been removed 246 | return None; 247 | } 248 | } else if current.is_null() { 249 | // Value has been erased, we can just return. 250 | return None; 251 | } else if self 252 | .cell 253 | .value 254 | .compare_exchange_weak(current, value, Ordering::Relaxed, Ordering::Relaxed) 255 | .is_ok() 256 | { 257 | return Some(current); 258 | } 259 | } 260 | } 261 | 262 | /// Updates the value for the referenced cell, using the `func` to compute 263 | /// the new value, returning the old value if the cell is still in the map, 264 | /// and `None` if the cell has been deleted. 265 | /// 266 | /// # Examples 267 | /// 268 | /// ``` 269 | /// let map = leapfrog::LeapMap::new(); 270 | /// map.insert(1, 12); 271 | /// if let Some(mut kv_ref) = map.get_mut(&1) { 272 | /// kv_ref.update(|mut v| { 273 | /// *v += 1; 274 | /// }); 275 | /// } 276 | /// 277 | /// assert_eq!(map.get(&1).unwrap().value(), Some(13)); 278 | pub fn update(&mut self, mut func: F) -> Option 279 | where 280 | K: Eq + Hash + Copy, 281 | H: BuildHasher + Default, 282 | F: FnMut(&mut V), 283 | { 284 | loop { 285 | let current = self.cell.value.load(Ordering::Relaxed); 286 | let mut updated = current; 287 | func(&mut updated); 288 | 289 | if current.is_redirect() || self.hash != self.cell.hash.load(Ordering::Relaxed) { 290 | // Map has/is being migrated, help and then try again ... 291 | self.map.participate_in_migration(); 292 | let key = self.cell.key.load(Ordering::Relaxed); 293 | if let Some(new_cell) = self.map.find(&key, self.hash) { 294 | self.cell = new_cell; 295 | } else { 296 | // Cell has been removed: 297 | return None; 298 | } 299 | } else if current.is_null() { 300 | // Value has been erased. 301 | return None; 302 | } else if self 303 | .cell 304 | .value 305 | .compare_exchange_weak(current, updated, Ordering::Relaxed, Ordering::Relaxed) 306 | .is_ok() 307 | { 308 | return Some(current); 309 | } 310 | 311 | // Lost the race to update the cell, go and try again 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A concurrent hash table which uses leapfrog probing. This map is also lock-free 2 | //! **if** the key and value support atomic operations. If not, an efficient spin-lock 3 | //! is used instead. 4 | //! 5 | //! All operations on a [`LeapMap`] can be used concurrently with each other, and 6 | //! the map is both fast and scalable up to many threads. The interface for the map 7 | //! has been kept as close as possible to `std::collections::HashMap`, however, 8 | //! there are some differences to ensure that concurrent operations are always correct 9 | //! without decreasing performance. 10 | //! 11 | //! The biggest differences between the interface of the [`LeapMap`] and the 12 | //! `std::collections::HashMap` are the types returned by `get` and `get_mut`. 13 | //! For the [`LeapMap`], these methods return [`Ref`] and [`RefMut`] types, 14 | //! respectively, whose interfaces are designed to allow for accessing the cells 15 | //! returned by the those methods concurrently and correctly. As a result, it is 16 | //! not possible to get a reference to a cell's value, since the cell value type 17 | //! is atomic. The cell's key, value, and key-value pairs can be accessed with 18 | //! `key`, `value`, and `key_value` methods, respectively for [`Ref`] and 19 | //! [`RefMut`] types. For the [`RefMut`] type, the value can additionally be 20 | //! modified with `set_value` or `update`. These interfaces ensure that the most 21 | //! up to date value is loaded/stored/updated, and that if the referenced cell is 22 | //! invalidated/updated/erased by other threads, the [`Ref`]/[`RefMut`] type is 23 | //! updated appropriately. This makes working concurrently with the map simple, 24 | //! without sacrificing performance. 25 | //! 26 | //! The map uses [leapfrog probing](https://preshing.com/20160314/leapfrog-probing/) 27 | //! which is similar to [hopscotch hashing](https://en.wikipedia.org/wiki/Hopscotch_hashing) 28 | //! since it is based off of it. Leapfrog probing stores two offset per 29 | //! cell which are used to traverse a local neighbourhood of values 30 | //! around the cell to which a key hashes. This makes the map operations cache 31 | //! friendly and scalable, even under heavy contention. Keys are also stored, 32 | //! and to allow the [`LeapMap`] to still be efficient the hash of a key is also 33 | //! stored, which adds 8 bytes of overhead per key-value pair. This can very likely 34 | //! be improved, but this map is still in its early stages. The [`LeapMap`] is 35 | //! designed for performance so if memory efficiency is more important, a different 36 | //! map is likely a better choice. 37 | //! 38 | //! # HashMap 39 | //! 40 | //! This crate also provides [`HashMap`], which is an fast single-threaded 41 | //! version of the concurrent lock-free hash map, whose performance is roughly 42 | //! 1.2-1.5x the hash map implementation in the standard library, when using the 43 | //! same hash function. The tradeoff that is currently made to enable this performance 44 | //! is increased memory use, since the map stores the offsets (8 bytes) and the 45 | //! hashed value for a key (8 bytes). This may make the map unsuitable for some 46 | //! use cases. 47 | //! 48 | //! # Hash Functions 49 | //! 50 | //! By default, the default hash function from the standard library is used for 51 | //! both the [`HashMap`] and the [`LeapMap`], since it is DOS resistant. It is, 52 | //! however, more expensive to evaluate. The [`MurmurHasher`] can be used instead, 53 | //! which is quite a bit faster but which is **not** DOS resistant. Additionally, the 54 | //! default initialization of the [`HashMap`] and [`LeapMap`] data requires that 55 | //! 0 not be used as a key for the map *if the [`MurmurHasher`] is used*. With the 56 | //! default hasher this limitation does not apply. Any other hash functions can 57 | //! be used by creating the maps with `with_hasher` or `with_capacity_and_hasher`. 58 | //! 59 | //! # Performance 60 | //! 61 | //! This map has been benchmarked against other hash maps across multiple threads 62 | //! and multiple workloads. The benchmarks can be found [here](https://github.com/robclu/conc-map-bench). 63 | //! A snapshot of the results for a read heavy workload are the following (with 64 | //! throughput in Mops/second, cores in brackets, and performance realtive to 65 | //! `std::collections::HashMap` with RwLock). While these benchmarks show good 66 | //! performance, **please take note of the limitations described above**, and 67 | //! benchmark for your use case. Please also create an issue if there are either 68 | //! correctness or performance problems for a specific use case. For a read heavy 69 | //! workload on 16 cores the following was benchmarked: 70 | //! 71 | //! | Map | Throughput (1) | Relative (1) | Throughput (16) | Relative (16) | 72 | //! |------------------|----------------|--------------|-----------------|---------------| 73 | //! | RwLock + HashMap | 19.4 | 1.0 | 11.7 | 0.60 | 74 | //! | Flurry | 11.2 | 0.58 | 80.8 | 4.16 | 75 | //! | DashMap | 14.1 | 0.72 | 87.5 | 4.51 | 76 | //! | LeapMap | 17.8 | 0.92 | 148.0 | 7.62 | 77 | //! 78 | //! On an 12 core M2 Max, the results were the following: 79 | //! 80 | //! | Map | Throughput (1) | Relative (1) | Throughput (12) | Relative (12) | 81 | //! |------------------|----------------|--------------|-----------------|---------------| 82 | //! | RwLock + HashMap | 21.2 | 1.0 | 3.4 | 0.16 | 83 | //! | Flurry | 8.0 | 0.37 | 64.5 | 3.04 | 84 | //! | DashMap | 10.4 | 0.49 | 42.2 | 2.00 | 85 | //! | Evmap | 13.5 | 0.64 | 18.7 | 0.88 | 86 | //! | LeapMap | 13.9 | 0.66 | 106.1 | 5.00 | 87 | //! 88 | //! On an exchange heavy benchmark, the leapmap was even faster. 89 | //! 90 | //! The performance of the [`LeapMap`] is limited is when rapidly growing the map, since 91 | //! the bottleneck then becomes the resizing (allocation) and migration operations. 92 | //! The map **is not** designed to be resized often (resizing infrequently has very 93 | //! little impact on performance, however), so it's best to use an initial capacity 94 | //! which is close to the expected maximum number of elements for the map. The growing 95 | //! performace is slightly worse than DashMap's growing performance (see the 96 | //! benchmarks). If resizing is frequent, this map is currently not the best choice, 97 | //! however, this can be improved by using a more efficient allocator. 98 | //! 99 | //! # Consistency 100 | //! 101 | //! All operations on a [`LeapMap`] map are non-blocking, and accessing/updating 102 | //! a value in the map will not lock **if the value type has built-in atomic support**. 103 | //! The map can be iterated (mutable or immutably) from multiple threads while 104 | //! concurrently calling the operations directly on the map from other threads. 105 | //! 106 | //! # Limitations 107 | //! 108 | //! Getting the length of the map does not return the length of the map, but rather an 109 | //! estimate of the length, unless the calling thread is the only thread operating 110 | //! on the map, and therefore there are no other readers or writers. Additionally, 111 | //! the length is expensive to compute since it is not tracked as insertions and 112 | //! removals are performed. The overhead of doing so when under contention 113 | //! was found to be significant during benchmarking, and given that it's only an 114 | //! estimate, it's not worth the cost. 115 | //! 116 | //! The same applies for `is_empty`. 117 | //! 118 | //! The size of the map must always be a power of two. This is handled internally 119 | //! and may change in the future to improve memory efficiency. 120 | //! 121 | //! The value type for the map needs to implement the [`Value`] trait, which is 122 | //! simple enough to implement, however, two values need to be chosen: one as a 123 | //! null value and another as a redirect value. For integer types MAX and MAX-1 124 | //! are used respectively. A good choice is values which are efficient for 125 | //! comparison. 126 | //! 127 | //! # Resizing 128 | //! 129 | //! The buckets for the map are expanded when an insertion would result in a 130 | //! offset which would overflow the maximum probe offset (i.e when it's not 131 | //! possible to find an empty cell within the probe neighbourhood), or shrunk 132 | //! when probes are too far apart. The resizing and subsequent removal of buckets 133 | //! from the old table to the new table will happen concurrently when multiple 134 | //! threads are attempting to modify the map, which makes the reszing 135 | //! efficient. Despite this, it is best to choose a larger size where possible, 136 | //! since it will ensure more stable performance of map operations. When the table 137 | //! is resized, the old memory is cleanup up using what is essentially a quescient 138 | //! based reclaimation strategy. 139 | //! 140 | //! # Features 141 | //! 142 | //! There is optional support for serde, via the "serde" feature. 143 | //! 144 | //! # Usage with stable 145 | //! 146 | //! This crate requires the `allocator_api` feature, which is only available on nightly. 147 | //! To enable use of the crate with the stable toolchain, the `"stable_alloc"` feature has 148 | //! been added. 149 | //! 150 | //! If/when the `allocator_api` feature is no longer experimental, this feature flag will 151 | //! be removed. 152 | 153 | #![cfg_attr(not(feature = "stable_alloc"), feature(allocator_api))] 154 | 155 | mod hashentry; 156 | mod hashiter; 157 | pub mod hashmap; 158 | mod leapiter; 159 | pub mod leapmap; 160 | pub mod leapref; 161 | pub mod util; 162 | 163 | #[cfg(feature = "serde")] 164 | mod hashmap_serde; 165 | 166 | #[cfg(feature = "serde")] 167 | mod leapmap_serde; 168 | 169 | use crate::util::load_u64_le; 170 | 171 | use core::{ 172 | borrow::Borrow, 173 | default::Default, 174 | fmt::Debug, 175 | hash::{BuildHasher, Hash, Hasher}, 176 | }; 177 | 178 | pub use hashmap::HashMap; 179 | pub use leapmap::LeapMap; 180 | pub use leapref::Ref; 181 | pub use leapref::RefMut; 182 | 183 | /// Trait which represents a value which can be stored in a map. 184 | pub trait Value: Sized + Debug + Clone + Copy { 185 | /// Must return true if the value is the redirect value. 186 | fn is_redirect(&self) -> bool; 187 | 188 | /// Returns true if the value is a null value. 189 | fn is_null(&self) -> bool; 190 | 191 | /// Returns the redirect value. 192 | fn redirect() -> Self; 193 | 194 | /// Returns the null value. 195 | fn null() -> Self; 196 | } 197 | 198 | macro_rules! value_impl { 199 | ($type:ty, $redirect_expr:expr, $null_expr:expr) => { 200 | impl Value for $type { 201 | #[inline] 202 | fn is_redirect(&self) -> bool { 203 | *self == $redirect_expr 204 | } 205 | #[inline] 206 | fn is_null(&self) -> bool { 207 | *self == $null_expr 208 | } 209 | #[inline] 210 | fn redirect() -> Self { 211 | $redirect_expr 212 | } 213 | #[inline] 214 | fn null() -> Self { 215 | $null_expr 216 | } 217 | } 218 | }; 219 | } 220 | 221 | value_impl!(u8, u8::MAX, u8::MAX - 1); 222 | value_impl!(u16, u16::MAX, u16::MAX - 1); 223 | value_impl!(u32, u32::MAX, u32::MAX - 1); 224 | value_impl!(u64, u64::MAX, u64::MAX - 1); 225 | value_impl!(i8, i8::MAX, i8::MAX - 1); 226 | value_impl!(i16, i16::MAX, i16::MAX - 1); 227 | value_impl!(i32, i32::MAX, i32::MAX - 1); 228 | value_impl!(i64, i64::MAX, i64::MAX - 1); 229 | value_impl!(usize, usize::MAX, usize::MAX - 1); 230 | 231 | /// Creates a hash value from the `hash_builder` and `value`. 232 | pub(crate) fn make_hash(hash_builder: &S, value: &Q) -> u64 233 | where 234 | K: Borrow, 235 | Q: Hash + ?Sized, 236 | S: BuildHasher, 237 | { 238 | let mut hasher = hash_builder.build_hasher(); 239 | value.hash(&mut hasher); 240 | hasher.finish() 241 | } 242 | 243 | /// Implementation of a hasher which hashes using murmur. 244 | #[derive(Default)] 245 | pub struct MurmurHasher(u64); 246 | 247 | impl Hasher for MurmurHasher { 248 | #[inline] 249 | fn finish(&self) -> u64 { 250 | self.0 251 | } 252 | 253 | #[inline] 254 | fn write(&mut self, bytes: &[u8]) { 255 | let mut v = load_u64_le(bytes, bytes.len()); 256 | v ^= v >> 33; 257 | v = v.wrapping_mul(0xff51afd7ed558ccd); 258 | v ^= v >> 33; 259 | v = v.wrapping_mul(0xc4ceb9fe1a85ec53); 260 | v ^= v >> 33; 261 | *self = MurmurHasher(v); 262 | } 263 | } 264 | 265 | /// Implementaion of hasher which hashes using FNV (Fowler-Noll-Vo). 266 | pub struct FnvHasher(u64); 267 | 268 | impl Default for FnvHasher { 269 | #[inline] 270 | fn default() -> FnvHasher { 271 | FnvHasher(0xcbf29ce484222325) 272 | } 273 | } 274 | 275 | impl Hasher for FnvHasher { 276 | #[inline] 277 | fn finish(&self) -> u64 { 278 | self.0 279 | } 280 | 281 | #[inline] 282 | fn write(&mut self, bytes: &[u8]) { 283 | let FnvHasher(mut hash) = *self; 284 | 285 | for byte in bytes.iter() { 286 | hash = hash ^ (*byte as u64); 287 | hash = hash.wrapping_mul(0x100000001b3); 288 | } 289 | 290 | *self = FnvHasher(hash); 291 | } 292 | } 293 | 294 | /// This is not really a hasher, but more of a mock hasher, as it just returns the value 295 | /// to be hashed. However, if it's known that the key is unique, it might be useful in 296 | /// such a scenario. 297 | #[derive(Default)] 298 | pub struct SimpleHasher(u64); 299 | 300 | impl Hasher for SimpleHasher { 301 | #[inline(always)] 302 | fn finish(&self) -> u64 { 303 | self.0 304 | } 305 | 306 | #[inline(always)] 307 | fn write(&mut self, bytes: &[u8]) { 308 | *self = SimpleHasher(load_u64_le(bytes, bytes.len())); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | //! This module provides utility functionality used throughout the crate. 2 | 3 | use core::{ 4 | alloc::Layout, 5 | mem::{align_of, size_of}, 6 | ops::{Add, BitOr, Shr, Sub, SubAssign}, 7 | }; 8 | 9 | #[cfg(feature = "stable_alloc")] 10 | use allocator_api2::alloc::Allocator; 11 | #[cfg(not(feature = "stable_alloc"))] 12 | use core::alloc::Allocator; 13 | 14 | /// Loads the buffer `buf` as a u64. 15 | #[inline(always)] 16 | pub fn load_u64_le(buf: &[u8], len: usize) -> u64 { 17 | debug_assert!(len <= buf.len()); 18 | let mut data = 0u64; 19 | let ptr: *mut _ = &mut data; 20 | unsafe { 21 | std::ptr::copy_nonoverlapping(buf.as_ptr(), ptr as *mut u8, len); 22 | } 23 | data.to_le() 24 | } 25 | 26 | /// Rounds the `value` up to the nearest power of two. 27 | #[inline] 28 | pub fn round_to_pow2(value: T) -> T 29 | where 30 | T: SubAssign 31 | + Add 32 | + Sub 33 | + Shr 34 | + BitOr 35 | + Copy 36 | + From, 37 | { 38 | let v = value - T::from(1); 39 | let res = match size_of::() { 40 | 1 => { 41 | let v = v | (v >> T::from(1)); 42 | let v = v | (v >> T::from(2)); 43 | v | (v >> T::from(4)) 44 | } 45 | 2 => { 46 | let v = v | (v >> T::from(1)); 47 | let v = v | (v >> T::from(2)); 48 | let v = v | (v >> T::from(4)); 49 | v | (v >> T::from(8)) 50 | } 51 | 4 => { 52 | let v = v | (v >> T::from(1)); 53 | let v = v | (v >> T::from(2)); 54 | let v = v | (v >> T::from(4)); 55 | let v = v | (v >> T::from(8)); 56 | v | (v >> T::from(16)) 57 | } 58 | 8 => { 59 | let v = v | (v >> T::from(1)); 60 | let v = v | (v >> T::from(2)); 61 | let v = v | (v >> T::from(4)); 62 | let v = v | (v >> T::from(8)); 63 | let v = v | (v >> T::from(16)); 64 | v | (v >> T::from(32)) 65 | } 66 | _ => v, 67 | }; 68 | res + T::from(1) 69 | } 70 | 71 | /// Kinds of allocation initialization. 72 | pub enum AllocationKind { 73 | /// Allocation should be zeroed. 74 | Zeroed, 75 | /// Allocation can be uninitialized. 76 | Uninitialized, 77 | } 78 | 79 | /// Allocates `count` number of elements of type T, using the `allocator`. 80 | pub(crate) fn allocate( 81 | allocator: &A, 82 | count: usize, 83 | kind: AllocationKind, 84 | ) -> *mut T { 85 | let size = size_of::(); 86 | let align = align_of::(); 87 | 88 | // We unwrap here because we want to panic if we fail to get a valid layout 89 | let layout = Layout::from_size_align(size * count, align).unwrap(); 90 | 91 | // Again, unwrap the allocation result. It should never fail to allocate. 92 | match kind { 93 | AllocationKind::Zeroed => allocator.allocate_zeroed(layout).unwrap().as_ptr() as *mut T, 94 | AllocationKind::Uninitialized => allocator.allocate(layout).unwrap().as_ptr() as *mut T, 95 | } 96 | } 97 | 98 | /// Deallocates `count` number of elements of type T, using the `allocator`. 99 | pub(crate) fn deallocate(allocator: &A, ptr: *mut T, count: usize) { 100 | let size = size_of::(); 101 | let align = align_of::(); 102 | 103 | // We unwrap here because we want to panic if we fail to get a valid layout 104 | let layout = Layout::from_size_align(size * count, align).unwrap(); 105 | 106 | // Again, unwrap the allocation result. It should never fail to allocate. 107 | let raw_ptr = ptr as *mut u8; 108 | let nonnull_ptr = std::ptr::NonNull::new(raw_ptr).unwrap(); 109 | unsafe { 110 | allocator.deallocate(nonnull_ptr, layout); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/basic.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicU64, Ordering}; 2 | use core_affinity::CoreId; 3 | use leapfrog::util::round_to_pow2; 4 | use leapfrog::{LeapMap, Value}; 5 | use rand::{thread_rng, Rng}; 6 | use std::collections::BTreeMap; 7 | use std::sync::Arc; 8 | 9 | const NUM_THREADS: u64 = 16; 10 | const KEYS_TO_INSERT: u64 = (1 << (NUM_THREADS + 9)) / NUM_THREADS; 11 | 12 | #[test] 13 | fn create_map() { 14 | const ELEMENTS: usize = 100; 15 | let leapmap = Arc::new(LeapMap::::with_capacity(ELEMENTS)); 16 | 17 | let mut threads = vec![]; 18 | for _ in 0..4 { 19 | let map = leapmap.clone(); 20 | threads.push(std::thread::spawn(move || { 21 | core_affinity::set_for_current(CoreId { id: 0 }); 22 | assert_eq!(map.capacity(), round_to_pow2(ELEMENTS)); 23 | })); 24 | } 25 | 26 | for t in threads { 27 | t.join().unwrap(); 28 | } 29 | } 30 | 31 | fn insert_keys( 32 | map: &Arc>, 33 | relative_prime: u64, 34 | start_index: u64, 35 | thread_index: u64, 36 | ) -> u64 { 37 | let mut index = start_index + thread_index * (KEYS_TO_INSERT + 2); 38 | let mut checksum = 0u64; 39 | for _ in 0..KEYS_TO_INSERT { 40 | let key = index.wrapping_mul(relative_prime); 41 | let key = key ^ (key >> 16); 42 | if key != u64::default() && key != ::redirect() { 43 | match map.insert(key, key) { 44 | Some(old) => { 45 | // This can only happen if we have generated a key which 46 | // hashes to the same value: 47 | assert!(map.hash_usize(&key) == map.hash_usize(&old)); 48 | } 49 | None => { 50 | checksum = checksum.wrapping_add(key); 51 | } 52 | } 53 | } 54 | index += 1; 55 | } 56 | checksum 57 | } 58 | 59 | fn remove_keys( 60 | map: &Arc>, 61 | relative_prime: u64, 62 | start_index: u64, 63 | thread_index: u64, 64 | ) -> u64 { 65 | let mut index = start_index + thread_index * (KEYS_TO_INSERT + 2); 66 | let mut checksum = 0u64; 67 | for _ in 0..KEYS_TO_INSERT { 68 | let key = index.wrapping_mul(relative_prime); 69 | let key = key ^ (key >> 16); 70 | if key != u64::default() && key != ::redirect() { 71 | match map.get(&key) { 72 | Some(mut value_ref) => { 73 | checksum = checksum.wrapping_add(value_ref.value().unwrap()); 74 | /* 75 | match value_ref.value() { 76 | Some(v) => { 77 | checksum = checksum.wrapping_add(v); 78 | } 79 | None => { 80 | // This can only happen when map is migrating, 81 | // which should not be happening here; 82 | assert!(key == 0); 83 | } 84 | } 85 | */ 86 | } 87 | None => { 88 | // Didn't find the key, which should not happen! 89 | assert!(key == 0); 90 | } 91 | } 92 | } 93 | index += 1; 94 | } 95 | checksum 96 | } 97 | 98 | #[test] 99 | fn insert_different_keys() { 100 | let leapmap = Arc::new(LeapMap::::with_capacity(1 << 24)); 101 | 102 | let mut rng = thread_rng(); 103 | let start_index: u64 = rng.gen(); 104 | let value: u64 = rng.gen(); 105 | let relative_prime: u64 = value.wrapping_mul(2) + 1; 106 | 107 | let mut threads = vec![]; 108 | let insert_checksum = Arc::new(AtomicU64::new(0)); 109 | let remove_checksum = Arc::new(AtomicU64::new(0)); 110 | let insert_flag = Arc::new(AtomicU64::new(0)); 111 | let remove_flag = Arc::new(AtomicU64::new(0)); 112 | 113 | for i in 0..NUM_THREADS / 2 { 114 | let map = leapmap.clone(); 115 | let start_flag = insert_flag.clone(); 116 | let end_flag = remove_flag.clone(); 117 | let sum = insert_checksum.clone(); 118 | threads.push(std::thread::spawn(move || { 119 | core_affinity::set_for_current(CoreId { id: i as usize }); 120 | 121 | start_flag.fetch_add(1, Ordering::Relaxed); 122 | while start_flag.load(Ordering::Relaxed) < NUM_THREADS / 2 { 123 | std::hint::spin_loop(); 124 | } 125 | let start = std::time::Instant::now(); 126 | let local_sum = insert_keys(&map, relative_prime, start_index, i); 127 | 128 | sum.fetch_add(local_sum, Ordering::Relaxed); 129 | end_flag.fetch_add(1, Ordering::Relaxed); 130 | 131 | let end = std::time::Instant::now(); 132 | let time = (end - start).as_millis(); 133 | println!( 134 | "[Insert] Thread: {0:<2}, {1} ms, {2} keys/sec", 135 | i, 136 | time, 137 | KEYS_TO_INSERT as f32 * (1000_f32 / time as f32), 138 | ); 139 | })); 140 | } 141 | 142 | for i in NUM_THREADS / 2..NUM_THREADS { 143 | let map = leapmap.clone(); 144 | let start_flag = remove_flag.clone(); 145 | let sum = remove_checksum.clone(); 146 | threads.push(std::thread::spawn(move || { 147 | core_affinity::set_for_current(CoreId { id: i as usize }); 148 | 149 | while start_flag.load(Ordering::Relaxed) < NUM_THREADS / 2 { 150 | std::hint::spin_loop(); 151 | } 152 | let start = std::time::Instant::now(); 153 | let local_sum = remove_keys(&map, relative_prime, start_index, i - NUM_THREADS / 2); 154 | 155 | sum.fetch_add(local_sum, Ordering::Relaxed); 156 | 157 | let end = std::time::Instant::now(); 158 | let time = (end - start).as_millis(); 159 | println!( 160 | "[Remove] Thread: {0:<2}, {1} ms, {2} keys/sec ", 161 | i, 162 | time, 163 | KEYS_TO_INSERT as f32 * (1000_f32 / time as f32), 164 | ); 165 | })); 166 | } 167 | 168 | for t in threads { 169 | t.join().unwrap(); 170 | } 171 | 172 | let insert = insert_checksum.load(Ordering::Relaxed); 173 | let remove = remove_checksum.load(Ordering::Relaxed); 174 | 175 | println!( 176 | "Insert {} : Remove {} : Diff {}{}", 177 | insert, 178 | remove, 179 | if insert > remove { "+" } else { "-" }, 180 | insert.max(remove) - insert.min(remove) 181 | ); 182 | assert!(insert == remove); 183 | } 184 | 185 | fn generate_kvs(keys: usize) -> BTreeMap { 186 | let mut map = BTreeMap::new(); 187 | 188 | let mut rng = thread_rng(); 189 | let start_index: u32 = rng.gen(); 190 | let value: u32 = rng.gen(); 191 | let relative_prime: u64 = value as u64 * 2 + 1; 192 | 193 | let mut index = start_index; 194 | for _ in 0..keys { 195 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 196 | key = key ^ (key >> 16); 197 | map.insert(key, key + 1); 198 | 199 | index += 1; 200 | } 201 | 202 | map 203 | } 204 | 205 | #[test] 206 | fn leapmap_into_iter() { 207 | const KEYS: usize = 150; 208 | let map = LeapMap::new(); 209 | let kv_map = generate_kvs(KEYS); 210 | 211 | for (k, v) in kv_map.iter() { 212 | let val = *v; 213 | let key = *k; 214 | map.insert(key, val); 215 | } 216 | 217 | assert_eq!(map.len(), KEYS); 218 | 219 | let mut count = 0usize; 220 | for (k, v) in map.into_iter() { 221 | if let Some(val) = kv_map.get(&k) { 222 | assert_eq!(v, *val); 223 | } else { 224 | panic!("LeapMap value is incorrect"); 225 | } 226 | count += 1; 227 | } 228 | 229 | assert_eq!(count, KEYS); 230 | } 231 | 232 | #[test] 233 | fn leapmap_iter() { 234 | const KEYS: usize = 150; 235 | let map = LeapMap::new(); 236 | let kv_map = generate_kvs(KEYS); 237 | 238 | for (k, v) in kv_map.iter() { 239 | let val = *v; 240 | let key = *k; 241 | map.insert(key, val); 242 | } 243 | 244 | assert_eq!(map.len(), KEYS); 245 | 246 | let mut count = 0usize; 247 | for mut item in map.iter() { 248 | let (k, v) = item.key_value().unwrap(); 249 | 250 | if let Some(val) = kv_map.get(&k) { 251 | assert_eq!(v, *val); 252 | } else { 253 | panic!("LeapMap value is incorrect"); 254 | } 255 | count += 1; 256 | } 257 | 258 | assert_eq!(count, KEYS); 259 | 260 | for k in kv_map.keys() { 261 | map.remove(k); 262 | } 263 | 264 | for mut item in map.iter() { 265 | assert!(item.key_value().is_none()); 266 | } 267 | } 268 | 269 | #[test] 270 | fn leapmap_iter_mut() { 271 | const KEYS: usize = 150; 272 | let map = LeapMap::new(); 273 | let kv_map = generate_kvs(KEYS); 274 | 275 | for (k, v) in kv_map.iter() { 276 | let val = *v; 277 | let key = *k; 278 | map.insert(key, val); 279 | } 280 | 281 | assert_eq!(map.len(), KEYS); 282 | 283 | let mut count = 0usize; 284 | for mut item in map.iter_mut() { 285 | let k = item.key().unwrap(); 286 | let old = item.update(|v| { 287 | *v += 2; 288 | }); 289 | assert_eq!(*kv_map.get(&k).unwrap(), old.unwrap()); 290 | count += 1; 291 | } 292 | assert_eq!(count, KEYS); 293 | 294 | for mut item in map.iter() { 295 | let (k, v) = item.key_value().unwrap(); 296 | 297 | if let Some(val) = kv_map.get(&k) { 298 | assert_eq!(v, *val + 2); 299 | } else { 300 | panic!("LeapMap value is incorrect"); 301 | } 302 | count += 1; 303 | } 304 | 305 | for k in kv_map.keys() { 306 | map.remove(k); 307 | } 308 | 309 | for mut item in map.iter() { 310 | assert!(item.key_value().is_none()); 311 | } 312 | } 313 | 314 | 315 | #[test] 316 | fn leapmap_try_insert() { 317 | let map = LeapMap::new(); 318 | map.insert(1, 1); 319 | map.remove(&1); 320 | map.try_insert(1, 2); 321 | let value = map.get(&1); 322 | 323 | if let Some(mut val) = value { 324 | assert_eq!(val.value(), Some(2)); 325 | } else { 326 | panic!("LeapMap value is incorrect"); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /tests/cuckoo.rs: -------------------------------------------------------------------------------- 1 | use leapfrog::LeapMap; 2 | use rand::distributions::{Distribution, Uniform}; 3 | use std::sync::{ 4 | atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}, 5 | Arc, 6 | }; 7 | use std::thread; 8 | 9 | /// Number of keys and values to work with. 10 | const NUM_KEYS: usize = 1 << 12; 11 | /// Number of threads that should be started. 12 | const NUM_THREADS: usize = 4; 13 | /// How long the stress test will run (in milliseconds). 14 | const TEST_LEN: u64 = 5000; 15 | 16 | type Key = u64; 17 | type Value = u64; 18 | 19 | struct Environment { 20 | table1: LeapMap, 21 | table2: LeapMap, 22 | keys: Vec, 23 | vals1: Vec, 24 | vals2: Vec, 25 | ind_dist: Uniform, 26 | val_dist1: Uniform, 27 | val_dist2: Uniform, 28 | in_table: Vec, 29 | in_use: Vec, 30 | finished: AtomicBool, 31 | num_inserts: AtomicUsize, 32 | num_deletes: AtomicUsize, 33 | num_updates: AtomicUsize, 34 | num_finds: AtomicUsize, 35 | } 36 | 37 | impl Environment { 38 | pub fn new() -> Self { 39 | let mut keys = Vec::with_capacity(NUM_KEYS); 40 | let mut in_use = Vec::with_capacity(NUM_KEYS); 41 | let mut in_table = Vec::with_capacity(NUM_KEYS); 42 | let mut vals1 = Vec::with_capacity(NUM_KEYS); 43 | let mut vals2 = Vec::with_capacity(NUM_KEYS); 44 | 45 | for i in 1..NUM_KEYS + 1 { 46 | keys.push(i as u64); 47 | vals1.push(AtomicU64::new(0)); 48 | vals2.push(AtomicU64::new(0)); 49 | in_use.push(AtomicBool::new(false)); 50 | in_table.push(AtomicBool::new(false)); 51 | } 52 | 53 | Self { 54 | table1: LeapMap::new(), 55 | table2: LeapMap::new(), 56 | keys, 57 | vals1, 58 | vals2, 59 | ind_dist: Uniform::from(0..NUM_KEYS - 1), 60 | val_dist1: Uniform::from(Value::min_value()..Value::max_value() - 2), 61 | val_dist2: Uniform::from(Value::min_value()..Value::max_value() - 2), 62 | in_table, 63 | in_use, 64 | finished: AtomicBool::new(false), 65 | num_inserts: AtomicUsize::new(0), 66 | num_deletes: AtomicUsize::new(0), 67 | num_updates: AtomicUsize::new(0), 68 | num_finds: AtomicUsize::new(0), 69 | } 70 | } 71 | } 72 | 73 | fn stress_insert_thread(env: Arc) { 74 | let mut rng = rand::thread_rng(); 75 | while !env.finished.load(Ordering::SeqCst) { 76 | let idx = env.ind_dist.sample(&mut rng); 77 | if env.in_use[idx] 78 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed) 79 | .is_ok() 80 | { 81 | let key = env.keys[idx]; 82 | let val1 = env.val_dist1.sample(&mut rng); 83 | let val2 = env.val_dist2.sample(&mut rng); 84 | let res1 = if !env.table1.contains_key(&key) { 85 | env.table1.insert(key, val1).map_or(true, |_| false) 86 | } else { 87 | false 88 | }; 89 | 90 | let res2 = if !env.table2.contains_key(&key) { 91 | match env.table2.insert(key, val2) { 92 | Some(_) => false, 93 | None => true, 94 | } 95 | } else { 96 | false 97 | }; 98 | 99 | let in_table = env.in_table[idx].load(Ordering::Relaxed); 100 | assert_ne!(res1, in_table); 101 | assert_ne!(res2, in_table); 102 | if res1 { 103 | assert_eq!(env.table1.get(&key).unwrap().value(), Some(val1)); 104 | assert_eq!(env.table2.get(&key).unwrap().value(), Some(val2)); 105 | env.vals1[idx].store(val1, Ordering::Relaxed); 106 | env.vals2[idx].store(val2, Ordering::Relaxed); 107 | env.in_table[idx].store(true, Ordering::Relaxed); 108 | env.num_inserts.fetch_add(2, Ordering::Relaxed); 109 | } 110 | env.in_use[idx].store(false, Ordering::SeqCst); 111 | } 112 | } 113 | } 114 | 115 | fn stress_delete_thread(env: Arc) { 116 | let mut rng = rand::thread_rng(); 117 | while !env.finished.load(Ordering::SeqCst) { 118 | let idx = env.ind_dist.sample(&mut rng); 119 | if env.in_use[idx] 120 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed) 121 | .is_ok() 122 | { 123 | let key = env.keys[idx]; 124 | let in_table = env.in_table[idx].load(Ordering::Relaxed); 125 | let res1 = env.table1.remove(&key).map_or(false, |_| true); 126 | let res2 = env.table2.remove(&key).map_or(false, |_| true); 127 | if res1 != in_table { 128 | println!("Error: {} {}", key, env.vals1[idx].load(Ordering::Relaxed)); 129 | } 130 | assert_eq!(res1, in_table); 131 | assert_eq!(res2, in_table); 132 | if res1 { 133 | assert!(env.table1.get(&key).is_none()); 134 | assert!(env.table2.get(&key).is_none()); 135 | env.in_table[idx].store(false, Ordering::Relaxed); 136 | env.num_deletes.fetch_add(2, Ordering::Relaxed); 137 | } 138 | env.in_use[idx].store(false, Ordering::SeqCst); 139 | } 140 | } 141 | } 142 | 143 | fn stress_find_thread(env: Arc) { 144 | let mut rng = rand::thread_rng(); 145 | while !env.finished.load(Ordering::SeqCst) { 146 | let idx = env.ind_dist.sample(&mut rng); 147 | if env.in_use[idx] 148 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed) 149 | .is_ok() 150 | { 151 | let key = env.keys[idx]; 152 | let val1 = env.vals1[idx].load(Ordering::Relaxed); 153 | let val2 = env.vals2[idx].load(Ordering::Relaxed); 154 | 155 | let value = env.table1.get(&key); 156 | if value.is_some() { 157 | assert_eq!(Some(val1), value.unwrap().value()); 158 | assert!(env.in_table[idx].load(Ordering::Relaxed)); 159 | } 160 | let value = env.table2.get(&key); 161 | if value.is_some() { 162 | assert_eq!(Some(val2), value.unwrap().value()); 163 | assert!(env.in_table[idx].load(Ordering::Relaxed)); 164 | } 165 | env.num_finds.fetch_add(2, Ordering::Relaxed); 166 | env.in_use[idx].swap(false, Ordering::SeqCst); 167 | } 168 | } 169 | } 170 | 171 | fn stress_update_thread(env: Arc) { 172 | let mut rng = rand::thread_rng(); 173 | 174 | while !env.finished.load(Ordering::SeqCst) { 175 | let idx = env.ind_dist.sample(&mut rng); 176 | if env.in_use[idx] 177 | .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed) 178 | .is_ok() 179 | { 180 | let key = env.keys[idx]; 181 | let val1 = env.val_dist1.sample(&mut rng); 182 | let val2 = env.val_dist2.sample(&mut rng); 183 | let in_table = env.in_table[idx].load(Ordering::Relaxed); 184 | 185 | // Change this if we add update_fn and/or upsert_fn 186 | let res = { 187 | let res1 = env.table1.update(&key, val1).map_or(false, |_| true); 188 | let res2 = env.table2.update(&key, val2).map_or(false, |_| true); 189 | assert_eq!(res1, in_table); 190 | assert_eq!(res2, in_table); 191 | res1 192 | }; 193 | if res { 194 | assert_eq!(Some(val1), env.table1.get(&key).unwrap().value()); 195 | assert_eq!(Some(val2), env.table2.get(&key).unwrap().value()); 196 | env.vals1[idx].store(val1, Ordering::Relaxed); 197 | env.vals2[idx].store(val2, Ordering::Relaxed); 198 | env.num_updates.fetch_add(2, Ordering::Relaxed); 199 | } 200 | 201 | env.in_use[idx].swap(false, Ordering::SeqCst); 202 | } 203 | } 204 | } 205 | 206 | #[test] 207 | #[cfg_attr(miri, ignore)] 208 | fn stress_test() { 209 | let root = Arc::new(Environment::new()); 210 | let mut threads = Vec::new(); 211 | for _ in 0..NUM_THREADS { 212 | let env = Arc::clone(&root); 213 | threads.push(thread::spawn(move || stress_insert_thread(env))); 214 | let env = Arc::clone(&root); 215 | threads.push(thread::spawn(move || stress_delete_thread(env))); 216 | let env = Arc::clone(&root); 217 | threads.push(thread::spawn(move || stress_find_thread(env))); 218 | let env = Arc::clone(&root); 219 | threads.push(thread::spawn(move || stress_update_thread(env))); 220 | } 221 | thread::sleep(std::time::Duration::from_millis(TEST_LEN)); 222 | root.finished.swap(true, Ordering::SeqCst); 223 | 224 | for t in threads { 225 | t.join().expect("failed to join thread"); 226 | } 227 | let in_table = &*root.in_table; 228 | let num_filled = in_table 229 | .iter() 230 | .filter(|b| b.load(Ordering::Relaxed)) 231 | .count(); 232 | assert_eq!(num_filled, root.table1.len()); 233 | assert_eq!(num_filled, root.table2.len()); 234 | } 235 | -------------------------------------------------------------------------------- /tests/hashmap.rs: -------------------------------------------------------------------------------- 1 | use leapfrog::hashmap::*; 2 | use rand::{thread_rng, Rng}; 3 | use std::collections::BTreeMap; 4 | 5 | const KEYS_TO_INSERT: usize = 2048; 6 | 7 | #[test] 8 | fn hashmap_creation() { 9 | const ELEMENTS: usize = 8; 10 | let map = HashMap::::with_capacity(ELEMENTS); 11 | 12 | assert!(map.capacity() >= ELEMENTS); 13 | } 14 | 15 | #[test] 16 | fn hashmap_insert() { 17 | let mut map = HashMap::::with_capacity(KEYS_TO_INSERT); 18 | 19 | let mut rng = thread_rng(); 20 | let start_index: u32 = rng.gen(); 21 | let value: u32 = rng.gen(); 22 | let relative_prime: u64 = value as u64 * 2 + 1; 23 | 24 | let mut inserted: usize = 0; 25 | let mut index = start_index; 26 | let mut insert_checksum = 0u64; 27 | while inserted < KEYS_TO_INSERT { 28 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 29 | key = key ^ (key >> 16); 30 | 31 | // Don't add keys which are 0 or 1 32 | if key >= 2 { 33 | // Map is empty, we should only have None here: 34 | if let Some(_old) = map.insert(key, key) { 35 | panic!("HashMap value found which should not be present"); 36 | } 37 | inserted += 1; 38 | insert_checksum = insert_checksum.wrapping_add(key); 39 | } 40 | index += 1; 41 | } 42 | 43 | let mut removed: usize = 0; 44 | let mut index = start_index; 45 | let mut remove_checksum = 0u64; 46 | while removed < KEYS_TO_INSERT { 47 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 48 | key = key ^ (key >> 16); 49 | 50 | if key >= 2 { 51 | if let Some(value) = map.get(&key) { 52 | assert!(*value == key); 53 | remove_checksum = remove_checksum.wrapping_add(key); 54 | removed += 1; 55 | } else { 56 | panic!("Value should be found"); 57 | } 58 | 59 | // Check get mut as well 60 | if let Some(value) = map.get_mut(&key) { 61 | assert!(*value == key); 62 | } else { 63 | panic!("Value should be found"); 64 | } 65 | } 66 | index += 1; 67 | } 68 | 69 | assert_eq!(insert_checksum, remove_checksum); 70 | assert_eq!(inserted, removed); 71 | } 72 | 73 | #[test] 74 | fn hashmap_clone() { 75 | let mut map = HashMap::::with_capacity(KEYS_TO_INSERT); 76 | 77 | let mut rng = thread_rng(); 78 | let start_index: u32 = rng.gen(); 79 | let value: u32 = rng.gen(); 80 | let relative_prime: u64 = value as u64 * 2 + 1; 81 | 82 | let mut inserted: usize = 0; 83 | let mut index = start_index; 84 | let mut insert_checksum = 0u64; 85 | while inserted < KEYS_TO_INSERT { 86 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 87 | key = key ^ (key >> 16); 88 | 89 | // Don't add keys which are 0 or 1 90 | if key >= 2 { 91 | // Map is empty, we should only have None here: 92 | if let Some(_old) = map.insert(key, key) { 93 | panic!("HashMap value found which should not be present"); 94 | } 95 | inserted += 1; 96 | insert_checksum = insert_checksum.wrapping_add(key); 97 | } 98 | index += 1; 99 | } 100 | 101 | let mut removed: usize = 0; 102 | let mut index = start_index; 103 | let mut remove_checksum = 0u64; 104 | let mut cloned_map = map.clone(); 105 | while removed < KEYS_TO_INSERT { 106 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 107 | key = key ^ (key >> 16); 108 | 109 | if key >= 2 { 110 | if let Some(value) = cloned_map.get(&key) { 111 | assert!(*value == key); 112 | remove_checksum = remove_checksum.wrapping_add(key); 113 | removed += 1; 114 | } else { 115 | panic!("Value should be found"); 116 | } 117 | 118 | // Check get mut as well 119 | if let Some(value) = cloned_map.get_mut(&key) { 120 | assert!(*value == key); 121 | } else { 122 | panic!("Value should be found"); 123 | } 124 | } 125 | index += 1; 126 | } 127 | 128 | assert_eq!(insert_checksum, remove_checksum); 129 | assert_eq!(inserted, removed); 130 | } 131 | 132 | fn generate_kvs(keys: usize) -> BTreeMap { 133 | let mut map = BTreeMap::new(); 134 | 135 | let mut rng = thread_rng(); 136 | let start_index: u32 = rng.gen(); 137 | let value: u32 = rng.gen(); 138 | let relative_prime: u64 = value as u64 * 2 + 1; 139 | 140 | let mut index = start_index; 141 | for _ in 0..keys { 142 | let mut key: u64 = (index as u64).wrapping_mul(relative_prime); 143 | key = key ^ (key >> 16); 144 | map.insert(key, key + 1); 145 | 146 | index += 1; 147 | } 148 | 149 | map 150 | } 151 | 152 | #[test] 153 | fn hashmap_into_iter() { 154 | const KEYS: usize = 150; 155 | let mut map = HashMap::new(); 156 | let kv_map = generate_kvs(KEYS); 157 | 158 | for (k, v) in kv_map.iter() { 159 | let val = *v; 160 | let key = *k; 161 | map.insert(key, val); 162 | } 163 | 164 | assert_eq!(map.len(), KEYS); 165 | 166 | let mut count = 0usize; 167 | for (k, v) in map.into_iter() { 168 | if let Some(val) = kv_map.get(&k) { 169 | assert_eq!(v, *val); 170 | } else { 171 | panic!("HashMap value is incorrect"); 172 | } 173 | count += 1; 174 | } 175 | 176 | assert_eq!(count, KEYS); 177 | } 178 | 179 | #[test] 180 | fn hashmap_iter() { 181 | const KEYS: usize = 150; 182 | let mut map = HashMap::new(); 183 | let kv_map = generate_kvs(KEYS); 184 | 185 | for (k, v) in kv_map.iter() { 186 | let val = *v; 187 | let key = *k; 188 | map.insert(key, val); 189 | } 190 | 191 | assert_eq!(map.len(), KEYS); 192 | 193 | let mut count = 0usize; 194 | for (k, v) in map.iter() { 195 | if let Some(val) = kv_map.get(k) { 196 | assert_eq!(*v, *val); 197 | } else { 198 | panic!("HashMap value is incorrect"); 199 | } 200 | count += 1; 201 | } 202 | 203 | assert_eq!(count, KEYS); 204 | 205 | for k in kv_map.keys() { 206 | map.remove(k); 207 | } 208 | 209 | assert!(map.is_empty()); 210 | assert!(map.iter().next().is_none()); 211 | } 212 | 213 | #[test] 214 | fn hashmap_iter_mut() { 215 | const KEYS: usize = 150; 216 | let mut map = HashMap::new(); 217 | let kv_map = generate_kvs(KEYS); 218 | 219 | for (k, v) in kv_map.iter() { 220 | let val = *v; 221 | let key = *k; 222 | map.insert(key, val); 223 | } 224 | 225 | assert_eq!(map.len(), KEYS); 226 | 227 | map.iter_mut().for_each(|(_k, v)| *v += 1); 228 | 229 | let mut count = 0usize; 230 | for (k, v) in map.iter() { 231 | if let Some(val) = kv_map.get(k) { 232 | assert_eq!(*v, *val + 1); 233 | } else { 234 | panic!("HashMap value is incorrect"); 235 | } 236 | count += 1; 237 | } 238 | 239 | assert_eq!(count, KEYS); 240 | 241 | for k in kv_map.keys() { 242 | map.remove(k); 243 | } 244 | 245 | assert!(map.is_empty()); 246 | assert!(map.iter_mut().next().is_none()); 247 | } 248 | --------------------------------------------------------------------------------