orx_v/cached/
cached_vec.rs

1use super::cache::{Cache, DefaultCache};
2use crate::common_trait_helpers::debug::*;
3use crate::{NVec, dim::*};
4use core::fmt::Debug;
5use core::{cell::UnsafeCell, marker::PhantomData};
6
7/// Wraps an `NVec<D, T>` into a cached vector which maintains an internal cache.
8///
9/// Each time an element is requested, the vector first checks the cache:
10/// * if the value is readily available in the cache, the vector returns it,    
11/// * otherwise, it computes its value, caches it for future use and returns it.
12///
13/// The cache is often a simple lookup or map, such as the HashMap. However, it might
14/// be more advanced such as the least frequently used cache. Any internal structure
15/// implementing the [`Cache`] can be used.
16///
17/// The aim of the cached vector is to improve the execution time where computing an
18/// element is expensive. Consider for instance element (i, j) corresponds to the
19/// duration between two addresses i and j on a street network which requires running
20/// an algorithm to compute. Further assume that:
21/// * pre-computing all elements is not justified as we will access only a small portion
22///   of the entire matrix, and
23/// * we do not know ahead of time which indices that we will access.
24///
25/// In such scenarios, [`IntoCached`] trait makes it very convenient to convert a functional
26/// vector into a cached functional vector.
27///
28/// # Examples
29///
30/// ```
31/// use orx_v::*;
32///
33/// // assume an expensive api call to compute distance
34/// fn api_call_to_get_distance(from: usize, to: usize) -> u64 {
35///     match from > to {
36///         true => (from - to) as u64,
37///         false => (to - from) as u64,
38///     }
39/// }
40///
41/// let v2 = V.d2().fun(|[i, j]| api_call_to_get_distance(i, j)).into_cached();
42/// assert_eq!(v2.cache_len(), 0);
43///
44/// // makes the api call; caches and returns the value
45/// assert_eq!(v2.at([0, 3]), 3);
46/// assert_eq!(v2.cache_len(), 1);
47///
48/// // does not repeat the api call; returns the value from the cache
49/// assert_eq!(v2.at([0, 3]), 3);
50/// ```
51///
52/// [`IntoCached`]: crate::IntoCached
53pub struct CachedVec<D, T, V, C = DefaultCache<D, T>>
54where
55    D: Dim,
56    V: NVec<D, T>,
57    C: Cache<D::Idx, T>,
58    T: Copy,
59{
60    pub(super) vec: V,
61    pub(super) cache: UnsafeCell<C>,
62    phantom: PhantomData<(D, T)>,
63}
64
65impl<D, T, V, C> CachedVec<D, T, V, C>
66where
67    D: Dim,
68    V: NVec<D, T>,
69    C: Cache<D::Idx, T>,
70    T: Copy,
71{
72    #[allow(clippy::mut_from_ref)]
73    #[inline(always)]
74    pub(super) unsafe fn entry_or_insert_with(&self, idx: impl IntoIdx<D>) -> &mut T {
75        let cache = unsafe { &mut *self.cache.get() };
76        cache.entry_or_insert_with(idx.into_idx(), |idx| self.vec.at(idx))
77    }
78
79    pub(crate) fn new(vec: V, cache: C) -> Self {
80        Self {
81            vec,
82            cache: cache.into(),
83            phantom: PhantomData,
84        }
85    }
86
87    /// Destructs the cached vec and returns the tuple of the underlying `NVec<D, T>`
88    /// and cache.
89    ///
90    /// Note that a new cached vector can be constructed by re-using the cache by
91    /// calling the [`into_cached_with`] method on the vec.
92    ///
93    /// [`into_cached_with`]: `crate::IntoCached::into_cached_with`
94    pub fn into_inner(self) -> (V, C) {
95        (self.vec, self.cache.into_inner())
96    }
97
98    /// Clears the internal cache of the cached vector; i.e., forgets all cached
99    /// elements.
100    pub fn clean_cache(&mut self) {
101        let cache = unsafe { &mut *self.cache.get() };
102        cache.clear();
103    }
104
105    /// Returns the number of elements which are currently available in the cache.
106    pub fn cache_len(&self) -> usize {
107        let cache = unsafe { &*self.cache.get() };
108        cache.len()
109    }
110}
111
112macro_rules! impl_debug {
113    ($dim:ty, $dbg_fn:ident) => {
114        impl<T, V, C> Debug for CachedVec<$dim, T, V, C>
115        where
116            V: NVec<$dim, T>,
117            C: Cache<<$dim as Dim>::Idx, T>,
118            T: Copy + Debug,
119            Self: NVec<$dim, T>,
120        {
121            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122                write!(
123                    f,
124                    "{{ kind: CachedVec, dim: D{}, is_bounded: {}, cache_len: {}, values: ",
125                    <$dim as Dim>::dimension(),
126                    self.is_bounded(),
127                    self.cache_len(),
128                )?;
129                $dbg_fn(f, self)?;
130                write!(f, " }}")
131            }
132        }
133    };
134}
135
136impl_debug!(D1, dbg_values_d1);
137impl_debug!(D2, dbg_values_d2);
138impl_debug!(D3, dbg_values_d3);
139impl_debug!(D4, dbg_values_d4);