penumbra_sdk_tct/internal/hash/
cache.rs

1//! A thread-safe cache intended hold lazily evaluated hashes.
2
3use std::fmt::Debug;
4
5use parking_lot::Mutex;
6
7use crate::prelude::*;
8
9/// An `Mutex`-based cache for hashes, to prevent repeated computation.
10#[derive(Default, Derivative)]
11pub struct CachedHash {
12    mutex: Mutex<OptionHash>,
13}
14
15impl Debug for CachedHash {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        if let Some(hash) = <Option<Hash>>::from(*self.mutex.lock()) {
18            write!(f, "{hash:?}")
19        } else {
20            write!(f, "_")
21        }
22    }
23}
24
25impl Clone for CachedHash {
26    fn clone(&self) -> Self {
27        Self {
28            mutex: Mutex::new(*self.mutex.lock()),
29        }
30    }
31}
32
33impl CachedHash {
34    /// Get the cached hash, or return `None` if it is not yet set.
35    pub fn get(&self) -> Option<Hash> {
36        (*self.mutex.lock()).into()
37    }
38
39    /// If the cache is empty, set its value using the closure, then return its contents regardless.
40    pub fn set_if_empty(&self, new: impl FnOnce() -> Hash) -> Hash {
41        let mut guard = self.mutex.lock();
42        if let Some(hash) = Option::from(*guard) {
43            hash
44        } else {
45            let new = new();
46            *guard = OptionHash::from(Some(new));
47            new
48        }
49    }
50
51    /// Reset the cached hash to empty.
52    pub fn clear(&self) {
53        *self.mutex.lock() = OptionHash::from(None);
54    }
55}
56
57impl From<Hash> for CachedHash {
58    fn from(hash: Hash) -> Self {
59        Self {
60            mutex: Mutex::new(OptionHash::from(Some(hash))),
61        }
62    }
63}
64
65#[cfg(test)]
66mod test {
67    use super::*;
68
69    #[test]
70    fn cached_hash_size() {
71        static_assertions::assert_eq_size!(CachedHash, [u8; 40]);
72    }
73}