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}