sequential_storage/cache/
mod.rs

1//! Module implementing all things cache related
2
3use core::{fmt::Debug, ops::Range};
4
5use embedded_storage_async::nor_flash::NorFlash;
6
7use crate::{PageState, item::ItemHeader, map::Key};
8
9use self::{
10    key_pointers::{KeyPointersCache, UncachedKeyPointers},
11    page_pointers::UncachedPagePointers,
12    page_states::UncachedPageStates,
13};
14
15pub(crate) mod array_impl;
16#[cfg(feature = "alloc")]
17pub(crate) mod heap_impl;
18pub(crate) mod key_pointers;
19pub(crate) mod page_pointers;
20pub(crate) mod page_states;
21
22mod tests;
23
24pub use array_impl::*;
25#[cfg(feature = "alloc")]
26pub use heap_impl::*;
27pub(crate) use page_pointers::PagePointersCache;
28pub(crate) use page_states::PageStatesCache;
29
30/// Trait implemented by all cache types
31#[allow(private_bounds)]
32pub trait CacheImpl: PrivateCacheImpl {}
33
34/// Trait implemented by all cache types that know about keys
35#[allow(private_bounds)]
36pub trait KeyCacheImpl<KEY: Key>: CacheImpl + PrivateKeyCacheImpl<KEY> {}
37
38pub(crate) trait Invalidate {
39    fn invalidate_cache_state(&mut self);
40}
41
42pub(crate) trait PrivateCacheImpl: Invalidate {
43    type PSC<'a>: PageStatesCache
44    where
45        Self: 'a;
46    type PPC<'a>: PagePointersCache
47    where
48        Self: 'a;
49
50    fn dirt_tracker<R>(&mut self, f: impl FnOnce(&mut DirtTracker) -> R) -> Option<R>;
51    fn page_states(&mut self) -> Self::PSC<'_>;
52    fn page_pointers(&mut self) -> Self::PPC<'_>;
53
54    /// True if the cache might be inconsistent
55    fn is_dirty(&mut self) -> bool {
56        self.dirt_tracker(|d| d.is_dirty()).unwrap_or_default()
57    }
58
59    /// Mark the cache as potentially inconsistent with reality
60    fn mark_dirty(&mut self) {
61        self.dirt_tracker(DirtTracker::mark_dirty);
62    }
63
64    /// Mark the cache as being consistent with reality
65    fn unmark_dirty(&mut self) {
66        self.dirt_tracker(DirtTracker::unmark_dirty);
67    }
68
69    /// Get the cache state of the requested page
70    fn get_page_state(&mut self, page_index: usize) -> Option<PageState> {
71        self.page_states().get_page_state(page_index)
72    }
73
74    /// Let the cache know a page state changed
75    ///
76    /// The dirty flag should be true if the page state is actually going to be changed.
77    /// Keep it false if we're only discovering the state.
78    fn notice_page_state(&mut self, page_index: usize, new_state: PageState, dirty: bool) {
79        if dirty {
80            self.mark_dirty();
81        }
82        self.page_states().notice_page_state(page_index, new_state);
83        self.page_pointers()
84            .notice_page_state(page_index, new_state);
85    }
86
87    /// Get the cached address of the first non-erased item in the requested page.
88    ///
89    /// This is purely for the items that get erased from start to end.
90    fn first_item_after_erased(&mut self, page_index: usize) -> Option<u32> {
91        self.page_pointers().first_item_after_erased(page_index)
92    }
93
94    /// Get the cached address of the first free unwritten item in the requested page.
95    fn first_item_after_written(&mut self, page_index: usize) -> Option<u32> {
96        self.page_pointers().first_item_after_written(page_index)
97    }
98
99    /// Let the cache know that an item has been written to flash
100    fn notice_item_written<S: NorFlash>(
101        &mut self,
102        flash_range: Range<u32>,
103        item_address: u32,
104        item_header: &ItemHeader,
105    ) {
106        self.mark_dirty();
107        self.page_pointers()
108            .notice_item_written::<S>(flash_range, item_address, item_header);
109    }
110
111    /// Let the cache know that an item has been erased from flash
112    fn notice_item_erased<S: NorFlash>(
113        &mut self,
114        flash_range: Range<u32>,
115        item_address: u32,
116        item_header: &ItemHeader,
117    ) {
118        self.mark_dirty();
119        self.page_pointers()
120            .notice_item_erased::<S>(flash_range, item_address, item_header);
121    }
122}
123
124pub(crate) trait PrivateKeyCacheImpl<KEY: Key>: PrivateCacheImpl {
125    type KPC<'a>: KeyPointersCache<KEY>
126    where
127        Self: 'a;
128
129    fn key_pointers(&mut self) -> Self::KPC<'_>;
130
131    fn key_location(&mut self, key: &KEY) -> Option<u32> {
132        self.key_pointers().key_location(key)
133    }
134
135    fn notice_key_location(&mut self, key: &KEY, item_address: u32, dirty: bool) {
136        if dirty {
137            self.mark_dirty();
138        }
139        self.key_pointers().notice_key_location(key, item_address);
140    }
141    #[allow(unused)]
142    fn notice_key_erased(&mut self, key: &KEY) {
143        self.mark_dirty();
144        self.key_pointers().notice_key_erased(key);
145    }
146}
147
148#[derive(Debug)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150pub(crate) struct DirtTracker {
151    /// Managed from the library code.
152    ///
153    /// When true, the cache and/or flash has changed and things might not be fully
154    /// consistent if there's an early return due to error.
155    dirty: bool,
156}
157
158impl DirtTracker {
159    pub const fn new() -> Self {
160        DirtTracker { dirty: false }
161    }
162
163    /// True if the cache might be inconsistent
164    pub fn is_dirty(&self) -> bool {
165        self.dirty
166    }
167
168    /// Mark the cache as potentially inconsistent with reality
169    pub fn mark_dirty(&mut self) {
170        self.dirty = true;
171    }
172
173    /// Mark the cache as being consistent with reality
174    pub fn unmark_dirty(&mut self) {
175        self.dirty = false;
176    }
177}
178
179/// A cache object implementing no cache.
180///
181/// This type of cache doesn't have to be kept around and may be constructed on every api call.
182/// You could simply pass `&mut NoCache::new()` every time.
183#[derive(Debug)]
184#[cfg_attr(feature = "defmt", derive(defmt::Format))]
185pub struct NoCache;
186
187impl NoCache {
188    /// Construct a new instance
189    #[must_use]
190    pub const fn new() -> Self {
191        Self
192    }
193}
194
195impl Default for NoCache {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201impl PrivateCacheImpl for NoCache {
202    type PSC<'a>
203        = UncachedPageStates
204    where
205        Self: 'a;
206    type PPC<'a>
207        = UncachedPagePointers
208    where
209        Self: 'a;
210
211    fn dirt_tracker<R>(&mut self, _f: impl FnOnce(&mut DirtTracker) -> R) -> Option<R> {
212        // We have no state, so no need to track dirtyness
213        None
214    }
215
216    fn page_states(&mut self) -> Self::PSC<'_> {
217        UncachedPageStates
218    }
219
220    fn page_pointers(&mut self) -> Self::PPC<'_> {
221        UncachedPagePointers
222    }
223}
224
225impl CacheImpl for NoCache {}
226impl<KEY: Key> KeyCacheImpl<KEY> for NoCache {}
227
228impl Invalidate for NoCache {
229    fn invalidate_cache_state(&mut self) {}
230}
231
232impl<KEY: Key> PrivateKeyCacheImpl<KEY> for NoCache {
233    type KPC<'a>
234        = UncachedKeyPointers
235    where
236        Self: 'a;
237
238    fn key_pointers(&mut self) -> Self::KPC<'_> {
239        UncachedKeyPointers
240    }
241}