Created
January 19, 2022 08:51
-
-
Save ihciah/10c886ab4fefeac227be006795a98319 to your computer and use it in GitHub Desktop.
Rust Global Cache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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