#![doc = include_str!("../README.md")]
pub mod raw;
use parking_lot::RwLock;
use type_key::TypeKey;
use std::cell::UnsafeCell;
use crate::raw::RawStore;
#[derive(Debug, Default)]
pub struct LocalFnStore<'a>(UnsafeCell<RawStore<'a>>);
impl<'a> LocalFnStore<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn get_ptr<T: 'a + Send>(&self, key_fn: impl FnOnce() -> T) -> *const T {
let key = TypeKey::of_val(&key_fn);
if let Some(ptr) = unsafe { &*self.0.get().cast_const() }.get(&key) {
return ptr;
}
let value = key_fn();
unsafe { &mut *self.0.get() }.insert(key, value)
}
pub fn get<T: 'a + Send>(&self, key: impl FnOnce() -> T) -> &T {
unsafe { &*self.get_ptr(key) }
}
pub fn get_mut<T: 'a + Send>(&mut self, key: impl FnOnce() -> T) -> &mut T {
unsafe { &mut *self.get_ptr(key).cast_mut() }
}
pub fn reset(&mut self) {
self.0.get_mut().reset();
}
}
unsafe impl Send for LocalFnStore<'_> {}
#[derive(Debug, Default)]
pub struct LocalOnlyFnStore<'a>(UnsafeCell<RawStore<'a>>);
impl<'a> LocalOnlyFnStore<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn get_ptr<T: 'a + Send>(&self, key_fn: impl FnOnce() -> T) -> *const T {
let key = TypeKey::of_val(&key_fn);
if let Some(ptr) = unsafe { &*self.0.get().cast_const() }.get(&key) {
return ptr;
}
let value = key_fn();
unsafe { &mut *self.0.get() }.insert(key, value)
}
pub fn get<T: 'a + Send>(&self, key: impl FnOnce() -> T) -> &T {
unsafe { &*self.get_ptr(key) }
}
pub fn get_mut<T: 'a + Send>(&mut self, key: impl FnOnce() -> T) -> &mut T {
unsafe { &mut *self.get_ptr(key).cast_mut() }
}
pub fn reset(&mut self) {
self.0.get_mut().reset();
}
}
#[derive(Debug, Default)]
pub struct AtomicFnStore<'a>(RwLock<RawStore<'a>>);
impl<'a> AtomicFnStore<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn get_ptr<T: 'a + Send + Sync>(&self, key_fn: impl FnOnce() -> T) -> *const T {
let key = TypeKey::of_val(&key_fn);
if let Some(ptr) = self.0.read().get(&key) {
return ptr;
}
let value = key_fn();
self.0.write().insert(key, value)
}
pub fn get<T: 'a + Send + Sync>(&self, key_fn: impl FnOnce() -> T) -> &T {
unsafe { &*self.get_ptr(key_fn) }
}
pub fn get_mut<T: 'a + Send + Sync, F>(&mut self, key_fn: impl FnOnce() -> T) -> &mut T {
unsafe { &mut *self.get_ptr(key_fn).cast_mut() }
}
pub fn reset(&mut self) {
self.0.get_mut().reset();
}
}
unsafe impl Send for AtomicFnStore<'_> {}
unsafe impl Sync for AtomicFnStore<'_> {}
#[cfg(test)]
mod tests {
use crate::LocalOnlyFnStore;
use super::{AtomicFnStore, LocalFnStore};
#[test]
fn test_trait() {
const fn is_send<T: Send>() {}
const fn is_sync<T: Sync>() {}
is_send::<LocalFnStore>();
is_send::<AtomicFnStore>();
is_sync::<AtomicFnStore>();
}
#[test]
fn test_local() {
let store = LocalFnStore::new();
fn one() -> i32 {
1
}
let b = store.get(|| store.get(one) + 1);
let a = store.get(one);
assert_eq!(*b, 2);
assert_eq!(*a, 1);
}
#[test]
fn test_local_only() {
let store = LocalOnlyFnStore::new();
fn one() -> i32 {
1
}
let b = store.get(|| store.get(one) + 1);
let a = store.get(one);
assert_eq!(*b, 2);
assert_eq!(*a, 1);
}
#[test]
fn test_atomic() {
let store = AtomicFnStore::new();
fn one() -> i32 {
1
}
let b = store.get(|| store.get(one) + 1);
let a = store.get(one);
assert_eq!(*b, 2);
assert_eq!(*a, 1);
}
}