data: use NonZeroUsize for memcache capacity

This commit is contained in:
anna 2023-01-23 21:23:36 +01:00
parent 89aa01c266
commit f54a172e07
Signed by: fef
GPG key ID: EC22E476DC2D3D84

View file

@ -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;
}