data: use NonZeroUsize for memcache capacity
This commit is contained in:
parent
89aa01c266
commit
f54a172e07
1 changed files with 21 additions and 20 deletions
|
@ -1,11 +1,10 @@
|
|||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::core::*;
|
||||
|
||||
/// Shared in-memory LRU with multiple cache lines.
|
||||
/// Basically a [`std::collections::HashMap`] that is [`Send`] + [`Sync`],
|
||||
/// and without a mechanism to handle hash collisions.
|
||||
|
@ -14,8 +13,8 @@ pub struct MemCache<I: HashableId, T: Indexable<I>> {
|
|||
_phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
pub trait HashableId: Copy + fmt::Debug + Hash + PartialEq + Send {}
|
||||
impl<T> HashableId for T where T: Copy + fmt::Debug + Hash + PartialEq + Send {}
|
||||
pub trait HashableId: Clone + fmt::Debug + Hash + PartialEq + Send {}
|
||||
impl<T> HashableId for T where T: Clone + fmt::Debug + Hash + PartialEq + Send {}
|
||||
|
||||
pub trait Indexable<I: HashableId>: Clone + Send {
|
||||
fn get_id(&self) -> I;
|
||||
|
@ -23,11 +22,11 @@ pub trait Indexable<I: HashableId>: Clone + Send {
|
|||
|
||||
impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
||||
pub fn new() -> MemCache<I, T> {
|
||||
MemCache::with_capacity(256)
|
||||
MemCache::with_capacity(unsafe { NonZeroUsize::new_unchecked(256) })
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> MemCache<I, T> {
|
||||
assert!(capacity > 0);
|
||||
pub fn with_capacity(capacity: NonZeroUsize) -> MemCache<I, T> {
|
||||
let capacity = capacity.get();
|
||||
let mut lines = Vec::with_capacity(capacity);
|
||||
for _ in 0..capacity {
|
||||
lines.push(RwLock::new(None));
|
||||
|
@ -39,7 +38,7 @@ impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
|||
}
|
||||
|
||||
pub async fn get(&self, id: I) -> Option<Arc<T>> {
|
||||
let index = self.compute_index(id);
|
||||
let index = self.compute_index(&id);
|
||||
let guard = self.lines[index].read().await;
|
||||
let entry = Arc::clone(guard.as_ref()?);
|
||||
drop(guard);
|
||||
|
@ -52,7 +51,7 @@ impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
|||
}
|
||||
|
||||
pub async fn put(&self, entry: T) {
|
||||
let index = self.compute_index(entry.get_id());
|
||||
let index = self.compute_index(&entry.get_id());
|
||||
let entry = Arc::new(entry);
|
||||
let mut guard = self.lines[index].write().await;
|
||||
let old_entry = guard.replace(entry);
|
||||
|
@ -64,7 +63,7 @@ impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
|||
}
|
||||
|
||||
pub async fn del(&self, id: I) -> Option<Arc<T>> {
|
||||
let index = self.compute_index(id);
|
||||
let index = self.compute_index(&id);
|
||||
let mut guard = self.lines[index].write().await;
|
||||
let entry = guard.as_ref()?;
|
||||
if entry.get_id() == id {
|
||||
|
@ -81,7 +80,7 @@ impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
|||
F: FnOnce(&mut T),
|
||||
{
|
||||
// FIXME: Find a way to avoid the deep clone
|
||||
let index = self.compute_index(id);
|
||||
let index = self.compute_index(&id);
|
||||
let mut guard = self.lines[index].write().await;
|
||||
if let Some(entry) = guard.as_ref() {
|
||||
if entry.get_id() == id {
|
||||
|
@ -93,7 +92,7 @@ impl<I: HashableId, T: Indexable<I>> MemCache<I, T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_index(&self, id: I) -> usize {
|
||||
fn compute_index(&self, id: &I) -> usize {
|
||||
let mut hasher = Djb2::new();
|
||||
id.hash(&mut hasher);
|
||||
hasher.finish() as usize % self.lines.len()
|
||||
|
@ -135,6 +134,7 @@ impl Hasher for Djb2 {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::core::*;
|
||||
|
||||
type TestId = Id<TestEntry>;
|
||||
|
||||
|
@ -152,12 +152,14 @@ mod tests {
|
|||
|
||||
#[actix_web::test]
|
||||
async fn store_stuff() {
|
||||
let cache = MemCache::with_capacity(256);
|
||||
let cache = MemCache::new();
|
||||
for i in 0i64..1024i64 {
|
||||
let id = i.into();
|
||||
let entry = TestEntry { id, data: 0 };
|
||||
cache.put(entry.clone()).await;
|
||||
assert_eq!(cache.get(id).await.map(|e| e.as_ref()), Some(&entry));
|
||||
|
||||
let retrieved_elem = cache.get(id).await.unwrap();
|
||||
assert_eq!(retrieved_elem.as_ref(), &entry);
|
||||
}
|
||||
|
||||
let mut had_entries = false;
|
||||
|
@ -176,20 +178,19 @@ mod tests {
|
|||
#[actix_web::test]
|
||||
async fn update_stuff() {
|
||||
let id = 420.into();
|
||||
let cache = MemCache::with_capacity(256);
|
||||
let cache = MemCache::new();
|
||||
cache.put(TestEntry { id, data: 69 }).await;
|
||||
cache.update(id, |entry| entry.data = 1312).await;
|
||||
assert_eq!(
|
||||
cache.get(id).await.map(|e| e.as_ref()),
|
||||
Some(&TestEntry { id, data: 1312 })
|
||||
);
|
||||
|
||||
let retrieved_elem = cache.get(id).await.unwrap();
|
||||
assert_eq!(retrieved_elem.as_ref(), &TestEntry { id, data: 1312 });
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
#[should_panic]
|
||||
async fn mutate_id() {
|
||||
let id = 420.into();
|
||||
let cache = MemCache::with_capacity(256);
|
||||
let cache = MemCache::new();
|
||||
cache.put(TestEntry { id, data: 69 }).await;
|
||||
cache.update(id, |entry| entry.id = 1312.into()).await;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue