fn_memo/
sync.rs

1#[cfg(feature = "concurrent_hash_map")]
2pub mod chashmap;
3pub mod rw_lock;
4
5use crate::FnMemo;
6use once_cell::sync::OnceCell;
7use recur_fn::RecurFn;
8use std::sync::Arc;
9
10/// The cache for synchronized memoization.
11pub trait Cache {
12    type Arg;
13    type Output;
14
15    /// Creates an empty cache.
16    fn new() -> Self;
17    /// Gets the cell of the `arg` in cache. Returns `None` if `arg` is not cached.
18    /// This method should only acquire read lock if needed.
19    fn get(&self, arg: &Self::Arg) -> Option<Arc<OnceCell<Self::Output>>>;
20    /// Gets the cell of the `arg` in cache, creates if `arg` is not cached.
21    /// This method may acquire write lock if needed.
22    fn get_or_new(&self, arg: Self::Arg) -> Arc<OnceCell<Self::Output>>;
23    /// Clears the cache.
24    fn clear(&self);
25}
26
27/// The synchronized implementation of `FnMemo`.
28pub struct Memo<C, F> {
29    cache: C,
30    f: F,
31}
32
33impl<C: Cache, F> Memo<C, F> {
34    /// Constructs a `Memo` using `C` as cache, caching function `f`.
35    pub fn new(f: F) -> Self {
36        Memo { cache: C::new(), f }
37    }
38}
39
40impl<C: Cache, F: RecurFn<C::Arg, C::Output>> FnMemo<C::Arg, C::Output> for Memo<C, F>
41where
42    C::Arg: Clone,
43    C::Output: Clone,
44{
45    fn call(&self, arg: C::Arg) -> C::Output {
46        self.cache
47            .get(&arg)
48            .unwrap_or_else(|| self.cache.get_or_new(arg.clone()))
49            .get_or_init(|| self.f.body(|arg| self.call(arg), arg))
50            .clone()
51    }
52
53    fn clear_cache(&self) {
54        self.cache.clear();
55    }
56}