mirror of https://github.com/xacrimon/dashmap
Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
Joel Wejdenstål | 626b98dab3 | |
Joel | 1e7efe5843 | |
Joel Wejdenstål | eff796d46b | |
Joel Wejdenstål | febc45dc62 | |
Joel Wejdenstål | dfb7b9af33 | |
Joel Wejdenstål | 3b26d70d2f | |
Joel Wejdenstål | 71a1dc6cd8 | |
Joel Wejdenstål | 8364d3e48e | |
Arthur Silva | 3448b6f070 | |
Joel Wejdenstål | ea4ea24a63 | |
Joel | 018f0a1671 | |
Joel Wejdenstål | 6616a8d28c | |
Tobias Kahlert | 3b418466ed | |
Amin Yahyaabadi | 079faeb02b | |
Jan | dcc97f8ddb | |
Joel | 4c7568cce6 | |
Joel | d36311e634 | |
Joel | 7f9522c528 | |
Arash Sahebolamri | 8a0149d31b | |
George | 83a2cf46e0 | |
Fointard | 0b2a2269b2 | |
Joel Wejdenstål | 4fd07038a1 | |
Joel Wejdenstål | f27dba3062 | |
Joel Wejdenstål | 342011296c | |
Tom Karwowski | d1f58eaf48 | |
Tom Karwowski | 9deeb37ccb | |
Tom Karwowski | 030671060f | |
Wolf Thomsen | 459db7ac6f | |
Joel | dd7164dfef | |
Joel Wejdenstål | 4b63e98814 | |
Joel | 3236196d01 | |
Konrad Borowski | 5fbb68f188 | |
Joel Wejdenstål | 270612e3f9 | |
Joel Wejdenstål | c162b21b6c |
|
@ -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,3 +1,2 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
|
|
|
@ -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"
|
15
Cargo.toml
15
Cargo.toml
|
@ -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"]
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[toolchain]
|
||||
channel = "1.59"
|
||||
components = [ "rustfmt", "clippy" ]
|
||||
profile = "minimal"
|
|
@ -0,0 +1,4 @@
|
|||
[toolchain]
|
||||
channel = "1.65"
|
||||
components = ["rustfmt", "clippy"]
|
||||
profile = "minimal"
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
226
src/lib.rs
226
src/lib.rs
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
318
src/lock.rs
318
src/lock.rs
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 VacantEntry’s 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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)]
|
||||
|
|
59
src/serde.rs
59
src/serde.rs
|
@ -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! {}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in New Issue