Merge pull request #126 from xacrimon/acrimon/fci

v4 "The maintenance release"
This commit is contained in:
Joel Wejdenstål 2020-12-26 00:51:27 +01:00 committed by GitHub
commit 63f8e06ba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 32 additions and 261 deletions

View File

@ -26,8 +26,7 @@ jobs:
i686-unknown-linux-gnu,
aarch64-unknown-linux-gnu,
armv7-linux-androideabi,
powerpc-unknown-linux-gnu,
wasm32-unknown-emscripten
powerpc-unknown-linux-gnu
]
steps:
- uses: actions/checkout@v2

View File

@ -1,6 +1,6 @@
[package]
name = "dashmap"
version = "3.11.10"
version = "4.0.0"
authors = ["Acrimon <joel.wejdenstal@gmail.com>"]
edition = "2018"
license = "MIT"
@ -15,14 +15,11 @@ categories = ["concurrency", "algorithms", "data-structures"]
[features]
default = []
raw-api = []
no_std = ["hashbrown"]
[dependencies]
num_cpus = "1.13.0"
ahash = "0.3.8"
serde = { version = "1.0.114", optional = true, features = ["derive"] }
serde = { version = "1.0.118", optional = true, features = ["derive"] }
cfg-if = "1.0.0"
hashbrown = { version = "0.8.0", optional = true }
rayon = { version = "1.5.0", optional = true }
[package.metadata.docs.rs]

View File

@ -24,12 +24,12 @@ If you have any suggestions or tips do not hesitate to open an issue or a PR.
## Cargo features
- `no_std` - Enable no_std + alloc support.
- `serde` - Enables serde support.
- `raw-api` - Enables the unstable raw-shard api.
- `rayon` - Enables rayon support.
## Support me
[![Foo](https://c5.patreon.com/external/logo/become_a_patron_button@2x.png)](https://patreon.com/acrimon)
@ -47,6 +47,9 @@ Do not hesitate to open issues or PR's.
I will take a look as soon as I have time for it.
That said I do not get paid (yet) to work on open-source. This means
that my time is limited and my work here comes after my personal life.
## Performance
A comprehensive benchmark suite including DashMap can be found [here](https://github.com/xacrimon/conc-map-bench).

View File

@ -4,19 +4,11 @@ use crate::lock::{RwLockReadGuard, RwLockWriteGuard};
use crate::t::Map;
use crate::util::SharedValue;
use crate::{DashMap, HashMap};
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::mem;
cfg_if::cfg_if! {
if #[cfg(feature = "no_std")] {
use alloc::sync::Arc;
use hashbrown::hash_map;
} else {
use std::sync::Arc;
use std::collections::hash_map;
}
}
use std::collections::hash_map;
use std::collections::hash_map::RandomState;
use std::sync::Arc;
/// Iterator over a DashMap yielding key value pairs.
///
@ -31,7 +23,6 @@ cfg_if::cfg_if! {
/// let pairs: Vec<(&'static str, &'static str)> = map.into_iter().collect();
/// assert_eq!(pairs.len(), 2);
/// ```
pub struct OwningIter<K, V, S = RandomState> {
map: DashMap<K, V, S>,
shard_i: usize,
@ -121,7 +112,6 @@ type GuardIterMut<'a, K, V, S> = (
/// map.insert("hello", "world");
/// assert_eq!(map.iter().count(), 1);
/// ```
pub struct Iter<'a, K, V, S = RandomState, M = DashMap<K, V, S>> {
map: &'a M,
shard_i: usize,
@ -200,7 +190,6 @@ impl<'a, K: Eq + Hash, V, S: 'a + BuildHasher + Clone, M: Map<'a, K, V, S>> Iter
/// map.iter_mut().for_each(|mut r| *r += 1);
/// assert_eq!(*map.get("Johnny").unwrap(), 22);
/// ```
pub struct IterMut<'a, K, V, S = RandomState, M = DashMap<K, V, S>> {
map: &'a M,
shard_i: usize,
@ -276,13 +265,10 @@ impl<'a, K: Eq + Hash, V, S: 'a + BuildHasher + Clone, M: Map<'a, K, V, S>> Iter
}
#[cfg(test)]
mod tests {
use crate::DashMap;
#[test]
fn iter_mut_manual_count() {
let map = DashMap::new();
@ -300,7 +286,6 @@ mod tests {
}
#[test]
fn iter_mut_count() {
let map = DashMap::new();
@ -312,7 +297,6 @@ mod tests {
}
#[test]
fn iter_count() {
let map = DashMap::new();

View File

@ -1,4 +1,3 @@
#![cfg_attr(all(feature = "no_std", not(test)), no_std)] // Note: Concurrency tests require std for threading/channels
#![allow(clippy::type_complexity)]
pub mod iter;
@ -19,7 +18,6 @@ pub mod rayon {
pub mod set;
}
use ahash::RandomState;
use cfg_if::cfg_if;
use core::borrow::Borrow;
use core::fmt;
@ -33,6 +31,7 @@ use mapref::multiple::RefMulti;
use mapref::one::{Ref, RefMut};
pub use read_only::ReadOnlyView;
pub use set::DashSet;
use std::collections::hash_map::RandomState;
pub use t::Map;
cfg_if! {
@ -43,17 +42,7 @@ cfg_if! {
}
}
cfg_if! {
if #[cfg(feature = "no_std")] {
extern crate alloc;
use alloc::{vec::Vec, boxed::Box};
pub(crate) type HashMap<K, V, S> = hashbrown::HashMap<K, SharedValue<V>, S>;
} else {
pub(crate) type HashMap<K, V, S> = std::collections::HashMap<K, SharedValue<V>, S>;
}
}
pub(crate) type HashMap<K, V, S> = std::collections::HashMap<K, SharedValue<V>, S>;
fn shard_amount() -> usize {
(num_cpus::get() * 4).next_power_of_two()
@ -71,7 +60,9 @@ fn ncb(shard_amount: usize) -> usize {
/// 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`.
/// 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.
/// This means that it is safe to ignore it across multiple threads.
pub struct DashMap<K, V, S = RandomState> {
shift: usize,
shards: Box<[RwLock<HashMap<K, V, S>>]>,
@ -117,7 +108,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a> DashMap<K, V, RandomState> {
/// let reviews = DashMap::new();
/// reviews.insert("Veloren", "What a fantastic game!");
/// ```
pub fn new() -> Self {
DashMap::with_hasher(RandomState::default())
}
@ -133,7 +123,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a> DashMap<K, V, RandomState> {
/// mappings.insert(2, 4);
/// mappings.insert(8, 16);
/// ```
pub fn with_capacity(capacity: usize) -> Self {
DashMap::with_capacity_and_hasher(capacity, RandomState::default())
}
@ -141,7 +130,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a> DashMap<K, V, RandomState> {
impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// Wraps this `DashMap` into a read-only view. This view allows to obtain raw references to the stored values.
pub fn into_read_only(self) -> ReadOnlyView<K, V, S> {
ReadOnlyView::new(self)
}
@ -158,7 +146,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let reviews = DashMap::with_hasher(s);
/// reviews.insert("Veloren", "What a fantastic game!");
/// ```
pub fn with_hasher(hasher: S) -> Self {
Self::with_capacity_and_hasher(0, hasher)
}
@ -176,7 +163,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// mappings.insert(2, 4);
/// mappings.insert(8, 16);
/// ```
pub fn with_capacity_and_hasher(mut capacity: usize, hasher: S) -> Self {
let shard_amount = shard_amount();
let shift = util::ptr_size_bits() - ncb(shard_amount);
@ -200,7 +186,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// 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();
@ -224,13 +209,11 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let map = DashMap::<(), ()>::new();
/// println!("Amount of shards: {}", map.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.shards
}
} else {
#[allow(dead_code)]
pub(crate) fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
&self.shards
}
@ -254,7 +237,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// map.insert("coca-cola", 1.4);
/// println!("coca-cola is stored in shard: {}", map.determine_map("coca-cola"));
/// ```
pub fn determine_map<Q>(&self, key: &Q) -> usize
where
K: Borrow<Q>,
@ -282,7 +264,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let hash = map.hash_usize(&key);
/// println!("hash is stored in shard: {}", map.determine_shard(hash));
/// ```
pub fn determine_shard(&self, hash: usize) -> usize {
// Leave the high 7 bits for the HashBrown SIMD tag.
(hash << 7) >> self.shift
@ -302,7 +283,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
///
/// ```rust
/// use dashmap::DashMap;
/// use ahash::RandomState;
/// use std::collections::hash_map::RandomState;
///
/// let hasher = RandomState::new();
/// let map: DashMap<i32, i32> = DashMap::new();
@ -310,7 +291,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// ```
///
/// [`BuildHasher`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html
pub fn hasher(&self) -> &S {
&self.hasher
}
@ -327,7 +307,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let map = DashMap::new();
/// map.insert("I am the key!", "And I am the value!");
/// ```
pub fn insert(&self, key: K, value: V) -> Option<V> {
self._insert(key, value)
}
@ -345,7 +324,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// soccer_team.insert("Jack", "Goalie");
/// assert_eq!(soccer_team.remove("Jack").unwrap().1, "Goalie");
/// ```
pub fn remove<Q>(&self, key: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
@ -375,7 +353,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// soccer_team.remove_if("Sam", |_, position| position == &"Forward");
/// assert!(!soccer_team.contains_key("Sam"));
/// ```
pub fn remove_if<Q>(&self, key: &Q, f: impl FnOnce(&K, &V) -> bool) -> Option<(K, V)>
where
K: Borrow<Q>,
@ -397,7 +374,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// words.insert("hello", "world");
/// assert_eq!(words.iter().count(), 1);
/// ```
pub fn iter(&'a self) -> Iter<'a, K, V, S, DashMap<K, V, S>> {
self._iter()
}
@ -416,7 +392,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// map.iter_mut().for_each(|mut r| *r += 1);
/// assert_eq!(*map.get("Johnny").unwrap(), 22);
/// ```
pub fn iter_mut(&'a self) -> IterMut<'a, K, V, S, DashMap<K, V, S>> {
self._iter_mut()
}
@ -434,7 +409,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// youtubers.insert("Bosnian Bill", 457000);
/// assert_eq!(*youtubers.get("Bosnian Bill").unwrap(), 457000);
/// ```
pub fn get<Q>(&'a self, key: &Q) -> Option<Ref<'a, K, V, S>>
where
K: Borrow<Q>,
@ -457,7 +431,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// *class.get_mut("Albin").unwrap() -= 1;
/// assert_eq!(*class.get("Albin").unwrap(), 14);
/// ```
pub fn get_mut<Q>(&'a self, key: &Q) -> Option<RefMut<'a, K, V, S>>
where
K: Borrow<Q>,
@ -469,7 +442,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// Remove excess capacity to reduce memory usage.
///
/// **Locking behaviour:** May deadlock if called when holding any sort of reference into the map.
pub fn shrink_to_fit(&self) {
self._shrink_to_fit();
}
@ -491,7 +463,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// people.retain(|_, v| *v > 20);
/// assert_eq!(people.len(), 2);
/// ```
pub fn retain(&self, f: impl FnMut(&K, &mut V) -> bool) {
self._retain(f);
}
@ -511,7 +482,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// people.insert("Charlie", 27);
/// assert_eq!(people.len(), 3);
/// ```
pub fn len(&self) -> usize {
self._len()
}
@ -528,7 +498,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let map = DashMap::<(), ()>::new();
/// assert!(map.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self._is_empty()
}
@ -548,7 +517,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// stats.clear();
/// assert!(stats.is_empty());
/// ```
pub fn clear(&self) {
self._clear();
}
@ -556,7 +524,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// Returns how many key-value pairs the map can store without reallocating.
///
/// **Locking behaviour:** May deadlock if called when holding a mutable reference into the map.
pub fn capacity(&self) -> usize {
self._capacity()
}
@ -579,7 +546,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// # Panics
///
/// If the given closure panics, then `alter` will abort the process
pub fn alter<Q>(&self, key: &Q, f: impl FnOnce(&K, V) -> V)
where
K: Borrow<Q>,
@ -608,7 +574,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// # Panics
///
/// If the given closure panics, then `alter_all` will abort the process
pub fn alter_all(&self, f: impl FnMut(&K, V) -> V) {
self._alter_all(f);
}
@ -626,7 +591,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// team_sizes.insert("Dakota Cherries", 23);
/// assert!(team_sizes.contains_key("Dakota Cherries"));
/// ```
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
@ -639,7 +603,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// See the documentation on `dashmap::mapref::entry` for more details.
///
/// **Locking behaviour:** May deadlock if called when holding any sort of reference into the map.
pub fn entry(&'a self, key: K) -> Entry<'a, K, V, S> {
self._entry(key)
}
@ -946,22 +909,11 @@ impl<K: Eq + Hash, V> FromIterator<(K, V)> for DashMap<K, V, RandomState> {
}
#[cfg(test)]
mod tests {
use crate::DashMap;
cfg_if::cfg_if! {
if #[cfg(feature = "no_std")] {
use alloc::string::String;
use ahash::RandomState;
} else {
use std::collections::hash_map::RandomState;
}
}
use std::collections::hash_map::RandomState;
#[test]
fn test_basic() {
let dm = DashMap::new();
@ -971,7 +923,6 @@ mod tests {
}
#[test]
fn test_default() {
let dm: DashMap<u32, u32> = DashMap::default();
@ -981,7 +932,6 @@ mod tests {
}
#[test]
fn test_multiple_hashes() {
let dm: DashMap<u32, u32> = DashMap::default();
@ -1005,7 +955,6 @@ mod tests {
}
#[test]
fn test_more_complex_values() {
#[derive(Hash, PartialEq, Debug, Clone)]
@ -1031,7 +980,6 @@ mod tests {
}
#[test]
fn test_different_hashers_randomstate() {
let dm_hm_default: DashMap<u32, u32, RandomState> =
DashMap::with_hasher(RandomState::new());

View File

@ -19,7 +19,6 @@ const UPGRADED: usize = 1 << 1;
const WRITER: usize = 1;
#[derive(Debug)]
pub struct RwLockReadGuard<'a, T: 'a + ?Sized> {
lock: &'a AtomicUsize,
data: NonNull<T>,
@ -30,7 +29,6 @@ unsafe impl<'a, T: Send> Send for RwLockReadGuard<'a, T> {}
unsafe impl<'a, T: Sync> Sync for RwLockReadGuard<'a, T> {}
#[derive(Debug)]
pub struct RwLockWriteGuard<'a, T: 'a + ?Sized> {
lock: &'a AtomicUsize,
data: NonNull<T>,
@ -43,7 +41,6 @@ unsafe impl<'a, T: Send> Send for RwLockWriteGuard<'a, T> {}
unsafe impl<'a, T: Sync> Sync for RwLockWriteGuard<'a, T> {}
#[derive(Debug)]
pub struct RwLockUpgradeableGuard<'a, T: 'a + ?Sized> {
lock: &'a AtomicUsize,
data: NonNull<T>,
@ -101,7 +98,6 @@ impl<T: ?Sized> RwLock<T> {
/// # Safety
///
/// This is only safe if the lock is currently locked in read mode and the number of readers is not 0.
pub unsafe fn force_read_decrement(&self) {
debug_assert!(self.lock.load(Ordering::Relaxed) & !WRITER > 0);
@ -111,7 +107,6 @@ impl<T: ?Sized> RwLock<T> {
/// # Safety
///
/// The lock must be locked in write mode.
pub unsafe fn force_write_unlock(&self) {
debug_assert_eq!(self.lock.load(Ordering::Relaxed) & !(WRITER | UPGRADED), 0);
@ -334,9 +329,7 @@ fn compare_exchange(
}
#[cfg(test)]
mod tests {
use super::*;
use std::prelude::v1::*;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -345,31 +338,22 @@ mod tests {
use std::thread;
#[derive(Eq, PartialEq, Debug)]
struct NonCopy(i32);
#[test]
fn smoke() {
let l = RwLock::new(());
drop(l.read());
drop(l.write());
drop((l.read(), l.read()));
drop(l.write());
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_rw_arc() {
let arc = Arc::new(RwLock::new(0));
let arc2 = arc.clone();
let (tx, rx) = channel();
thread::spawn(move || {
@ -377,11 +361,8 @@ mod tests {
for _ in 0..10 {
let tmp = *lock;
*lock = -1;
thread::yield_now();
*lock = tmp + 1;
}
@ -395,7 +376,6 @@ mod tests {
children.push(thread::spawn(move || {
let lock = arc3.read();
assert!(*lock >= 0);
}));
}
@ -405,18 +385,14 @@ mod tests {
}
rx.recv().unwrap();
let lock = arc.read();
assert_eq!(*lock, 10);
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_rw_access_in_unwind() {
let arc = Arc::new(RwLock::new(1));
let arc2 = arc.clone();
let _ = thread::spawn(move || {
@ -439,20 +415,16 @@ mod tests {
.join();
let lock = arc.read();
assert_eq!(*lock, 2);
}
#[test]
fn test_rwlock_unsized() {
let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]);
{
let b = &mut *rw.write();
b[0] = 4;
b[2] = 5;
}
@ -462,14 +434,11 @@ mod tests {
}
#[test]
fn test_rwlock_try_write() {
use std::mem::drop;
let lock = RwLock::new(0isize);
let read_guard = lock.read();
let write_result = lock.try_write();
match write_result {
@ -481,7 +450,6 @@ mod tests {
}
#[test]
fn test_rw_try_read() {
let m = RwLock::new(0);
@ -491,7 +459,6 @@ mod tests {
}
#[test]
fn test_into_inner() {
let m = RwLock::new(NonCopy(10));
@ -499,7 +466,6 @@ mod tests {
}
#[test]
fn test_into_inner_drop() {
struct Foo(Arc<AtomicUsize>);
@ -510,14 +476,11 @@ mod tests {
}
let num_drops = Arc::new(AtomicUsize::new(0));
let m = RwLock::new(Foo(num_drops.clone()));
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
{
let _inner = m.into_inner();
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
}
@ -525,21 +488,15 @@ mod tests {
}
#[test]
fn test_force_read_decrement() {
let m = RwLock::new(());
::std::mem::forget(m.read());
::std::mem::forget(m.read());
::std::mem::forget(m.read());
assert!(m.try_write().is_none());
unsafe {
m.force_read_decrement();
m.force_read_decrement();
}
@ -553,7 +510,6 @@ mod tests {
}
#[test]
fn test_force_write_unlock() {
let m = RwLock::new(());
@ -569,39 +525,28 @@ mod tests {
}
#[test]
fn test_upgrade_downgrade() {
let m = RwLock::new(());
{
let _r = m.read();
let upg = m.try_upgradeable_read().unwrap();
assert!(m.try_read().is_none());
assert!(m.try_write().is_none());
assert!(upg.try_upgrade().is_err());
}
{
let w = m.write();
assert!(m.try_upgradeable_read().is_none());
let _r = w.downgrade();
assert!(m.try_upgradeable_read().is_some());
assert!(m.try_read().is_some());
assert!(m.try_write().is_none());
}
{
let _u = m.upgradeable_read();
assert!(m.try_upgradeable_read().is_none());
}

View File

@ -3,10 +3,10 @@ use crate::lock::RwLockWriteGuard;
use crate::util;
use crate::util::SharedValue;
use crate::HashMap;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::mem;
use core::ptr;
use std::collections::hash_map::RandomState;
pub enum Entry<'a, K, V, S = RandomState> {
Occupied(OccupiedEntry<'a, K, V, S>),
@ -15,7 +15,6 @@ pub enum Entry<'a, K, V, S = RandomState> {
impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
/// Apply a function to the stored value if it exists.
pub fn and_modify(self, f: impl FnOnce(&mut V)) -> Self {
match self {
Entry::Occupied(mut entry) => {
@ -29,7 +28,6 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
}
/// Get the key of the entry.
pub fn key(&self) -> &K {
match *self {
Entry::Occupied(ref entry) => entry.key(),
@ -38,7 +36,6 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
}
/// Into the key of the entry.
pub fn into_key(self) -> K {
match self {
Entry::Occupied(entry) => entry.into_key(),
@ -48,7 +45,6 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
/// Return a mutable reference to the element if it exists,
/// otherwise insert the default and return a mutable reference to that.
pub fn or_default(self) -> RefMut<'a, K, V, S>
where
V: Default,
@ -61,7 +57,6 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
/// Return a mutable reference to the element if it exists,
/// otherwise a provided value and return a mutable reference to that.
pub fn or_insert(self, value: V) -> RefMut<'a, K, V, S> {
match self {
Entry::Occupied(entry) => entry.into_ref(),
@ -71,7 +66,6 @@ impl<'a, K: Eq + Hash, V, S: BuildHasher> Entry<'a, K, V, S> {
/// Return a mutable reference to the element if it exists,
/// otherwise insert the result of a provided function and return a mutable reference to that.
pub fn or_insert_with(self, value: impl FnOnce() -> V) -> RefMut<'a, K, V, S> {
match self {
Entry::Occupied(entry) => entry.into_ref(),

View File

@ -1,17 +1,10 @@
use crate::lock::{RwLockReadGuard, RwLockWriteGuard};
use crate::HashMap;
use ahash::RandomState;
use core::hash::BuildHasher;
use core::hash::Hash;
use core::ops::{Deref, DerefMut};
cfg_if::cfg_if! {
if #[cfg(feature = "no_std")] {
use alloc::sync::Arc;
} else {
use std::sync::Arc;
}
}
use std::collections::hash_map::RandomState;
use std::sync::Arc;
// -- Shared
pub struct RefMulti<'a, K, V, S = RandomState> {

View File

@ -1,8 +1,8 @@
use crate::lock::{RwLockReadGuard, RwLockWriteGuard};
use crate::HashMap;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::ops::{Deref, DerefMut};
use std::collections::hash_map::RandomState;
// -- Shared
pub struct Ref<'a, K, V, S = RandomState> {

View File

@ -2,18 +2,11 @@ use crate::lock::RwLock;
use crate::mapref::multiple::{RefMulti, RefMutMulti};
use crate::util;
use crate::{DashMap, HashMap};
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use rayon::iter::plumbing::UnindexedConsumer;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
cfg_if::cfg_if! {
if #[cfg(feature = "no_std")] {
use alloc::{boxed::Box, sync::Arc, vec::Vec};
} else {
use std::sync::Arc;
}
}
use std::collections::hash_map::RandomState;
use std::sync::Arc;
impl<K, V, S> ParallelExtend<(K, V)> for DashMap<K, V, S>
where

View File

@ -1,9 +1,9 @@
use crate::setref::multiple::RefMulti;
use crate::DashSet;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use rayon::iter::plumbing::UnindexedConsumer;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
use std::collections::hash_map::RandomState;
impl<K, S> ParallelExtend<K> for DashSet<K, S>
where

View File

@ -1,12 +1,11 @@
use crate::t::Map;
use crate::{DashMap, HashMap};
use ahash::RandomState;
use core::borrow::Borrow;
use core::fmt;
use core::hash::{BuildHasher, Hash};
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>,
}
@ -33,7 +32,6 @@ impl<K, V, S> ReadOnlyView<K, V, S> {
}
/// Consumes this `ReadOnlyView`, returning the underlying `DashMap`.
pub fn into_inner(self) -> DashMap<K, V, S> {
self.map
}
@ -41,25 +39,21 @@ impl<K, V, S> ReadOnlyView<K, V, S> {
impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S> {
/// Returns the number of elements in the map.
pub fn len(&self) -> usize {
self.map.len()
}
/// Returns `true` if the map contains no elements.
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
/// Returns the number of elements the map can hold without reallocating.
pub fn capacity(&self) -> usize {
self.map.capacity()
}
/// Returns `true` if the map contains a value for the specified key.
pub fn contains_key<Q>(&'a self, key: &Q) -> bool
where
K: Borrow<Q>,
@ -75,7 +69,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
}
/// Returns a reference to the value corresponding to the key.
pub fn get<Q>(&'a self, key: &Q) -> Option<&'a V>
where
K: Borrow<Q>,
@ -91,7 +84,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
}
/// Returns the key-value pair corresponding to the supplied key.
pub fn get_key_value<Q>(&'a self, key: &Q) -> Option<(&'a K, &'a V)>
where
K: Borrow<Q>,
@ -112,7 +104,6 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
}
/// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is `(&'a K, &'a V)`.
pub fn iter(&'a self) -> impl Iterator<Item = (&'a K, &'a V)> + 'a {
self.shard_read_iter()
.flat_map(|shard| shard.iter())
@ -120,13 +111,11 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
}
/// An iterator visiting all keys in arbitrary order. The iterator element type is `&'a K`.
pub fn keys(&'a self) -> impl Iterator<Item = &'a K> + 'a {
self.shard_read_iter().flat_map(|shard| shard.keys())
}
/// An iterator visiting all values in arbitrary order. The iterator element type is `&'a V`.
pub fn values(&'a self) -> impl Iterator<Item = &'a V> + 'a {
self.shard_read_iter()
.flat_map(|shard| shard.values())

View File

@ -5,18 +5,17 @@ use crate::setref::one::Ref;
use crate::DashMap;
#[cfg(feature = "raw-api")]
use crate::HashMap;
use ahash::RandomState;
use cfg_if::cfg_if;
use core::borrow::Borrow;
use core::fmt;
use core::hash::{BuildHasher, Hash};
use core::iter::FromIterator;
use std::collections::hash_map::RandomState;
/// DashSet is a thin wrapper around [`DashMap`] using `()` as the value type. It uses
/// methods and types which are more convenient to work with on a set.
///
/// [`DashMap`]: struct.DashMap.html
pub struct DashSet<K, S = RandomState> {
pub(crate) inner: DashMap<K, (), S>,
}
@ -60,7 +59,6 @@ impl<'a, K: 'a + Eq + Hash> DashSet<K, RandomState> {
/// let games = DashSet::new();
/// games.insert("Veloren");
/// ```
pub fn new() -> Self {
Self::with_hasher(RandomState::default())
}
@ -76,7 +74,6 @@ impl<'a, K: 'a + Eq + Hash> DashSet<K, RandomState> {
/// numbers.insert(2);
/// numbers.insert(8);
/// ```
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, RandomState::default())
}
@ -95,7 +92,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let games = DashSet::with_hasher(s);
/// games.insert("Veloren");
/// ```
pub fn with_hasher(hasher: S) -> Self {
Self::with_capacity_and_hasher(0, hasher)
}
@ -113,7 +109,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// numbers.insert(2);
/// numbers.insert(8);
/// ```
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
Self {
inner: DashMap::with_capacity_and_hasher(capacity, hasher),
@ -122,7 +117,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// 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.inner.hash_usize(item)
}
@ -142,7 +136,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let set = DashSet::<()>::new();
/// println!("Amount of shards: {}", set.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, (), S>>] {
self.inner.shards()
}
@ -166,7 +159,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// set.insert("coca-cola");
/// println!("coca-cola is stored in shard: {}", set.determine_map("coca-cola"));
/// ```
pub fn determine_map<Q>(&self, key: &Q) -> usize
where
K: Borrow<Q>,
@ -193,7 +185,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let hash = set.hash_usize(&key);
/// println!("hash is stored in shard: {}", set.determine_shard(hash));
/// ```
pub fn determine_shard(&self, hash: usize) -> usize {
self.inner.determine_shard(hash)
}
@ -210,7 +201,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let set = DashSet::new();
/// set.insert("I am the key!");
/// ```
pub fn insert(&self, key: K) -> bool {
self.inner.insert(key, ()).is_none()
}
@ -226,7 +216,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// soccer_team.insert("Jack");
/// assert_eq!(soccer_team.remove("Jack").unwrap(), "Jack");
/// ```
pub fn remove<Q>(&self, key: &Q) -> Option<K>
where
K: Borrow<Q>,
@ -254,7 +243,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// soccer_team.remove_if("Jacob", |player| player.starts_with("Ja"));
/// assert!(!soccer_team.contains("Jacob"));
/// ```
pub fn remove_if<Q>(&self, key: &Q, f: impl FnOnce(&K) -> bool) -> Option<K>
where
K: Borrow<Q>,
@ -275,7 +263,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// words.insert("hello");
/// assert_eq!(words.iter().count(), 1);
/// ```
pub fn iter(&'a self) -> Iter<'a, K, S, DashMap<K, (), S>> {
let iter = self.inner.iter();
@ -293,7 +280,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// youtubers.insert("Bosnian Bill");
/// assert_eq!(*youtubers.get("Bosnian Bill").unwrap(), "Bosnian Bill");
/// ```
pub fn get<Q>(&'a self, key: &Q) -> Option<Ref<'a, K, S>>
where
K: Borrow<Q>,
@ -303,7 +289,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
}
/// Remove excess capacity to reduce memory usage.
pub fn shrink_to_fit(&self) {
self.inner.shrink_to_fit()
}
@ -323,9 +308,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// people.retain(|name| name.contains('i'));
/// assert_eq!(people.len(), 2);
/// ```
pub fn retain(&self, mut f: impl FnMut(&K) -> bool) {
// TODO: Don't create another closure
self.inner.retain(|k, _| f(k))
}
@ -342,7 +325,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// people.insert("Charlie");
/// assert_eq!(people.len(), 3);
/// ```
pub fn len(&self) -> usize {
self.inner.len()
}
@ -357,7 +339,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let map = DashSet::<()>::new();
/// assert!(map.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
@ -375,13 +356,11 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// people.clear();
/// assert!(people.is_empty());
/// ```
pub fn clear(&self) {
self.inner.clear()
}
/// Returns how many keys the set can store without reallocating.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
@ -397,7 +376,6 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// people.insert("Dakota Cherries");
/// assert!(people.contains("Dakota Cherries"));
/// ```
pub fn contains<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
@ -436,13 +414,10 @@ impl<K: Eq + Hash> FromIterator<K> for DashSet<K, RandomState> {
}
#[cfg(test)]
mod tests {
use crate::DashSet;
#[test]
fn test_basic() {
let set = DashSet::new();
@ -452,7 +427,6 @@ mod tests {
}
#[test]
fn test_default() {
let set: DashSet<u32> = DashSet::default();
@ -462,7 +436,6 @@ mod tests {
}
#[test]
fn test_multiple_hashes() {
let set = DashSet::<u32>::default();

View File

@ -1,8 +1,7 @@
use crate::mapref;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::ops::Deref;
use std::collections::hash_map::RandomState;
pub struct RefMulti<'a, K, S = RandomState> {
inner: mapref::multiple::RefMulti<'a, K, (), S>,
}

View File

@ -1,8 +1,7 @@
use crate::mapref;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::ops::Deref;
use std::collections::hash_map::RandomState;
pub struct Ref<'a, K, S = RandomState> {
inner: mapref::one::Ref<'a, K, (), S>,
}

View File

@ -1,4 +1,5 @@
//! Central map trait to ease modifications and extensions down the road.
use crate::iter::{Iter, IterMut};
use crate::lock::{RwLockReadGuard, RwLockWriteGuard};
use crate::mapref::entry::Entry;
@ -8,26 +9,22 @@ use core::borrow::Borrow;
use core::hash::{BuildHasher, Hash};
/// Implementation detail that is exposed due to generic constraints in public types.
pub trait Map<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + Clone + BuildHasher> {
fn _shard_count(&self) -> usize;
/// # Safety
///
/// The index must not be out of bounds.
unsafe fn _get_read_shard(&'a self, i: usize) -> &'a HashMap<K, V, S>;
/// # Safety
///
/// The index must not be out of bounds.
unsafe fn _yield_read_shard(&'a self, i: usize) -> RwLockReadGuard<'a, HashMap<K, V, S>>;
/// # Safety
///
/// The index must not be out of bounds.
unsafe fn _yield_write_shard(&'a self, i: usize) -> RwLockWriteGuard<'a, HashMap<K, V, S>>;
fn _insert(&self, key: K, value: V) -> Option<V>;

View File

@ -22,7 +22,6 @@ pub fn map_in_place_2<T, U, F: FnOnce(U, T) -> T>((k, v): (U, &mut T), f: F) {
///
/// Requires that you ensure the reference does not become invalid.
/// The object has to outlive the reference.
pub unsafe fn change_lifetime_const<'a, 'b, T>(x: &'a T) -> &'b T {
&*(x as *const T)
}
@ -31,7 +30,6 @@ pub unsafe fn change_lifetime_const<'a, 'b, T>(x: &'a T) -> &'b T {
///
/// Requires that you ensure the reference does not become invalid.
/// The object has to outlive the reference.
pub unsafe fn change_lifetime_mut<'a, 'b, T>(x: &'a mut T) -> &'b mut T {
&mut *(x as *mut T)
}
@ -46,7 +44,6 @@ pub unsafe fn change_lifetime_mut<'a, 'b, T>(x: &'a mut T) -> &'b mut T {
///
/// This type is meant to be an implementation detail, but must be exposed due to the `Dashmap::shards`
#[repr(transparent)]
pub struct SharedValue<T> {
value: UnsafeCell<T>,
}
@ -67,7 +64,6 @@ unsafe impl<T: Sync> Sync for SharedValue<T> {}
impl<T> SharedValue<T> {
/// Create a new `SharedValue<T>`
pub const fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
@ -75,25 +71,21 @@ impl<T> SharedValue<T> {
}
/// Get a shared reference to `T`
pub fn get(&self) -> &T {
unsafe { &*self.value.get() }
}
/// Get an unique reference to `T`
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.value.get() }
}
/// Unwraps the value
pub fn into_inner(self) -> T {
self.value.into_inner()
}
/// Get a mutable raw pointer to the underlying value
pub(crate) fn as_ptr(&self) -> *mut T {
self.value.get()
}
@ -103,42 +95,8 @@ struct AbortOnPanic;
impl Drop for AbortOnPanic {
fn drop(&mut self) {
cfg_if::cfg_if! {
if #[cfg(feature = "no_std")] {
// Note: This is hard, as core/no_std has no concept of threads or knowledge of panicking.
// An alternative would be to do this:
//
// ```rust
// // Elsewhere in the library/host binary
// use core::sync::atomic::{AtomicBool, Ordering};
//
// static UNWINDING: AtomicBool = AtomicBool::new(false);
//
// #[panic_handler]
// fn panic(info: &PanicInfo) -> ! {
// UNWINDING.store(true, Ordering::Relaxed);
//
// unsafe {
// core::intrinsics::abort();
// }
// }
//
// // In AbortOnPanic::drop
// if UNWINDING.load(Ordering::Relaxed) {
// unsafe {
// core::intrinsics::abort();
// }
// }
// ```
//
// Now, this isn't an ideal solution for multiple reasons, as it uses intrinsics which require a feature
// and can be overwritten by the user without them even knowing. That being said, *most* users of no_std
// do tend to use panic = "abort", which solves this problem for us by aborting on panics.
} else {
if std::thread::panicking() {
std::process::abort()
}
}
if std::thread::panicking() {
std::process::abort()
}
}
}