Skip to content

Instantly share code, notes, and snippets.

@ihciah
Created January 19, 2022 08:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ihciah/10c886ab4fefeac227be006795a98319 to your computer and use it in GitHub Desktop.
Save ihciah/10c886ab4fefeac227be006795a98319 to your computer and use it in GitHub Desktop.
Rust Global Cache
use std::{borrow::Borrow, cell::UnsafeCell, hash::Hash, ptr::NonNull, sync::RwLock};
use fxhash::FxHashMap;
// const CACHE: Cache = unsafe { Cache::new() };
struct Cache<K, V> {
data: UnsafeCell<NonNull<RwLock<FxHashMap<K, V>>>>,
}
impl<K, V> Cache<K, V> {
/// Create a new Cache instance
/// # Safety:
/// You MUST call init before drop or manually forget Self, otherwise drop will cause problem.
pub const unsafe fn new() -> Self {
Self {
data: UnsafeCell::new(NonNull::dangling()),
}
}
/// Do init
/// This action may only do once, otherwise it may cause memory leak.
pub fn init(&self) {
let data = unsafe { &mut *self.data.get() };
let ptr = Box::leak(Box::new(RwLock::new(FxHashMap::<K, V>::default())));
*data = unsafe { NonNull::new_unchecked(ptr) };
}
/// Get a key
/// # Safety:
/// You MUST call init before.
pub unsafe fn get<Q: ?Sized>(&self, key: &Q) -> Option<V>
where
V: Clone,
K: Borrow<Q> + Hash + Eq,
Q: Hash + Eq,
{
let data = (&*self.data.get()).as_ref();
let locked = data.read().unwrap();
let val = locked.get(key).cloned();
val
}
/// Get a key and check if it is expired, if so, None is returned.
/// # Safety:
/// You MUST call init before.
pub unsafe fn get_expired<Q: ?Sized>(&self, key: &Q) -> Option<V>
where
V: Clone + Expired,
K: Borrow<Q> + Hash + Eq,
Q: Hash + Eq,
{
let data = (&*self.data.get()).as_ref();
let locked = data.read().unwrap();
let val = locked.get(key);
if let Some(v) = val {
if !v.expired() {
return Some(v.clone());
}
}
None
}
/// Set a key value
/// # Safety:
/// You MUST call init before.
pub unsafe fn set(&self, key: K, val: V) -> Option<V>
where
K: Hash + Eq,
{
let data = (&*self.data.get()).as_ref();
let mut locked = data.write().unwrap();
locked.insert(key, val)
}
/// Delete a key
/// # Safety:
/// You MUST call init before.
pub unsafe fn delete<Q: ?Sized>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q> + Hash + Eq,
Q: Hash + Eq,
{
let data = (&*self.data.get()).as_ref();
let mut locked = data.write().unwrap();
locked.remove(key)
}
}
pub trait Expired {
fn expired(&self) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache() {
let cache = unsafe { Cache::<String, String>::new() };
cache.init();
unsafe {
cache.set("C".to_string(), "H".to_string());
assert_eq!(cache.get("C"), Some("H".to_string()));
cache.delete("C");
assert_eq!(cache.get("C"), None);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment