fn_cache/
generic_cache.rs

1use crate::container::{
2	ContainerClear, ContainerLen, ContainerRemove, ContainerReserve, SparseContainer,
3};
4use crate::{FnCache, FnCacheMany};
5
6/// A generic cache for a function backed by anything that implements the [`SparseContainer`]
7/// trait.
8///
9/// Not all containers may support the precise access pattern, such as [`VecCache`] which
10/// implements [`FnCache`] directly, but most uses can simply implement [`SparseContainer`] for
11/// their container and immediately have everything working with [`GenericCache`].
12///
13/// The cache takes ownership of all inputs, but only passes a reference to the function, allowing
14/// it to store the input in the cache without any copies or clones. Additionally, the function is
15/// shared using by using a [`RefCache`] when actually calling the function, preventing any
16/// reference counting or clones of the closure.
17pub struct GenericCache<'f, C: SparseContainer> {
18	pub(crate) cache: C,
19	f: Box<dyn Fn(&mut RefCache<C>, &C::Input) -> C::Output + Send + 'f>,
20}
21
22impl<'f, C: SparseContainer> GenericCache<'f, C> {
23	/// Create a `GenericCache` out of a cache and a function.
24	///
25	/// Using this function you can pre-initialize some values into the cache if desired, change
26	/// settings using specific constructors on the cache type, or any variation. If a default
27	/// version of the cache is sufficient for your needs, [`Self::new`] may be less verbose.
28	///
29	/// ```
30	/// # use fn_cache::GenericCache;
31	/// # use std::collections::HashMap;
32	/// let cache = GenericCache::with_cache(HashMap::<usize, usize>::new(), |x: &usize| *x);
33	/// ```
34	pub fn with_cache(cache: C, f: impl Fn(&C::Input) -> C::Output + Send + 'f) -> Self {
35		Self {
36			cache,
37			f: Box::new(move |_, i| f(i)),
38		}
39	}
40
41	/// Create a `GenericCache` out of a cache and a recursive function.
42	///
43	/// Using this function you can pre-initialize some values into the cache if desired, change
44	/// settings using specific constructors on the cache type, or any variation. If a default
45	/// version of the cache is sufficient for your needs, [`Self::recursive`] may be less verbose.
46	///
47	/// ```
48	/// # use fn_cache::{FnCacheMany, GenericCache};
49	/// # use std::collections::HashMap;
50	/// let cache = GenericCache::recursive_with_cache(HashMap::<usize, usize>::new(), |cache, x| match x {
51	///     0 => 1,
52	///     1 => 1,
53	///     _ => cache.get_many([x - 1, x - 2]).into_iter().sum()
54	/// });
55	/// ```
56	pub fn recursive_with_cache(
57		cache: C,
58		f: impl Fn(&mut RefCache<C>, &C::Input) -> C::Output + Send + 'f,
59	) -> Self {
60		Self {
61			cache,
62			f: Box::new(f),
63		}
64	}
65
66	/// Get a reference to the underlying cache object, letting you use functions exclusive to the
67	/// cache type (as long they only need `&self` of course).
68	pub fn cache(&self) -> &C {
69		&self.cache
70	}
71}
72
73impl<'f, C> GenericCache<'f, C>
74where
75	C: SparseContainer + Default,
76{
77	/// Create a `GenericCache` using the `Default` implementation of the [`Cache`] type.
78	///
79	/// If a specific instance of a cache is required, see [`Self::with_cache`].
80	///
81	/// ```
82	/// # use fn_cache::GenericCache;
83	/// # use std::collections::HashMap;
84	/// let cache: GenericCache<HashMap<_,_>> = GenericCache::new(|x: &usize| *x);
85	/// ```
86	pub fn new(f: impl Fn(&C::Input) -> C::Output + Send + 'f) -> Self {
87		Self::with_cache(Default::default(), f)
88	}
89
90	/// Create a `GenericCache` using the `Default` implementation of the [`Cache`] type, using a
91	/// recursive function.
92	///
93	/// If a specific instance of a cache is required, see [`Self::recursive_with_cache`].
94	///
95	/// ```
96	/// # use fn_cache::{FnCacheMany, GenericCache};
97	/// # use std::collections::HashMap;
98	/// let cache: GenericCache<HashMap<usize, u64>> = GenericCache::recursive(|cache, x| match x {
99	///     0 => 1,
100	///     1 => 1,
101	///     _ => cache.get_many([x - 1, x - 2]).into_iter().sum()
102	/// });
103	/// ```
104	///
105	/// # Issues
106	/// Currently it does not work if you pass in a recursive function generic over [`FnCache`] via
107	/// a function pointer. Wrap the pointer in a closure.
108	///
109	/// ```
110	/// # use fn_cache::{FnCache, GenericCache};
111	/// # use std::collections::HashMap;
112	/// fn increment(cache: &mut impl FnCache<usize, usize>, x: &usize) -> usize {
113	///     match x {
114	///         0 => 0,
115	///         _ => cache.get(x - 1) + 1,
116	///     }
117	/// }
118	///
119	/// // no good
120	/// //let cache: GenericCache<HashMap<_, _>> = GenericCache::recursive(increment);
121	/// //okay
122	/// let cache: GenericCache<HashMap<_, _>> = GenericCache::recursive(|c, i| increment(c, i));
123	/// ```
124	pub fn recursive(f: impl Fn(&mut RefCache<C>, &C::Input) -> C::Output + Send + 'f) -> Self {
125		Self::recursive_with_cache(Default::default(), f)
126	}
127}
128
129impl<'f, C: SparseContainer + ContainerLen> GenericCache<'f, C> {
130	/// Returns the number of elements in the cache.
131	pub fn len(&self) -> usize {
132		self.cache.len()
133	}
134}
135
136impl<'f, C: SparseContainer + ContainerClear> GenericCache<'f, C> {
137	/// Clears the cache, removing all key-value pairs.
138	/// Keeps the allocated memory for reuse.
139	pub fn clear(&mut self) {
140		self.cache.clear()
141	}
142}
143
144impl<'f, C: SparseContainer + ContainerReserve> GenericCache<'f, C> {
145	/// Reserves capacity for at least `additional` more elements
146	/// to be inserted in the cache. The collection may
147	/// reserve more space to avoid frequent reallocations.
148	pub fn reserve(&mut self, additional: usize) {
149		self.cache.reserve(additional)
150	}
151}
152
153impl<'f, C: ContainerRemove> GenericCache<'f, C> {
154	/// Removes the input from the cache, returning any value
155	/// if the input was previously in the cache.
156	pub fn remove(&mut self, input: &C::Input) -> Option<C::Output> {
157		self.cache.remove(input)
158	}
159}
160
161impl<'f, C: SparseContainer> FnCache<C::Input, C::Output> for GenericCache<'f, C> {
162	fn get(&mut self, input: C::Input) -> &C::Output {
163		if self.cache.has(&input) {
164			self.cache.get(&input).unwrap()
165		} else {
166			let mut ref_cache = RefCache::new(&mut self.cache, self.f.as_ref());
167			let output = (self.f)(&mut ref_cache, &input);
168			self.cache.put(input, output)
169		}
170	}
171}
172
173impl<'f, C> FnCacheMany<C::Input, C::Output> for GenericCache<'f, C>
174where
175	C: SparseContainer,
176	C::Input: Clone,
177{
178	fn get_many<const N: usize>(&mut self, inputs: [C::Input; N]) -> [&C::Output; N] {
179		for i in &inputs {
180			self.get(i.clone());
181		}
182
183		inputs.map(|i| self.cache.get(&i).unwrap())
184	}
185}
186
187pub struct RefCache<'c, C: SparseContainer> {
188	pub(crate) cache: &'c mut C,
189	f: &'c (dyn Fn(&mut Self, &C::Input) -> C::Output + Send),
190}
191
192impl<'c, C: SparseContainer> RefCache<'c, C> {
193	pub fn new(
194		cache: &'c mut C,
195		f: &'c (dyn Fn(&mut Self, &C::Input) -> C::Output + Send),
196	) -> Self {
197		Self { cache, f }
198	}
199}
200
201impl<'c, C> FnCache<C::Input, C::Output> for RefCache<'c, C>
202where
203	C: SparseContainer,
204{
205	fn get(&mut self, input: C::Input) -> &C::Output {
206		if self.cache.has(&input) {
207			self.cache.get(&input).unwrap()
208		} else {
209			let output = (self.f)(self, &input);
210			self.cache.put(input, output)
211		}
212	}
213}
214
215impl<'c, C> FnCacheMany<C::Input, C::Output> for RefCache<'c, C>
216where
217	C: SparseContainer,
218	C::Input: Clone,
219{
220	fn get_many<const N: usize>(&mut self, inputs: [C::Input; N]) -> [&C::Output; N] {
221		for i in &inputs {
222			self.get(i.clone());
223		}
224
225		inputs.map(|i| self.cache.get(&i).unwrap())
226	}
227}