repr_rs/cache/
mod.rs

1pub(crate) mod lazy;
2#[cfg(feature = "eager")]
3pub mod eager;
4
5use crate::Repr;
6use downcast_rs::{impl_downcast, Downcast};
7use std::collections::BTreeMap;
8use std::fmt::{Debug, Display};
9use std::hash::{Hash, Hasher};
10use std::ops::{Deref, DerefMut};
11
12pub(crate) trait Cache<T>: Downcast {
13	fn notify(&self, _value: &T);
14}
15impl_downcast!(Cache<T>);
16
17/// Wraps a value and ensures that an invariant is maintained while allowing that value to be
18/// mutated. The invariant is checked after every mutation.
19/// Additionally, this struct allows for cacheable reads of the value. This is useful when the
20/// read function is expensive. By default, the caching is lazy, so after a value is read once that
21/// same read function will fetch the cached value unless the value has been mutated.
22///
23/// With the feature `eager` enabled, the [`crate::EagerCacheLookup`] trait is implemented for this struct
24/// and can be used to cache values eagerly. Whenever the value is mutated, all eager caches
25/// will be updated in parallel.
26/// 
27/// This struct requires that the value has a `'static` lifetime. If you need to store a value
28/// with a non-static lifetime consider using [`Repr`].
29pub struct CacheableRepr<T: Debug + 'static, I: Fn(&T) -> bool> {
30	inner: Repr<T, I>,
31	caches: BTreeMap<usize, Box<dyn Cache<T>>>,
32	eager_caches: BTreeMap<usize, Box<dyn Cache<T>>>,
33}
34impl<T: Debug + 'static, I: Fn(&T) -> bool> CacheableRepr<T, I> {
35	/// Creates a new representation invariant with the given value and invariant function.
36	/// ```rust
37	/// use repr_rs::CacheableRepr;
38	/// #[derive(Debug)]
39	/// struct MinMax { min: i32, max: i32 }
40	/// CacheableRepr::new(
41	///   MinMax { min: 1, max: 5 },
42	///   |mm| mm.min < mm.max,
43	/// );
44	/// ```
45	pub const fn new(inner: T, invariant: I) -> Self {
46		let repr = Repr::new(inner, invariant);
47		Self {
48			caches: BTreeMap::new(),
49			eager_caches: BTreeMap::new(),
50			inner: repr,
51		}
52	}
53	/// Creates a new representation invariant with the given value, invariant function, and violation message.
54	/// ```rust
55	/// use repr_rs::CacheableRepr;
56	/// #[derive(Debug)]
57	/// struct MinMax { min: i32, max: i32 }
58	/// CacheableRepr::with_msg(
59	///   MinMax { min: 1, max: 5 },
60	///   |mm| mm.min < mm.max,
61	///   "min must be less than max",
62	/// );
63	/// ```
64	pub const fn with_msg(inner: T, invariant: I, violation_message: &'static str) -> Self {
65		let repr = Repr::with_msg(inner, invariant, violation_message);
66		Self {
67			caches: BTreeMap::new(),
68			eager_caches: BTreeMap::new(),
69			inner: repr,
70		}
71	}
72	/// Borrows a read-only view of the value in the representation invariant.
73	/// ```rust
74	/// use repr_rs::CacheableRepr;
75	/// #[derive(Debug)]
76	/// struct MinMax { min: i32, max: i32 }
77	/// let repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
78	/// let view = repr.read();
79	/// assert_eq!(1, view.min);
80	/// assert_eq!(5, view.max);
81	/// ```
82	#[inline]
83	pub fn read(&self) -> &T {
84		// Safety: borrowing rules ensure that T is valid, and because this is an immutable borrow
85		// of the Repr, no mutable borrows can take place.
86		self.inner.read()
87	}
88	/// Borrows a mutable view of the value in the representation invariant.
89	/// ```rust
90	/// use repr_rs::CacheableRepr;
91	/// #[derive(Debug)]
92	/// struct MinMax { min: i32, max: i32 }
93	/// let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
94	/// {
95	///   let view = repr.read();
96	///   assert_eq!(1, view.min);
97	///   assert_eq!(5, view.max);
98	/// }
99	/// repr.write().min = 4;
100	/// let view = repr.read();
101	/// assert_eq!(4, view.min);
102	/// assert_eq!(5, view.max);
103	/// ```
104	///
105	/// Rust's borrowing rules prevent the read-only view being held while a mutation occurs. For
106	/// example, this won't compile:
107	/// ```compile_fail
108	/// use repr_rs::CacheableRepr;
109	/// #[derive(Debug)]
110	/// struct MinMax { min: i32, max: i32 }
111	/// let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
112	/// let view = repr.read();
113	/// assert_eq!(1, view.min);
114	/// assert_eq!(5, view.max);
115	/// // error[E0502]: cannot borrow `repr` as mutable because it is also borrowed as immutable
116	/// repr.write().min = 4;
117	/// assert_eq!(4, view.min);
118	/// assert_eq!(5, view.max);
119	/// ```
120	#[inline]
121	pub fn write(&mut self) -> ReprMutator<T, I> {
122		// Can be `const` when `const_mut_refs` is stabilised.
123		ReprMutator {
124			repr: self,
125		}
126	}
127	/// Consumes the representation invariant and returns the inner value.
128	/// ```rust
129	/// use repr_rs::Repr;
130	/// #[derive(Debug)]
131	/// struct MinMax { min: i32, max: i32 }
132	/// let repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
133	/// let inner = repr.into_inner();
134	/// assert_eq!(1, inner.min);
135	/// ```
136	#[inline]
137	pub fn into_inner(self) -> T {
138		self.inner.into_inner()
139	}
140	/// Borrows a read-only view of the value in the representation invariant and caches the
141	/// result of the read function. The cache is keyed by the read function's address, so in general
142	/// you should use function references instead of closures. It is a bug to perform any side effects
143	/// in the read function (i.e. reading from a file).
144	/// ```rust
145	/// use std::sync::atomic::{AtomicU32, Ordering};
146	/// use repr_rs::CacheableRepr;
147	/// #[derive(Debug)]
148	/// struct Person { name: String }
149	/// let mut repr = CacheableRepr::new(Person { name: "Alice and Bob together at last".into() }, |p| !p.name.is_empty());
150	/// static READ_SPY: AtomicU32 = AtomicU32::new(0);
151	/// fn expensive_read(p: &Person) -> usize {
152	///   // Just for demonstration purposes.
153	///   // Do not do side effects in your read functions!
154	///   READ_SPY.fetch_add(1, Ordering::Relaxed);
155	///   fib(p.name.len())
156	/// }
157	/// let fib_of_name_len = repr.lazy(expensive_read);
158	/// assert_eq!(832040, fib_of_name_len);
159	/// // this does not recompute the fibonacci number, it just gets it from the cache!
160	/// let fib_of_name_len2 = repr.lazy(expensive_read);
161	/// assert_eq!(832040, fib_of_name_len2);
162	/// repr.write().name = "Alice".into();
163	/// // this recomputes the fibonacci number because the name has changed
164	/// let fib_of_name_len3 = repr.lazy(expensive_read);
165	/// assert_eq!(5, fib_of_name_len3);
166	/// assert_eq!(2, READ_SPY.load(Ordering::Relaxed));
167	/// # fn fib(n: usize) -> usize {
168	/// #   if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }
169	/// # }
170	pub fn lazy<R: Clone + 'static>(&mut self, read_fn: fn(&T) -> R) -> R {
171		let fn_identity = read_fn as *const fn(&T) -> R as usize;
172		let entry = self.caches.entry(fn_identity);
173
174		let cache = entry.or_insert_with(|| Box::new(lazy::CacheableRead::<T, R>::new(read_fn)));
175		let cache = cache.downcast_mut::<lazy::CacheableRead<T, R>>().unwrap();
176		let data = self.inner.inner.get_mut();
177		cache.read(data)
178	}
179
180	fn check(&mut self) {
181		self.inner.check();
182		let data = self.inner.inner.get_mut();
183		for cache in self.caches.values().chain(self.eager_caches.values()) {
184			cache.notify(data);
185		}
186	}
187}
188impl<T: Debug + 'static, I: Fn(&T) -> bool> From<Repr<T, I>> for CacheableRepr<T, I> {
189	fn from(value: Repr<T, I>) -> Self {
190		Self {
191			caches: BTreeMap::new(),
192			eager_caches: BTreeMap::new(),
193			inner: value,
194		}
195	}
196}
197impl<T: Debug + 'static, I: Fn(&T) -> bool> From<CacheableRepr<T, I>> for Repr<T, I> {
198	fn from(value: CacheableRepr<T, I>) -> Self {
199		value.inner
200	}
201}
202/// # Safety
203/// This is safe because we can only mutate the inner value through the ReprMutator, which can only
204/// be created by borrowing the Repr mutably. The only other potential issue could be if the
205/// invariant function was not thread safe, which is why we require it to be [Sync].
206///
207/// The inner mutation (i.e. adding new caches or updating caches) either requires a mutable borrow
208/// or is guarded behind a lock.
209unsafe impl<T: Debug + Sync, I: Fn(&T) -> bool + Sync> Sync for CacheableRepr<T, I> {}
210/// # Safety
211/// We exclusively own all inner values here (both the repr and the caches), so we can safely
212/// implement Send for this type.
213unsafe impl<T: Debug + Send, I: Fn(&T) -> bool + Send> Send for CacheableRepr<T, I> {}
214impl<T: Debug, I: Fn(&T) -> bool> AsRef<T> for CacheableRepr<T, I> {
215	fn as_ref(&self) -> &T {
216		self.read()
217	}
218}
219impl<T: Debug + Clone, I: Fn(&T) -> bool + Clone> Clone for CacheableRepr<T, I> {
220	fn clone(&self) -> Self {
221		let clone = self.inner.clone();
222		Self::from(clone)
223	}
224}
225impl<T: Debug + Hash, I: Fn(&T) -> bool> Hash for CacheableRepr<T, I> {
226	fn hash<H: Hasher>(&self, state: &mut H) {
227		self.inner.hash(state);
228	}
229}
230impl<T: Debug + PartialEq, I: Fn(&T) -> bool> PartialEq for CacheableRepr<T, I> {
231	fn eq(&self, other: &Self) -> bool {
232		self.inner.eq(&other.inner)
233	}
234}
235impl<T: Debug + Eq, I: Fn(&T) -> bool> Eq for CacheableRepr<T, I> {}
236
237impl<T: Debug, I: Fn(&T) -> bool> Debug for CacheableRepr<T, I> {
238	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239		write!(f, "Repr({:?})", self.read())
240	}
241}
242impl <T: Debug + Display, I: Fn(&T) -> bool> Display for CacheableRepr<T, I> {
243	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244		write!(f, "{}", self.read())
245	}
246}
247
248#[repr(transparent)]
249pub struct ReprMutator<'a, T: Debug + 'static, I: Fn(&T) -> bool> {
250	// inner: &'a mut T,
251	repr: &'a mut CacheableRepr<T, I>,
252}
253impl<'a, T: Debug, I: Fn(&T) -> bool> Deref for ReprMutator<'a, T, I> {
254	type Target = T;
255	fn deref(&self) -> &Self::Target {
256		// Safety: borrowing rules ensure that T is valid, and because ReprMutate mutably borrows
257		// the Repr, no mutable borrows of the inner can take place if we borrow it as imm here.
258		unsafe { &*self.repr.inner.inner.get() }
259	}
260}
261impl<'a, T: Debug, I: Fn(&T) -> bool> AsRef<T> for ReprMutator<'a, T, I> {
262	fn as_ref(&self) -> &T {
263		self.deref()
264	}
265}
266impl<'a, T: Debug, I: Fn(&T) -> bool> DerefMut for ReprMutator<'a, T, I> {
267	fn deref_mut(&mut self) -> &mut Self::Target {
268		self.repr.inner.inner.get_mut()
269	}
270}
271impl<'a, T: Debug, I: Fn(&T) -> bool> AsMut<T> for ReprMutator<'a, T, I> {
272	fn as_mut(&mut self) -> &mut T {
273		self.deref_mut()
274	}
275}
276impl<T: Debug, I: Fn(&T) -> bool> Drop for ReprMutator<'_, T, I> {
277	fn drop(&mut self) {
278		self.repr.check();
279	}
280}
281
282// For Deref/DerefMut we need to make sure that it hashes, orders, and has equality with the
283// same semantics as the reference we give
284impl<'a, T: Debug + Hash> Hash for ReprMutator<'a, T, fn(&T) -> bool> {
285	fn hash<H: Hasher>(&self, state: &mut H) {
286		self.deref().hash(state);
287	}
288}
289impl<'a, T: Debug + PartialEq> PartialEq for ReprMutator<'a, T, fn(&T) -> bool> {
290	fn eq(&self, other: &Self) -> bool {
291		self.deref() == other.deref()
292	}
293}
294impl<'a, T: Debug + Eq> Eq for ReprMutator<'a, T, fn(&T) -> bool> {}
295impl<'a, T: Debug + PartialOrd> PartialOrd for ReprMutator<'a, T, fn(&T) -> bool> {
296	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
297		self.deref().partial_cmp(other.deref())
298	}
299}
300impl<'a, T: Debug + Ord> Ord for ReprMutator<'a, T, fn(&T) -> bool> {
301	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
302		self.deref().cmp(other.deref())
303	}
304}