Skip to content

Commit

Permalink
Merge pull request #167 from moka-rs/get-with-slice
Browse files Browse the repository at this point in the history
Change the trait bound `Arc<K>: Borrow<Q>` to `K: Borrow<Q>` (`sync` and `future` caches)
  • Loading branch information
tatsuya6502 authored Jul 16, 2022
2 parents 043b311 + 9d3d834 commit f6059e0
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 332 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"deqs",
"Deque",
"Deques",
"docsrs",
"Einziger",
"else's",
"Eytan",
Expand Down
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Moka Cache &mdash; Change Log

## Version 0.9.1

### Fixed (Changed)

- Relax the too restrictive requirement `Arc<K>: Borrow<Q>` to `K: Borrow<Q>` for the
`contains_key`, `get` and `invalidate` methods of the following caches, so that
they will accept `&[u8]` as the key when `K` is `Vec<u8>`: ([#167][gh-pull-0167])
- `sync::Cache`
- `sync::SegmentedCache`
- `future::Cache`


## Version 0.9.0

### Added

- Add support for eviction listener to the following caches ([#145][gh-pull-0145]).
Eviction listener is a callback function that will be called when an entry is
removed from the cache.
removed from the cache:
- `sync::Cache`
- `sync::SegmentedCache`
- `future::Cache`
Expand Down Expand Up @@ -412,6 +424,7 @@ The minimum supported Rust version (MSRV) is now 1.51.0 (2021-03-25).
[gh-issue-0034]: https://github.com/moka-rs/moka/issues/34/
[gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/

[gh-pull-0167]: https://github.com/moka-rs/moka/pull/167/
[gh-pull-0159]: https://github.com/moka-rs/moka/pull/159/
[gh-pull-0145]: https://github.com/moka-rs/moka/pull/145/
[gh-pull-0143]: https://github.com/moka-rs/moka/pull/143/
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "moka"
version = "0.9.0"
version = "0.9.1"
edition = "2018"
rust-version = "1.51"

Expand Down
88 changes: 54 additions & 34 deletions src/cht/map/bucket.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::{
borrow::Borrow,
hash::{BuildHasher, Hash, Hasher},
mem::{self, MaybeUninit},
ptr,
Expand Down Expand Up @@ -72,16 +71,12 @@ impl<K, V> Drop for BucketArray<K, V> {
}

impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
pub(crate) fn get<Q>(
pub(crate) fn get(
&self,
guard: &'g Guard,
hash: u64,
key: &Q,
) -> Result<Shared<'g, Bucket<K, V>>, RelocatedError>
where
Q: Eq + ?Sized,
K: Borrow<Q>,
{
mut eq: impl FnMut(&K) -> bool,
) -> Result<Shared<'g, Bucket<K, V>>, RelocatedError> {
let loop_result = self.probe_loop(guard, hash, |_, _, this_bucket_ptr| {
let this_bucket_ref = if let Some(r) = unsafe { this_bucket_ptr.as_ref() } {
r
Expand All @@ -90,7 +85,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
return ProbeLoopAction::Return(Shared::null());
};

if this_bucket_ref.key.borrow() != key {
if !eq(&this_bucket_ref.key) {
// Different key. Try next bucket
return ProbeLoopAction::Continue;
}
Expand All @@ -111,16 +106,14 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
}
}

pub(crate) fn remove_if<Q, F>(
pub(crate) fn remove_if<F>(
&self,
guard: &'g Guard,
hash: u64,
key: &Q,
mut eq: impl FnMut(&K) -> bool,
mut condition: F,
) -> Result<Shared<'g, Bucket<K, V>>, F>
where
Q: Eq + ?Sized,
K: Borrow<Q>,
F: FnMut(&K, &V) -> bool,
{
let loop_result = self.probe_loop(guard, hash, |_, this_bucket, this_bucket_ptr| {
Expand All @@ -133,7 +126,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {

let this_key = &this_bucket_ref.key;

if this_key.borrow() != key {
if !eq(this_key) {
// Different key. Try next bucket.
return ProbeLoopAction::Continue;
}
Expand Down Expand Up @@ -189,7 +182,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
let state = maybe_state.take().unwrap();

if let Some(this_bucket_ref) = unsafe { this_bucket_ptr.as_ref() } {
if this_bucket_ref.key.borrow() != state.key() {
if &this_bucket_ref.key != state.key() {
// Different key. Try next bucket.
maybe_state = Some(state);
return ProbeLoopAction::Continue;
Expand Down Expand Up @@ -784,52 +777,79 @@ mod tests {
let h3 = hash(&build_hasher, k3);
let v3 = 15;

assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k1, h1, || v1),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k2, h2, || v2),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2)));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(
into_value(buckets.get(guard, h2, |&k| k == k2)),
Ok(Some(v2))
);
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k3, h3, || v3),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2)));
assert_eq!(into_value(buckets.get(guard, h3, k3)), Ok(Some(v3)));

let b1 = buckets.remove_if(guard, h1, k1, |_, _| true).ok().unwrap();
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(
into_value(buckets.get(guard, h2, |&k| k == k2)),
Ok(Some(v2))
);
assert_eq!(
into_value(buckets.get(guard, h3, |&k| k == k3)),
Ok(Some(v3))
);

let b1 = buckets
.remove_if(guard, h1, |&k| k == k1, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b1));
unsafe { defer_destroy_tombstone(guard, b1) };

let b2 = buckets.remove_if(guard, h2, k2, |_, _| true).ok().unwrap();
let b2 = buckets
.remove_if(guard, h2, |&k| k == k2, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b2));
unsafe { defer_destroy_tombstone(guard, b2) };

let b3 = buckets.remove_if(guard, h3, k3, |_, _| true).ok().unwrap();
let b3 = buckets
.remove_if(guard, h3, |&k| k == k3, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b3));
unsafe { defer_destroy_tombstone(guard, b3) };

assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

for this_bucket in buckets.buckets.iter() {
let this_bucket_ptr = this_bucket.swap(Shared::null(), Ordering::Relaxed, guard);
Expand Down
66 changes: 21 additions & 45 deletions src/cht/map/bucket_array_ref.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::bucket::{self, Bucket, BucketArray, InsertOrModifyState, RehashOp};

use std::{
borrow::Borrow,
hash::{BuildHasher, Hash},
sync::atomic::{AtomicUsize, Ordering},
};
Expand All @@ -19,17 +18,12 @@ where
K: Hash + Eq,
S: BuildHasher,
{
pub(crate) fn get_key_value_and_then<Q, F, T>(
pub(crate) fn get_key_value_and_then<T>(
&self,
key: &Q,
hash: u64,
with_entry: F,
) -> Option<T>
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
F: FnOnce(&K, &V) -> Option<T>,
{
mut eq: impl FnMut(&K) -> bool,
with_entry: impl FnOnce(&K, &V) -> Option<T>,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand All @@ -38,7 +32,7 @@ where

loop {
match bucket_array_ref
.get(guard, hash, key)
.get(guard, hash, &mut eq)
.map(|p| unsafe { p.as_ref() })
{
Ok(Some(Bucket {
Expand All @@ -64,19 +58,13 @@ where
result
}

pub(crate) fn remove_entry_if_and<Q, F, G, T>(
pub(crate) fn remove_entry_if_and<T>(
&self,
key: &Q,
hash: u64,
mut condition: F,
with_previous_entry: G,
) -> Option<T>
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
F: FnMut(&K, &V) -> bool,
G: FnOnce(&K, &V) -> T,
{
mut eq: impl FnMut(&K) -> bool,
mut condition: impl FnMut(&K, &V) -> bool,
with_previous_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand All @@ -96,7 +84,7 @@ where
bucket_array_ref = bucket_array_ref.rehash(guard, self.build_hasher, rehash_op);
}

match bucket_array_ref.remove_if(guard, hash, key, condition) {
match bucket_array_ref.remove_if(guard, hash, &mut eq, condition) {
Ok(previous_bucket_ptr) => {
if let Some(previous_bucket_ref) = unsafe { previous_bucket_ptr.as_ref() } {
let Bucket {
Expand Down Expand Up @@ -129,17 +117,13 @@ where
result
}

pub(crate) fn insert_if_not_present_and<F, G, T>(
pub(crate) fn insert_if_not_present_and<T>(
&self,
key: K,
hash: u64,
on_insert: F,
with_existing_entry: G,
) -> Option<T>
where
F: FnOnce() -> V,
G: FnOnce(&K, &V) -> T,
{
on_insert: impl FnOnce() -> V,
with_existing_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
use bucket::InsertionResult;

let guard = &crossbeam_epoch::pin();
Expand Down Expand Up @@ -198,19 +182,14 @@ where
result
}

pub(crate) fn insert_with_or_modify_entry_and<T, F, G, H>(
pub(crate) fn insert_with_or_modify_entry_and<T>(
&self,
key: K,
hash: u64,
on_insert: F,
mut on_modify: G,
with_old_entry: H,
) -> Option<T>
where
F: FnOnce() -> V,
G: FnMut(&K, &V) -> V,
H: FnOnce(&K, &V) -> T,
{
on_insert: impl FnOnce() -> V,
mut on_modify: impl FnMut(&K, &V) -> V,
with_old_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand Down Expand Up @@ -267,10 +246,7 @@ where
result
}

pub(crate) fn keys<F, T>(&self, mut with_key: F) -> Vec<T>
where
F: FnMut(&K) -> T,
{
pub(crate) fn keys<T>(&self, mut with_key: impl FnMut(&K) -> T) -> Vec<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand Down
Loading

0 comments on commit f6059e0

Please sign in to comment.