sequential_storage/cache/
array_impl.rs

1use core::num::NonZeroU32;
2
3use crate::{
4    PageState,
5    cache::{
6        CacheImpl, DirtTracker, Invalidate, KeyCacheImpl, PagePointersCache, PageStatesCache,
7        PrivateCacheImpl, PrivateKeyCacheImpl,
8        key_pointers::{CachedKeyPointers, KeyPointersCache, UncachedKeyPointers},
9        page_pointers::{CachedPagePointers, UncachedPagePointers},
10        page_states::CachedPageStates,
11    },
12    map::Key,
13};
14
15/// A cache object that keeps track of the page states.
16///
17/// This cache has to be kept around and passed to *every* api call to the same memory region until the cache gets discarded.
18///
19/// Valid usecase:\
20/// `Create cache 1` -> `use 1` -> `use 1` -> `create cache 2` -> `use 2` -> `use 2`
21///
22/// Invalid usecase:\
23/// `Create cache 1` -> `use 1` -> `create cache 2` -> `use 2` -> `❌ use 1 ❌`
24///
25/// Make sure the page count is correct. If the number is lower than the actual amount, the code will panic at some point.
26#[derive(Debug)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct PageStateCache<const PAGE_COUNT: usize> {
29    dirt_tracker: DirtTracker,
30    page_states: [Option<PageState>; PAGE_COUNT],
31}
32
33impl<const PAGE_COUNT: usize> PageStateCache<PAGE_COUNT> {
34    /// Construct a new instance
35    #[must_use]
36    pub const fn new() -> Self {
37        Self {
38            dirt_tracker: DirtTracker::new(),
39            page_states: [None; PAGE_COUNT],
40        }
41    }
42}
43
44impl<const PAGE_COUNT: usize> Default for PageStateCache<PAGE_COUNT> {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50impl<const PAGE_COUNT: usize> PrivateCacheImpl for PageStateCache<PAGE_COUNT> {
51    type PSC<'a>
52        = CachedPageStates<'a>
53    where
54        Self: 'a;
55    type PPC<'a>
56        = UncachedPagePointers
57    where
58        Self: 'a;
59
60    fn dirt_tracker<R>(&mut self, f: impl FnOnce(&mut DirtTracker) -> R) -> Option<R> {
61        Some(f(&mut self.dirt_tracker))
62    }
63
64    fn page_states(&mut self) -> Self::PSC<'_> {
65        CachedPageStates::new(&mut self.page_states)
66    }
67
68    fn page_pointers(&mut self) -> Self::PPC<'_> {
69        UncachedPagePointers
70    }
71}
72
73impl<const PAGE_COUNT: usize> CacheImpl for PageStateCache<PAGE_COUNT> {}
74impl<KEY: Key, const PAGE_COUNT: usize> KeyCacheImpl<KEY> for PageStateCache<PAGE_COUNT> {}
75
76impl<const PAGE_COUNT: usize> Invalidate for PageStateCache<PAGE_COUNT> {
77    fn invalidate_cache_state(&mut self) {
78        self.dirt_tracker.unmark_dirty();
79        self.page_states().invalidate_cache_state();
80        self.page_pointers().invalidate_cache_state();
81    }
82}
83
84impl<KEY: Key, const PAGE_COUNT: usize> PrivateKeyCacheImpl<KEY> for PageStateCache<PAGE_COUNT> {
85    type KPC<'a>
86        = UncachedKeyPointers
87    where
88        Self: 'a;
89
90    fn key_pointers(&mut self) -> Self::KPC<'_> {
91        UncachedKeyPointers
92    }
93}
94
95/// A cache object that keeps track of the page states and some pointers to the items in the page.
96///
97/// This cache has to be kept around and passed to *every* api call to the same memory region until the cache gets discarded.
98///
99/// Valid usecase:\
100/// `Create cache 1` -> `use 1` -> `use 1` -> `create cache 2` -> `use 2` -> `use 2`
101///
102/// Invalid usecase:\
103/// `Create cache 1` -> `use 1` -> `create cache 2` -> `use 2` -> `❌ use 1 ❌`
104///
105/// Make sure the page count is correct. If the number is lower than the actual amount, the code will panic at some point.
106#[derive(Debug)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub struct PagePointerCache<const PAGE_COUNT: usize> {
109    dirt_tracker: DirtTracker,
110    page_states: [Option<PageState>; PAGE_COUNT],
111    after_erased_pointers: [Option<NonZeroU32>; PAGE_COUNT],
112    after_written_pointers: [Option<NonZeroU32>; PAGE_COUNT],
113}
114
115impl<const PAGE_COUNT: usize> PagePointerCache<PAGE_COUNT> {
116    /// Construct a new instance
117    #[must_use]
118    pub const fn new() -> Self {
119        Self {
120            dirt_tracker: DirtTracker::new(),
121            page_states: [None; PAGE_COUNT],
122            after_erased_pointers: [None; PAGE_COUNT],
123            after_written_pointers: [None; PAGE_COUNT],
124        }
125    }
126}
127
128impl<const PAGE_COUNT: usize> Default for PagePointerCache<PAGE_COUNT> {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134impl<const PAGE_COUNT: usize> PrivateCacheImpl for PagePointerCache<PAGE_COUNT> {
135    type PSC<'a>
136        = CachedPageStates<'a>
137    where
138        Self: 'a;
139    type PPC<'a>
140        = CachedPagePointers<'a>
141    where
142        Self: 'a;
143
144    fn dirt_tracker<R>(&mut self, f: impl FnOnce(&mut DirtTracker) -> R) -> Option<R> {
145        Some(f(&mut self.dirt_tracker))
146    }
147
148    fn page_states(&mut self) -> Self::PSC<'_> {
149        CachedPageStates::new(&mut self.page_states)
150    }
151
152    fn page_pointers(&mut self) -> Self::PPC<'_> {
153        CachedPagePointers::new(
154            &mut self.after_erased_pointers,
155            &mut self.after_written_pointers,
156        )
157    }
158}
159
160impl<const PAGE_COUNT: usize> CacheImpl for PagePointerCache<PAGE_COUNT> {}
161impl<KEY: Key, const PAGE_COUNT: usize> KeyCacheImpl<KEY> for PagePointerCache<PAGE_COUNT> {}
162
163impl<const PAGE_COUNT: usize> Invalidate for PagePointerCache<PAGE_COUNT> {
164    fn invalidate_cache_state(&mut self) {
165        self.dirt_tracker.unmark_dirty();
166        self.page_states().invalidate_cache_state();
167        self.page_pointers().invalidate_cache_state();
168    }
169}
170
171impl<KEY: Key, const PAGE_COUNT: usize> PrivateKeyCacheImpl<KEY> for PagePointerCache<PAGE_COUNT> {
172    type KPC<'a>
173        = UncachedKeyPointers
174    where
175        Self: 'a;
176
177    fn key_pointers(&mut self) -> Self::KPC<'_> {
178        UncachedKeyPointers
179    }
180}
181
182/// An object that caches the location of the newest item with a given key.
183/// This cache also caches pages states and page pointers.
184///
185/// This cache has to be kept around and passed to *every* api call to the same memory region until the cache gets discarded.
186///
187/// Valid usecase:\
188/// `Create cache 1` -> `use 1` -> `use 1` -> `create cache 2` -> `use 2` -> `use 2`
189///
190/// Invalid usecase:\
191/// `Create cache 1` -> `use 1` -> `create cache 2` -> `use 2` -> `❌ use 1 ❌`
192///
193/// Make sure the page count is correct. If the number is lower than the actual amount, the code will panic at some point.
194///
195/// The number of key slots can be lower than the total amount of possible keys used, but this will lower
196/// the chance of a cache hit.
197/// The keys are cached in a fifo and any time its location is updated in cache it's added to the front.
198#[derive(Debug)]
199#[cfg_attr(feature = "defmt", derive(defmt::Format))]
200pub struct KeyPointerCache<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> {
201    dirt_tracker: DirtTracker,
202    page_states: [Option<PageState>; PAGE_COUNT],
203    after_erased_pointers: [Option<NonZeroU32>; PAGE_COUNT],
204    after_written_pointers: [Option<NonZeroU32>; PAGE_COUNT],
205    key_pointers: [Option<(KEY, NonZeroU32)>; KEYS],
206}
207
208impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> KeyPointerCache<PAGE_COUNT, KEY, KEYS> {
209    /// Construct a new instance
210    #[must_use]
211    pub const fn new() -> Self {
212        Self {
213            dirt_tracker: DirtTracker::new(),
214            page_states: [None; PAGE_COUNT],
215            after_erased_pointers: [None; PAGE_COUNT],
216            after_written_pointers: [None; PAGE_COUNT],
217            key_pointers: [const { None }; KEYS],
218        }
219    }
220}
221
222impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> Default
223    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
224{
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> PrivateCacheImpl
231    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
232{
233    type PSC<'a>
234        = CachedPageStates<'a>
235    where
236        Self: 'a;
237    type PPC<'a>
238        = CachedPagePointers<'a>
239    where
240        Self: 'a;
241
242    fn dirt_tracker<R>(&mut self, f: impl FnOnce(&mut DirtTracker) -> R) -> Option<R> {
243        Some(f(&mut self.dirt_tracker))
244    }
245
246    fn page_states(&mut self) -> Self::PSC<'_> {
247        CachedPageStates::new(&mut self.page_states)
248    }
249
250    fn page_pointers(&mut self) -> Self::PPC<'_> {
251        CachedPagePointers::new(
252            &mut self.after_erased_pointers,
253            &mut self.after_written_pointers,
254        )
255    }
256}
257
258impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> CacheImpl
259    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
260{
261}
262impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> KeyCacheImpl<KEY>
263    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
264{
265}
266
267impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> Invalidate
268    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
269{
270    fn invalidate_cache_state(&mut self) {
271        self.dirt_tracker.unmark_dirty();
272        self.page_states().invalidate_cache_state();
273        self.page_pointers().invalidate_cache_state();
274        self.key_pointers().invalidate_cache_state();
275    }
276}
277
278impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> PrivateKeyCacheImpl<KEY>
279    for KeyPointerCache<PAGE_COUNT, KEY, KEYS>
280{
281    type KPC<'a>
282        = CachedKeyPointers<'a, KEY>
283    where
284        Self: 'a;
285
286    fn key_pointers(&mut self) -> Self::KPC<'_> {
287        CachedKeyPointers::new(&mut self.key_pointers)
288    }
289}