Compare commits

...

34 Commits

Author SHA1 Message Date
Joel Wejdenstål 626b98dab3
v5.5.3 2023-08-29 23:13:21 +02:00
Joel 1e7efe5843
bump msrv to 1.65 (#279) 2023-08-29 23:12:11 +02:00
Joel Wejdenstål eff796d46b
v5.5.2 2023-08-29 13:17:51 +02:00
Joel Wejdenstål febc45dc62
Revert "Only hash keys once, taking advantage of hashbrown raw entry api (#259)"
This reverts commit 3448b6f070.
2023-08-29 13:17:03 +02:00
Joel Wejdenstål dfb7b9af33
v5.5.1 2023-08-22 02:06:11 +02:00
Joel Wejdenstål 3b26d70d2f
commit lockfile 2023-08-22 02:05:50 +02:00
Joel Wejdenstål 71a1dc6cd8
rename toolchain file 2023-08-22 02:01:56 +02:00
Joel Wejdenstål 8364d3e48e
fix lint 2023-08-22 02:00:33 +02:00
Arthur Silva 3448b6f070
Only hash keys once, taking advantage of hashbrown raw entry api (#259) 2023-08-22 01:57:35 +02:00
Joel Wejdenstål ea4ea24a63
v5.5.0 2023-07-11 06:11:45 +02:00
Joel 018f0a1671
assert that the shard count is not 1 (#273)
* assert that the shard count is not 1

* lint
2023-07-11 05:39:27 +02:00
Joel Wejdenstål 6616a8d28c
fmt 2023-07-11 05:30:00 +02:00
Tobias Kahlert 3b418466ed
Entry insert (#226)
* added Entry::insert_entry

* added Entry::insert
2023-07-11 05:29:24 +02:00
Amin Yahyaabadi 079faeb02b
feat: implement Display and AsRef for MappedRef (#261) 2023-07-11 05:24:05 +02:00
Jan dcc97f8ddb
Implement `Serialize` for reference types (#264) 2023-07-11 05:21:26 +02:00
Joel 4c7568cce6
add support for arbitrary (#272) 2023-07-11 05:20:05 +02:00
Joel d36311e634
update deps & toolchain to stable-1.64 (#271) 2023-07-11 04:56:54 +02:00
Joel 7f9522c528
fmt (#251) 2023-01-20 08:08:52 +01:00
Arash Sahebolamri 8a0149d31b
Add `shards_mut()` and `into_shards()` to `DashMap` and `shards()` to `ReadOnlyView` (#237) 2023-01-20 08:04:58 +01:00
George 83a2cf46e0
Added optional feature to enable inline in `hashbrown` crate. (#249) 2023-01-20 08:02:09 +01:00
Fointard 0b2a2269b2
fix: typo in lib.rs (#229) 2022-10-09 13:57:57 +02:00
Joel Wejdenstål 4fd07038a1
v5.4.0 2022-08-30 20:12:48 +02:00
Joel Wejdenstål f27dba3062
fmt 2022-08-30 20:09:31 +02:00
Joel Wejdenstål 342011296c
upgrade deps 2022-08-30 20:09:09 +02:00
Tom Karwowski d1f58eaf48
feat: add ReadOnlyView::par_iter() and ReadOnlyView::into_par_iter() (#223) 2022-08-30 20:08:29 +02:00
Tom Karwowski 9deeb37ccb
feat: memoize default shard amount (#222)
Use once_cell crate to memoize the value.
This doesn't add any new dependencies, as once_cell was already a subdependency. Additionally it was merged into std in nighlty and awaiting stabilization.
2022-08-30 20:05:07 +02:00
Tom Karwowski 030671060f
feat: add try_reserve method (#221) 2022-08-30 20:04:31 +02:00
Wolf Thomsen 459db7ac6f
Fix small typo in lib.rs module docstring (#209) 2022-06-06 14:09:52 +02:00
Joel dd7164dfef
Delete table.rs 2022-06-06 14:07:11 +02:00
Joel Wejdenstål 4b63e98814
Release v5.3.4 2022-05-28 13:20:30 +02:00
Joel 3236196d01
Use a proper lock implementation (#214)
* use a proper but reentrant safe lock impl

* impl downgrade
2022-05-28 13:19:37 +02:00
Konrad Borowski 5fbb68f188
Set rust-version to 1.59 (#210)
This crate uses available_parallelism which was introduced
in Rust 1.59.
2022-05-28 12:58:00 +02:00
Joel Wejdenstål 270612e3f9
Release v5.3.3 2022-05-02 17:55:25 +02:00
Joel Wejdenstål c162b21b6c
Revert "Hash keys once (#194)"
This reverts commit 3d315aa7a7.
2022-05-02 17:55:05 +02:00
18 changed files with 1075 additions and 159 deletions

View File

@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: style
@ -13,7 +13,7 @@ jobs:
run: cargo clippy --all-targets --all-features -- -D warnings
test:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
strategy:
matrix:
target: [

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
/target
**/*.rs.bk
Cargo.lock

311
Cargo.lock generated Normal file
View File

@ -0,0 +1,311 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "arbitrary"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "dashmap"
version = "5.5.3"
dependencies = [
"arbitrary",
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
"rayon",
"serde",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "syn"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View File

@ -1,8 +1,9 @@
[package]
name = "dashmap"
version = "5.3.2"
version = "5.5.3"
authors = ["Acrimon <joel.wejdenstal@gmail.com>"]
edition = "2018"
rust-version = "1.65"
license = "MIT"
repository = "https://github.com/xacrimon/dashmap"
homepage = "https://github.com/xacrimon/dashmap"
@ -14,13 +15,17 @@ categories = ["concurrency", "algorithms", "data-structures"]
[features]
raw-api = []
inline = ["hashbrown/inline-more"]
[dependencies]
lock_api = "0.4.7"
hashbrown = { version = "0.12.0", default-features = false }
serde = { version = "1.0.136", optional = true, features = ["derive"] }
lock_api = "0.4.10"
parking_lot_core = "0.9.8"
hashbrown = { version = "0.14.0", default-features = false }
serde = { version = "1.0.188", optional = true, features = ["derive"] }
cfg-if = "1.0.0"
rayon = { version = "1.5.2", optional = true }
rayon = { version = "1.7.0", optional = true }
once_cell = "1.18.0"
arbitrary = { version = "1.3.0", optional = true }
[package.metadata.docs.rs]
features = ["rayon", "raw-api", "serde"]

View File

@ -20,7 +20,7 @@ If you have any suggestions or tips do not hesitate to open an issue or a PR.
[![downloads](https://img.shields.io/crates/d/dashmap)](https://crates.io/crates/dashmap)
[![minimum rustc version](https://img.shields.io/badge/rustc-1.59-orange.svg)](https://crates.io/crates/dashmap)
[![minimum rustc version](https://img.shields.io/badge/rustc-1.65-orange.svg)](https://crates.io/crates/dashmap)
## Cargo features
@ -30,6 +30,10 @@ If you have any suggestions or tips do not hesitate to open an issue or a PR.
- `rayon` - Enables rayon support.
- `inline` - Enables `inline-more` feature from the `hashbrown` crate. Can lead to better performance, but with the cost of longer compile-time.
- `arbitrary` - Enables support for the `arbitrary` crate.
## Contributing
DashMap gladly accepts contributions!

View File

@ -1,4 +0,0 @@
[toolchain]
channel = "1.59"
components = [ "rustfmt", "clippy" ]
profile = "minimal"

4
rust-toolchain.toml Normal file
View File

@ -0,0 +1,4 @@
[toolchain]
channel = "1.65"
components = ["rustfmt", "clippy"]
profile = "minimal"

13
src/arbitrary.rs Normal file
View File

@ -0,0 +1,13 @@
use arbitrary::{Arbitrary, Unstructured};
use core::hash::BuildHasher;
impl<'a, K, V, S> Arbitrary<'a> for crate::DashMap<K, V, S>
where
K: Eq + std::hash::Hash + Arbitrary<'a>,
V: Arbitrary<'a>,
S: Default + BuildHasher + Clone,
{
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary_iter()?.collect()
}
}

View File

@ -6,6 +6,7 @@ use crate::util::SharedValue;
use crate::{DashMap, HashMap};
use core::hash::{BuildHasher, Hash};
use core::mem;
use hashbrown::hash_map;
use std::collections::hash_map::RandomState;
use std::sync::Arc;
@ -38,7 +39,7 @@ impl<K: Eq + Hash, V, S: BuildHasher + Clone> OwningIter<K, V, S> {
}
}
type GuardOwningIter<K, V> = hashbrown::hash_map::IntoIter<K, SharedValue<V>>;
type GuardOwningIter<K, V> = hash_map::IntoIter<K, SharedValue<V>>;
impl<K: Eq + Hash, V, S: BuildHasher + Clone> Iterator for OwningIter<K, V, S> {
type Item = (K, V);
@ -92,12 +93,12 @@ where
type GuardIter<'a, K, V, S> = (
Arc<RwLockReadGuard<'a, HashMap<K, V, S>>>,
hashbrown::hash_map::Iter<'a, K, SharedValue<V>>,
hash_map::Iter<'a, K, SharedValue<V>>,
);
type GuardIterMut<'a, K, V, S> = (
Arc<RwLockWriteGuard<'a, HashMap<K, V, S>>>,
hashbrown::hash_map::IterMut<'a, K, SharedValue<V>>,
hash_map::IterMut<'a, K, SharedValue<V>>,
);
/// Iterator over a DashMap yielding immutable references.
@ -117,7 +118,7 @@ pub struct Iter<'a, K, V, S = RandomState, M = DashMap<K, V, S>> {
current: Option<GuardIter<'a, K, V, S>>,
}
impl<'a, 'i, K: Clone + Hash + Eq, V: Clone, S: Clone + BuildHasher> Clone for Iter<'i, K, V, S> {
impl<'i, K: Clone + Hash + Eq, V: Clone, S: Clone + BuildHasher> Clone for Iter<'i, K, V, S> {
fn clone(&self) -> Self {
Iter::new(self.map)
}

View File

@ -1,5 +1,7 @@
#![allow(clippy::type_complexity)]
#[cfg(feature = "arbitrary")]
mod arbitrary;
pub mod iter;
pub mod iter_set;
mod lock;
@ -16,6 +18,7 @@ mod util;
#[cfg(feature = "rayon")]
pub mod rayon {
pub mod map;
pub mod read_only;
pub mod set;
}
@ -35,6 +38,7 @@ use iter::{Iter, IterMut, OwningIter};
use mapref::entry::{Entry, OccupiedEntry, VacantEntry};
use mapref::multiple::RefMulti;
use mapref::one::{Ref, RefMut};
use once_cell::sync::OnceCell;
pub use read_only::ReadOnlyView;
pub use set::DashSet;
use std::collections::hash_map::RandomState;
@ -51,8 +55,19 @@ cfg_if! {
pub(crate) type HashMap<K, V, S> = hashbrown::HashMap<K, SharedValue<V>, S>;
// Temporary reimplementation of [`std::collections::TryReserveError`]
// util [`std::collections::TryReserveError`] stabilises.
// We cannot easily create `std::collections` error type from `hashbrown` error type
// without access to `TryReserveError::kind` method.
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TryReserveError {}
fn default_shard_amount() -> usize {
(std::thread::available_parallelism().map_or(1, usize::from) * 4).next_power_of_two()
static DEFAULT_SHARD_AMOUNT: OnceCell<usize> = OnceCell::new();
*DEFAULT_SHARD_AMOUNT.get_or_init(|| {
(std::thread::available_parallelism().map_or(1, usize::from) * 4).next_power_of_two()
})
}
fn ncb(shard_amount: usize) -> usize {
@ -65,7 +80,7 @@ fn ncb(shard_amount: usize) -> usize {
/// with some slight changes to handle concurrency.
///
/// DashMap tries to be very simple to use and to be a direct replacement for `RwLock<HashMap<K, V, S>>`.
/// To accomplish these all methods take `&self` instead modifying methods taking `&mut self`.
/// To accomplish this, all methods take `&self` instead of modifying methods taking `&mut self`.
/// This allows you to put a DashMap in an `Arc<T>` and share it between threads while being able to modify it.
///
/// Documentation mentioning locking behaviour acts in the reference frame of the calling thread.
@ -216,7 +231,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// Creates a new DashMap with a specified hasher and shard amount
///
/// shard_amount should greater than 0 and be a power of two.
/// shard_amount should be greater than 0 and a power of two.
/// If a shard_amount which is not a power of two is provided, the function will panic.
///
/// # Examples
@ -255,7 +270,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
hasher: S,
shard_amount: usize,
) -> Self {
assert!(shard_amount > 0);
assert!(shard_amount > 1);
assert!(shard_amount.is_power_of_two());
let shift = util::ptr_size_bits() - ncb(shard_amount);
@ -277,18 +292,14 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
}
}
fn hash_u64<T: Hash>(&self, item: &T) -> u64 {
/// Hash a given item to produce a usize.
/// Uses the provided or default HashBuilder.
pub fn hash_usize<T: Hash>(&self, item: &T) -> usize {
let mut hasher = self.hasher.build_hasher();
item.hash(&mut hasher);
hasher.finish()
}
/// Hash a given item to produce a usize.
/// Uses the provided or default HashBuilder.
pub fn hash_usize<T: Hash>(&self, item: &T) -> usize {
self.hash_u64(item) as usize
hasher.finish() as usize
}
cfg_if! {
@ -309,11 +320,51 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.shards
}
/// Provides mutable access to the inner shards that store your data.
/// You should probably not use this unless you know what you are doing.
///
/// Requires the `raw-api` feature to be enabled.
///
/// # Examples
///
/// ```
/// use dashmap::DashMap;
/// use dashmap::SharedValue;
///
/// let mut map = DashMap::<i32, &'static str>::new();
/// let shard_ind = map.determine_map(&42);
/// map.shards_mut()[shard_ind].get_mut().insert(42, SharedValue::new("forty two"));
/// assert_eq!(*map.get(&42).unwrap(), "forty two");
/// ```
pub fn shards_mut(&mut self) -> &mut [RwLock<HashMap<K, V, S>>] {
&mut self.shards
}
/// Consumes this `DashMap` and returns the inner shards.
/// You should probably not use this unless you know what you are doing.
///
/// Requires the `raw-api` feature to be enabled.
///
/// See [`DashMap::shards()`] and [`DashMap::shards_mut()`] for more information.
pub fn into_shards(self) -> Box<[RwLock<HashMap<K, V, S>>]> {
self.shards
}
} else {
#[allow(dead_code)]
pub(crate) fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.shards
}
#[allow(dead_code)]
pub(crate) fn shards_mut(&mut self) -> &mut [RwLock<HashMap<K, V, S>>] {
&mut self.shards
}
#[allow(dead_code)]
pub(crate) fn into_shards(self) -> Box<[RwLock<HashMap<K, V, S>>]> {
self.shards
}
}
}
@ -339,8 +390,8 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
self.determine_shard(hash as usize)
let hash = self.hash_usize(&key);
self.determine_shard(hash)
}
}
}
@ -501,7 +552,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
self._iter_mut()
}
/// Get a immutable reference to an entry in the map
/// Get an immutable reference to an entry in the map
///
/// **Locking behaviour:** May deadlock if called when holding a mutable reference into the map.
///
@ -801,6 +852,24 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
pub fn try_entry(&'a self, key: K) -> Option<Entry<'a, K, V, S>> {
self._try_entry(key)
}
/// Advanced entry API that tries to mimic `std::collections::HashMap::try_reserve`.
/// Tries to reserve capacity for at least `shard * additional`
/// and may reserve more space to avoid frequent reallocations.
///
/// # Errors
///
/// If the capacity overflows, or the allocator reports a failure, then an error is returned.
// TODO: return std::collections::TryReserveError once std::collections::TryReserveErrorKind stabilises.
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
for shard in self.shards.iter() {
shard
.write()
.try_reserve(additional)
.map_err(|_| TryReserveError {})?;
}
Ok(())
}
}
impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
@ -847,21 +916,15 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
}
fn _insert(&self, key: K, value: V) -> Option<V> {
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let mut shard = unsafe { self._yield_write_shard(idx) };
match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &key) {
hashbrown::hash_map::RawEntryMut::Occupied(mut occupied) => {
Some(occupied.insert(SharedValue::new(value)).into_inner())
}
hashbrown::hash_map::RawEntryMut::Vacant(vacant) => {
vacant.insert(key, SharedValue::new(value));
None
}
}
shard
.insert(key, SharedValue::new(value))
.map(|v| v.into_inner())
}
fn _remove<Q>(&self, key: &Q) -> Option<(K, V)>
@ -869,19 +932,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let mut shard = unsafe { self._yield_write_shard(idx) };
match shard.raw_entry_mut().from_key_hashed_nocheck(hash, key) {
hashbrown::hash_map::RawEntryMut::Occupied(entry) => {
let (k, v) = entry.remove_entry();
Some((k, v.into_inner()))
}
hashbrown::hash_map::RawEntryMut::Vacant(_) => None,
}
shard.remove_entry(key).map(|(k, v)| (k, v.into_inner()))
}
fn _remove_if<Q>(&self, key: &Q, f: impl FnOnce(&K, &V) -> bool) -> Option<(K, V)>
@ -889,22 +946,25 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let mut shard = unsafe { self._yield_write_shard(idx) };
match shard.raw_entry_mut().from_key_hashed_nocheck(hash, key) {
hashbrown::hash_map::RawEntryMut::Occupied(occupied) => {
if f(occupied.key(), occupied.get().get()) {
let (k, v) = occupied.remove_entry();
Some((k, v.into_inner()))
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
if f(&*kptr, &mut *vptr) {
shard.remove_entry(key).map(|(k, v)| (k, v.into_inner()))
} else {
None
}
}
hashbrown::hash_map::RawEntryMut::Vacant(_) => None,
} else {
None
}
}
@ -913,23 +973,25 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let mut shard = unsafe { self._yield_write_shard(idx) };
match shard.raw_entry_mut().from_key_hashed_nocheck(hash, key) {
hashbrown::hash_map::RawEntryMut::Occupied(mut occupied) => {
let (k, v) = occupied.get_key_value_mut();
if f(k, v.get_mut()) {
let (k, v) = occupied.remove_entry();
Some((k, v.into_inner()))
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
if f(&*kptr, &mut *vptr) {
shard.remove_entry(key).map(|(k, v)| (k, v.into_inner()))
} else {
None
}
}
hashbrown::hash_map::RawEntryMut::Vacant(_) => None,
} else {
None
}
}
@ -946,13 +1008,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = unsafe { self._yield_read_shard(idx) };
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, key) {
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *const V = vptr.get();
@ -968,13 +1030,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = unsafe { self._yield_write_shard(idx) };
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, key) {
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
@ -990,16 +1052,16 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = match unsafe { self._try_yield_read_shard(idx) } {
Some(shard) => shard,
None => return TryResult::Locked,
};
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, key) {
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *const V = vptr.get();
@ -1015,16 +1077,16 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = match unsafe { self._try_yield_write_shard(idx) } {
Some(shard) => shard,
None => return TryResult::Locked,
};
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, key) {
if let Some((kptr, vptr)) = shard.get_key_value(key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
@ -1083,13 +1145,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
}
fn _entry(&'a self, key: K) -> Entry<'a, K, V, S> {
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = unsafe { self._yield_write_shard(idx) };
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, &key) {
if let Some((kptr, vptr)) = shard.get_key_value(&key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
@ -1101,16 +1163,16 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
}
fn _try_entry(&'a self, key: K) -> Option<Entry<'a, K, V, S>> {
let hash = self.hash_u64(&key);
let hash = self.hash_usize(&key);
let idx = self.determine_shard(hash as usize);
let idx = self.determine_shard(hash);
let shard = match unsafe { self._try_yield_write_shard(idx) } {
Some(shard) => shard,
None => return None,
};
if let Some((kptr, vptr)) = shard.raw_entry().from_key_hashed_nocheck(hash, &key) {
if let Some((kptr, vptr)) = shard.get_key_value(&key) {
unsafe {
let kptr: *const K = kptr;
let vptr: *mut V = vptr.as_ptr();
@ -1203,7 +1265,7 @@ where
}
}
impl<'a, K: Eq + Hash, V, S: BuildHasher + Clone> IntoIterator for DashMap<K, V, S> {
impl<K: Eq + Hash, V, S: BuildHasher + Clone> IntoIterator for DashMap<K, V, S> {
type Item = (K, V);
type IntoIter = OwningIter<K, V, S>;
@ -1378,4 +1440,26 @@ mod tests {
assert!(result2.is_locked());
}
}
#[test]
fn test_try_reserve() {
let mut map: DashMap<i32, i32> = DashMap::new();
// DashMap is empty and doesn't allocate memory
assert_eq!(map.capacity(), 0);
map.try_reserve(10).unwrap();
// And now map can hold at least 10 elements
assert!(map.capacity() >= 10);
}
#[test]
fn test_try_reserve_errors() {
let mut map: DashMap<i32, i32> = DashMap::new();
match map.try_reserve(usize::MAX) {
Err(_) => {}
_ => panic!("should have raised CapacityOverflow error"),
}
}
}

View File

@ -1,76 +1,300 @@
use lock_api::GuardSend;
use std::hint;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
const USIZE_BITS: usize = mem::size_of::<usize>() * 8;
const EXCLUSIVE_BIT: usize = 1 << (USIZE_BITS - 1);
use core::sync::atomic::{AtomicUsize, Ordering};
use parking_lot_core::{ParkToken, SpinWait, UnparkToken};
pub type RwLock<T> = lock_api::RwLock<RawRwLock, T>;
pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>;
pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>;
const READERS_PARKED: usize = 0b0001;
const WRITERS_PARKED: usize = 0b0010;
const ONE_READER: usize = 0b0100;
const ONE_WRITER: usize = !(READERS_PARKED | WRITERS_PARKED);
pub struct RawRwLock {
data: AtomicUsize,
state: AtomicUsize,
}
unsafe impl lock_api::RawRwLock for RawRwLock {
type GuardMarker = GuardSend;
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = RawRwLock {
data: AtomicUsize::new(0),
const INIT: Self = Self {
state: AtomicUsize::new(0),
};
fn lock_shared(&self) {
while !self.try_lock_shared() {
hint::spin_loop();
}
}
fn try_lock_shared(&self) -> bool {
let x = self.data.load(Ordering::Acquire);
if x & EXCLUSIVE_BIT != 0 {
return false;
}
let y = x + 1;
self.data
.compare_exchange(x, y, Ordering::Release, Ordering::Relaxed)
.is_ok()
}
unsafe fn unlock_shared(&self) {
self.data.fetch_sub(1, Ordering::Release);
}
fn lock_exclusive(&self) {
while !self.try_lock_exclusive() {
hint::spin_loop();
}
}
type GuardMarker = lock_api::GuardNoSend;
#[inline]
fn try_lock_exclusive(&self) -> bool {
self.data
.compare_exchange(0, EXCLUSIVE_BIT, Ordering::Release, Ordering::Relaxed)
self.state
.compare_exchange(0, ONE_WRITER, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
#[inline]
fn lock_exclusive(&self) {
if self
.state
.compare_exchange_weak(0, ONE_WRITER, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
self.lock_exclusive_slow();
}
}
#[inline]
unsafe fn unlock_exclusive(&self) {
self.data.store(0, Ordering::Release)
if self
.state
.compare_exchange(ONE_WRITER, 0, Ordering::Release, Ordering::Relaxed)
.is_err()
{
self.unlock_exclusive_slow();
}
}
fn is_locked(&self) -> bool {
self.data.load(Ordering::Acquire) != 0
#[inline]
fn try_lock_shared(&self) -> bool {
self.try_lock_shared_fast() || self.try_lock_shared_slow()
}
fn is_locked_exclusive(&self) -> bool {
self.data.load(Ordering::Acquire) & EXCLUSIVE_BIT != 0
#[inline]
fn lock_shared(&self) {
if !self.try_lock_shared_fast() {
self.lock_shared_slow();
}
}
#[inline]
unsafe fn unlock_shared(&self) {
let state = self.state.fetch_sub(ONE_READER, Ordering::Release);
if state == (ONE_READER | WRITERS_PARKED) {
self.unlock_shared_slow();
}
}
}
unsafe impl lock_api::RawRwLockDowngrade for RawRwLock {
#[inline]
unsafe fn downgrade(&self) {
self.data.store(1, Ordering::SeqCst);
let state = self
.state
.fetch_and(ONE_READER | WRITERS_PARKED, Ordering::Release);
if state & READERS_PARKED != 0 {
parking_lot_core::unpark_all((self as *const _ as usize) + 1, UnparkToken(0));
}
}
}
impl RawRwLock {
#[cold]
fn lock_exclusive_slow(&self) {
let mut acquire_with = 0;
loop {
let mut spin = SpinWait::new();
let mut state = self.state.load(Ordering::Relaxed);
loop {
while state & ONE_WRITER == 0 {
match self.state.compare_exchange_weak(
state,
state | ONE_WRITER | acquire_with,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return,
Err(e) => state = e,
}
}
if state & WRITERS_PARKED == 0 {
if spin.spin() {
state = self.state.load(Ordering::Relaxed);
continue;
}
if let Err(e) = self.state.compare_exchange_weak(
state,
state | WRITERS_PARKED,
Ordering::Relaxed,
Ordering::Relaxed,
) {
state = e;
continue;
}
}
let _ = unsafe {
parking_lot_core::park(
self as *const _ as usize,
|| {
let state = self.state.load(Ordering::Relaxed);
(state & ONE_WRITER != 0) && (state & WRITERS_PARKED != 0)
},
|| {},
|_, _| {},
ParkToken(0),
None,
)
};
acquire_with = WRITERS_PARKED;
break;
}
}
}
#[cold]
fn unlock_exclusive_slow(&self) {
let state = self.state.load(Ordering::Relaxed);
assert_eq!(state & ONE_WRITER, ONE_WRITER);
let mut parked = state & (READERS_PARKED | WRITERS_PARKED);
assert_ne!(parked, 0);
if parked != (READERS_PARKED | WRITERS_PARKED) {
if let Err(new_state) =
self.state
.compare_exchange(state, 0, Ordering::Release, Ordering::Relaxed)
{
assert_eq!(new_state, ONE_WRITER | READERS_PARKED | WRITERS_PARKED);
parked = READERS_PARKED | WRITERS_PARKED;
}
}
if parked == (READERS_PARKED | WRITERS_PARKED) {
self.state.store(WRITERS_PARKED, Ordering::Release);
parked = READERS_PARKED;
}
if parked == READERS_PARKED {
return unsafe {
parking_lot_core::unpark_all((self as *const _ as usize) + 1, UnparkToken(0));
};
}
assert_eq!(parked, WRITERS_PARKED);
unsafe {
parking_lot_core::unpark_one(self as *const _ as usize, |_| UnparkToken(0));
}
}
#[inline(always)]
fn try_lock_shared_fast(&self) -> bool {
let state = self.state.load(Ordering::Relaxed);
if let Some(new_state) = state.checked_add(ONE_READER) {
if new_state & ONE_WRITER != ONE_WRITER {
return self
.state
.compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed)
.is_ok();
}
}
false
}
#[cold]
fn try_lock_shared_slow(&self) -> bool {
let mut state = self.state.load(Ordering::Relaxed);
while let Some(new_state) = state.checked_add(ONE_READER) {
if new_state & ONE_WRITER == ONE_WRITER {
break;
}
match self.state.compare_exchange_weak(
state,
new_state,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return true,
Err(e) => state = e,
}
}
false
}
#[cold]
fn lock_shared_slow(&self) {
loop {
let mut spin = SpinWait::new();
let mut state = self.state.load(Ordering::Relaxed);
loop {
let mut backoff = SpinWait::new();
while let Some(new_state) = state.checked_add(ONE_READER) {
assert_ne!(
new_state & ONE_WRITER,
ONE_WRITER,
"reader count overflowed",
);
if self
.state
.compare_exchange_weak(
state,
new_state,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
return;
}
backoff.spin_no_yield();
state = self.state.load(Ordering::Relaxed);
}
if state & READERS_PARKED == 0 {
if spin.spin() {
state = self.state.load(Ordering::Relaxed);
continue;
}
if let Err(e) = self.state.compare_exchange_weak(
state,
state | READERS_PARKED,
Ordering::Relaxed,
Ordering::Relaxed,
) {
state = e;
continue;
}
}
let _ = unsafe {
parking_lot_core::park(
(self as *const _ as usize) + 1,
|| {
let state = self.state.load(Ordering::Relaxed);
(state & ONE_WRITER == ONE_WRITER) && (state & READERS_PARKED != 0)
},
|| {},
|_, _| {},
ParkToken(0),
None,
)
};
break;
}
}
}
#[cold]
fn unlock_shared_slow(&self) {
if self
.state
.compare_exchange(WRITERS_PARKED, 0, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
unsafe {
parking_lot_core::unpark_one(self as *const _ as usize, |_| UnparkToken(0));
}
}
}
}

View File

@ -82,6 +82,36 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
Entry::Vacant(entry) => Ok(entry.insert(value()?)),
}
}
/// Sets the value of the entry, and returns a reference to the inserted value.
pub fn insert(self, value: V) -> RefMut<'a, K, V, S> {
match self {
Entry::Occupied(mut entry) => {
entry.insert(value);
entry.into_ref()
}
Entry::Vacant(entry) => entry.insert(value),
}
}
/// Sets the value of the entry, and returns an OccupiedEntry.
///
/// If you are not interested in the occupied entry,
/// consider [`insert`] as it doesn't need to clone the key.
///
/// [`insert`]: Entry::insert
pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, S>
where
K: Clone,
{
match self {
Entry::Occupied(mut entry) => {
entry.insert(value);
entry
}
Entry::Vacant(entry) => entry.insert_entry(value),
}
}
}
pub struct VacantEntry<'a, K, V, S = RandomState> {
@ -117,6 +147,22 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> VacantEntry<'a, K, V, S> {
}
}
/// Sets the value of the entry with the VacantEntrys key, and returns an OccupiedEntry.
pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, S>
where
K: Clone,
{
unsafe {
self.shard.insert(self.key.clone(), SharedValue::new(value));
let (k, v) = self.shard.get_key_value(&self.key).unwrap();
let kptr: *const K = k;
let vptr: *mut V = v.as_ptr();
OccupiedEntry::new(self.shard, self.key, (kptr, vptr))
}
}
pub fn into_key(self) -> K {
self.key
}
@ -187,3 +233,46 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> OccupiedEntry<'a, K, V, S> {
(k, v.into_inner())
}
}
#[cfg(test)]
mod tests {
use crate::DashMap;
use super::*;
#[test]
fn test_insert_entry_into_vacant() {
let map: DashMap<u32, u32> = DashMap::new();
let entry = map.entry(1);
assert!(matches!(entry, Entry::Vacant(_)));
let entry = entry.insert_entry(2);
assert_eq!(*entry.get(), 2);
drop(entry);
assert_eq!(*map.get(&1).unwrap(), 2);
}
#[test]
fn test_insert_entry_into_occupied() {
let map: DashMap<u32, u32> = DashMap::new();
map.insert(1, 1000);
let entry = map.entry(1);
assert!(matches!(&entry, Entry::Occupied(entry) if *entry.get() == 1000));
let entry = entry.insert_entry(2);
assert_eq!(*entry.get(), 2);
drop(entry);
assert_eq!(*map.get(&1).unwrap(), 2);
}
}

View File

@ -241,6 +241,20 @@ impl<'a, K: Eq + Hash, V, T, S: BuildHasher> Deref for MappedRef<'a, K, V, T, S>
}
}
impl<'a, K: Eq + Hash, V, T: std::fmt::Display> std::fmt::Display for MappedRef<'a, K, V, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.value(), f)
}
}
impl<'a, K: Eq + Hash, V, T: AsRef<TDeref>, TDeref: ?Sized> AsRef<TDeref>
for MappedRef<'a, K, V, T>
{
fn as_ref(&self) -> &TDeref {
self.value().as_ref()
}
}
pub struct MappedRefMut<'a, K, V, T, S = RandomState> {
_guard: RwLockWriteGuard<'a, HashMap<K, V, S>>,
k: *const K,

View File

@ -80,7 +80,7 @@ where
}
pub struct OwningIter<K, V, S = RandomState> {
shards: Box<[RwLock<HashMap<K, V, S>>]>,
pub(super) shards: Box<[RwLock<HashMap<K, V, S>>]>,
}
impl<K, V, S> ParallelIterator for OwningIter<K, V, S>
@ -125,7 +125,7 @@ where
}
pub struct Iter<'a, K, V, S = RandomState> {
shards: &'a [RwLock<HashMap<K, V, S>>],
pub(super) shards: &'a [RwLock<HashMap<K, V, S>>],
}
impl<'a, K, V, S> ParallelIterator for Iter<'a, K, V, S>
@ -173,7 +173,7 @@ where
}
}
impl<'a, K, V, S> DashMap<K, V, S>
impl<K, V, S> DashMap<K, V, S>
where
K: Send + Sync + Eq + Hash,
V: Send + Sync,

96
src/rayon/read_only.rs Normal file
View File

@ -0,0 +1,96 @@
use crate::mapref::multiple::RefMulti;
use crate::rayon::map::Iter;
use crate::ReadOnlyView;
use core::hash::{BuildHasher, Hash};
use rayon::iter::IntoParallelIterator;
impl<K, V, S> IntoParallelIterator for ReadOnlyView<K, V, S>
where
K: Send + Eq + Hash,
V: Send,
S: Send + Clone + BuildHasher,
{
type Iter = super::map::OwningIter<K, V, S>;
type Item = (K, V);
fn into_par_iter(self) -> Self::Iter {
super::map::OwningIter {
shards: self.map.shards,
}
}
}
// This impl also enables `IntoParallelRefIterator::par_iter`
impl<'a, K, V, S> IntoParallelIterator for &'a ReadOnlyView<K, V, S>
where
K: Send + Sync + Eq + Hash,
V: Send + Sync,
S: Send + Sync + Clone + BuildHasher,
{
type Iter = Iter<'a, K, V, S>;
type Item = RefMulti<'a, K, V, S>;
fn into_par_iter(self) -> Self::Iter {
Iter {
shards: &self.map.shards,
}
}
}
#[cfg(test)]
mod tests {
use crate::DashMap;
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
fn construct_sample_map() -> DashMap<i32, String> {
let map = DashMap::new();
map.insert(1, "one".to_string());
map.insert(10, "ten".to_string());
map.insert(27, "twenty seven".to_string());
map.insert(45, "forty five".to_string());
map
}
#[test]
fn test_par_iter() {
let map = construct_sample_map();
let view = map.clone().into_read_only();
view.par_iter().for_each(|entry| {
let key = *entry.key();
assert!(view.contains_key(&key));
let map_entry = map.get(&key).unwrap();
assert_eq!(view.get(&key).unwrap(), map_entry.value());
let key_value: (&i32, &String) = view.get_key_value(&key).unwrap();
assert_eq!(key_value.0, map_entry.key());
assert_eq!(key_value.1, map_entry.value());
});
}
#[test]
fn test_into_par_iter() {
let map = construct_sample_map();
let view = map.clone().into_read_only();
view.into_par_iter().for_each(|(key, value)| {
let map_entry = map.get(&key).unwrap();
assert_eq!(&key, map_entry.key());
assert_eq!(&value, map_entry.value());
});
}
}

View File

@ -1,5 +1,7 @@
use crate::lock::RwLock;
use crate::t::Map;
use crate::{DashMap, HashMap};
use cfg_if::cfg_if;
use core::borrow::Borrow;
use core::fmt;
use core::hash::{BuildHasher, Hash};
@ -7,7 +9,7 @@ use std::collections::hash_map::RandomState;
/// A read-only view into a `DashMap`. Allows to obtain raw references to the stored values.
pub struct ReadOnlyView<K, V, S = RandomState> {
map: DashMap<K, V, S>,
pub(crate) map: DashMap<K, V, S>,
}
impl<K: Eq + Hash + Clone, V: Clone, S: Clone> Clone for ReadOnlyView<K, V, S> {
@ -59,16 +61,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.map.hash_u64(&key);
let hash = self.map.hash_usize(&key);
let idx = self.map.determine_shard(hash as usize);
let idx = self.map.determine_shard(hash);
let shard = unsafe { self.map._get_read_shard(idx) };
shard
.raw_entry()
.from_key_hashed_nocheck(hash, key)
.is_some()
shard.contains_key(key)
}
/// Returns a reference to the value corresponding to the key.
@ -77,16 +76,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.map.hash_u64(&key);
let hash = self.map.hash_usize(&key);
let idx = self.map.determine_shard(hash as usize);
let idx = self.map.determine_shard(hash);
let shard = unsafe { self.map._get_read_shard(idx) };
shard
.raw_entry()
.from_key_hashed_nocheck(hash, key)
.map(|(_k, v)| v.get())
shard.get(key).map(|v| v.get())
}
/// Returns the key-value pair corresponding to the supplied key.
@ -95,16 +91,13 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.map.hash_u64(&key);
let hash = self.map.hash_usize(&key);
let idx = self.map.determine_shard(hash as usize);
let idx = self.map.determine_shard(hash);
let shard = unsafe { self.map._get_read_shard(idx) };
shard
.raw_entry()
.from_key_hashed_nocheck(hash, key)
.map(|(k, v)| (k, v.get()))
shard.get_key_value(key).map(|(k, v)| (k, v.get()))
}
fn shard_read_iter(&'a self) -> impl Iterator<Item = &'a HashMap<K, V, S>> + 'a {
@ -130,6 +123,32 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
.flat_map(|shard| shard.values())
.map(|v| v.get())
}
cfg_if! {
if #[cfg(feature = "raw-api")] {
/// Allows you to peek at the inner shards that store your data.
/// You should probably not use this unless you know what you are doing.
///
/// Requires the `raw-api` feature to be enabled.
///
/// # Examples
///
/// ```
/// use dashmap::DashMap;
///
/// let map = DashMap::<(), ()>::new().into_read_only();
/// println!("Amount of shards: {}", map.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.map.shards
}
} else {
#[allow(dead_code)]
pub(crate) fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.map.shards
}
}
}
}
#[cfg(test)]

View File

@ -1,4 +1,4 @@
use crate::{DashMap, DashSet};
use crate::{mapref, setref, DashMap, DashSet};
use core::fmt;
use core::hash::{BuildHasher, Hash};
use core::marker::PhantomData;
@ -156,3 +156,60 @@ where
seq.end()
}
}
macro_rules! serialize_impl {
() => {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: serde::Serializer,
{
std::ops::Deref::deref(self).serialize(serializer)
}
};
}
// Map
impl<'a, K: Eq + Hash, V: Serialize, S: BuildHasher> Serialize
for mapref::multiple::RefMulti<'a, K, V, S>
{
serialize_impl! {}
}
impl<'a, K: Eq + Hash, V: Serialize, S: BuildHasher> Serialize
for mapref::multiple::RefMutMulti<'a, K, V, S>
{
serialize_impl! {}
}
impl<'a, K: Eq + Hash, V: Serialize, S: BuildHasher> Serialize for mapref::one::Ref<'a, K, V, S> {
serialize_impl! {}
}
impl<'a, K: Eq + Hash, V: Serialize, S: BuildHasher> Serialize
for mapref::one::RefMut<'a, K, V, S>
{
serialize_impl! {}
}
impl<'a, K: Eq + Hash, V, T: Serialize, S: BuildHasher> Serialize
for mapref::one::MappedRef<'a, K, V, T, S>
{
serialize_impl! {}
}
impl<'a, K: Eq + Hash, V, T: Serialize, S: BuildHasher> Serialize
for mapref::one::MappedRefMut<'a, K, V, T, S>
{
serialize_impl! {}
}
// Set
impl<'a, V: Hash + Eq + Serialize, S: BuildHasher> Serialize
for setref::multiple::RefMulti<'a, V, S>
{
serialize_impl! {}
}
impl<'a, V: Hash + Eq + Serialize, S: BuildHasher> Serialize for setref::one::Ref<'a, V, S> {
serialize_impl! {}
}

View File

@ -385,7 +385,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
}
}
impl<'a, K: Eq + Hash, S: BuildHasher + Clone> IntoIterator for DashSet<K, S> {
impl<K: Eq + Hash, S: BuildHasher + Clone> IntoIterator for DashSet<K, S> {
type Item = K;
type IntoIter = OwningIter<K, S>;