sequential_storage/cache/
page_pointers.rs1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6use core::{fmt::Debug, num::NonZeroU32, ops::Range};
7
8use embedded_storage_async::nor_flash::NorFlash;
9
10use crate::{
11 NorFlashExt, PageState, calculate_page_address, calculate_page_index, item::ItemHeader,
12};
13
14pub(crate) trait SealedPagePointersCache {
15 fn first_item_after_erased(&self, page_index: usize) -> Option<u32>;
16 fn first_item_after_written(&self, page_index: usize) -> Option<u32>;
17
18 fn notice_item_written<S: NorFlash>(
19 &mut self,
20 flash_range: Range<u32>,
21 item_address: u32,
22 item_header: &ItemHeader,
23 );
24 fn notice_item_erased<S: NorFlash>(
25 &mut self,
26 flash_range: Range<u32>,
27 item_address: u32,
28 item_header: &ItemHeader,
29 );
30
31 fn notice_page_state(&mut self, page_index: usize, new_state: PageState);
32 fn invalidate_cache_state(&mut self);
33}
34
35#[allow(private_bounds)]
37pub trait PagePointersCache: SealedPagePointersCache {}
38
39#[derive(Debug)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct ArrayPagePointers<const PAGE_COUNT: usize> {
43 after_erased_pointers: [Option<NonZeroU32>; PAGE_COUNT],
44 after_written_pointers: [Option<NonZeroU32>; PAGE_COUNT],
45}
46
47impl<const PAGE_COUNT: usize> ArrayPagePointers<PAGE_COUNT> {
48 pub const fn new() -> Self {
50 Self {
51 after_erased_pointers: [None; _],
52 after_written_pointers: [None; _],
53 }
54 }
55
56 fn as_tracked_mut(&mut self) -> TrackedPagePointers<'_> {
57 TrackedPagePointers {
58 after_erased_pointers: &mut self.after_erased_pointers,
59 after_written_pointers: &mut self.after_written_pointers,
60 }
61 }
62}
63
64impl<const PAGE_COUNT: usize> Default for ArrayPagePointers<PAGE_COUNT> {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl<const PAGE_COUNT: usize> SealedPagePointersCache for ArrayPagePointers<PAGE_COUNT> {
71 fn first_item_after_erased(&self, page_index: usize) -> Option<u32> {
72 self.after_erased_pointers
73 .get(page_index)
74 .copied()
75 .flatten()
76 .map(NonZeroU32::get)
77 }
78
79 fn first_item_after_written(&self, page_index: usize) -> Option<u32> {
80 self.after_written_pointers
81 .get(page_index)
82 .copied()
83 .flatten()
84 .map(NonZeroU32::get)
85 }
86
87 fn notice_item_written<S: NorFlash>(
88 &mut self,
89 flash_range: Range<u32>,
90 item_address: u32,
91 item_header: &ItemHeader,
92 ) {
93 self.as_tracked_mut()
94 .notice_item_written::<S>(flash_range, item_address, item_header);
95 }
96
97 fn notice_item_erased<S: NorFlash>(
98 &mut self,
99 flash_range: Range<u32>,
100 item_address: u32,
101 item_header: &ItemHeader,
102 ) {
103 self.as_tracked_mut()
104 .notice_item_erased::<S>(flash_range, item_address, item_header);
105 }
106
107 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
108 self.as_tracked_mut()
109 .notice_page_state(page_index, new_state);
110 }
111
112 fn invalidate_cache_state(&mut self) {
113 self.as_tracked_mut().invalidate_cache_state();
114 }
115}
116impl<const PAGE_COUNT: usize> PagePointersCache for ArrayPagePointers<PAGE_COUNT> {}
117
118#[cfg(feature = "alloc")]
119#[derive(Debug)]
121#[cfg_attr(feature = "defmt", derive(defmt::Format))]
122pub struct HeapPagePointers {
123 after_erased_pointers: alloc::vec::Vec<Option<NonZeroU32>>,
124 after_written_pointers: alloc::vec::Vec<Option<NonZeroU32>>,
125}
126
127#[cfg(feature = "alloc")]
128impl HeapPagePointers {
129 pub fn new(page_count: usize) -> Self {
131 Self {
132 after_erased_pointers: alloc::vec![None; page_count],
133 after_written_pointers: alloc::vec![None; page_count],
134 }
135 }
136
137 fn as_tracked(&mut self) -> TrackedPagePointers<'_> {
138 TrackedPagePointers {
139 after_erased_pointers: &mut self.after_erased_pointers,
140 after_written_pointers: &mut self.after_written_pointers,
141 }
142 }
143}
144
145#[cfg(feature = "alloc")]
146impl SealedPagePointersCache for HeapPagePointers {
147 fn first_item_after_erased(&self, page_index: usize) -> Option<u32> {
148 self.after_erased_pointers
149 .get(page_index)
150 .copied()
151 .flatten()
152 .map(NonZeroU32::get)
153 }
154
155 fn first_item_after_written(&self, page_index: usize) -> Option<u32> {
156 self.after_written_pointers
157 .get(page_index)
158 .copied()
159 .flatten()
160 .map(NonZeroU32::get)
161 }
162
163 fn notice_item_written<S: NorFlash>(
164 &mut self,
165 flash_range: Range<u32>,
166 item_address: u32,
167 item_header: &ItemHeader,
168 ) {
169 self.as_tracked()
170 .notice_item_written::<S>(flash_range, item_address, item_header);
171 }
172
173 fn notice_item_erased<S: NorFlash>(
174 &mut self,
175 flash_range: Range<u32>,
176 item_address: u32,
177 item_header: &ItemHeader,
178 ) {
179 self.as_tracked()
180 .notice_item_erased::<S>(flash_range, item_address, item_header);
181 }
182
183 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
184 self.as_tracked().notice_page_state(page_index, new_state);
185 }
186
187 fn invalidate_cache_state(&mut self) {
188 self.as_tracked().invalidate_cache_state();
189 }
190}
191#[cfg(feature = "alloc")]
192impl PagePointersCache for HeapPagePointers {}
193
194pub(crate) struct TrackedPagePointers<'a> {
197 after_erased_pointers: &'a mut [Option<NonZeroU32>],
198 after_written_pointers: &'a mut [Option<NonZeroU32>],
199}
200
201impl SealedPagePointersCache for TrackedPagePointers<'_> {
202 fn first_item_after_erased(&self, page_index: usize) -> Option<u32> {
203 self.after_erased_pointers
204 .get(page_index)
205 .copied()
206 .flatten()
207 .map(NonZeroU32::get)
208 }
209
210 fn first_item_after_written(&self, page_index: usize) -> Option<u32> {
211 self.after_written_pointers
212 .get(page_index)
213 .copied()
214 .flatten()
215 .map(NonZeroU32::get)
216 }
217
218 fn notice_item_written<S: NorFlash>(
219 &mut self,
220 flash_range: Range<u32>,
221 item_address: u32,
222 item_header: &ItemHeader,
223 ) {
224 let page_index = calculate_page_index::<S>(flash_range, item_address);
225
226 let next_item_address = item_header.next_item_address::<S>(item_address);
227
228 if let Some(first_item_after_written) = self.first_item_after_written(page_index) {
230 if next_item_address <= first_item_after_written {
231 return;
232 }
233 }
234
235 if let Some(after_written_pointer) = self.after_written_pointers.get_mut(page_index) {
236 *after_written_pointer = NonZeroU32::new(next_item_address);
237 }
238 }
239
240 fn notice_item_erased<S: NorFlash>(
241 &mut self,
242 flash_range: Range<u32>,
243 item_address: u32,
244 item_header: &ItemHeader,
245 ) {
246 let page_index = calculate_page_index::<S>(flash_range.clone(), item_address);
247
248 let next_unerased_item = self.first_item_after_erased(page_index).unwrap_or_else(|| {
250 calculate_page_address::<S>(flash_range, page_index) + S::WORD_SIZE as u32
251 });
252
253 if item_address == next_unerased_item {
254 if let Some(after_erased_pointer) = self.after_erased_pointers.get_mut(page_index) {
255 *after_erased_pointer =
256 NonZeroU32::new(item_header.next_item_address::<S>(item_address));
257 }
258 }
259 }
260
261 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
262 if new_state.is_open() {
263 if let Some(after_erased_pointer) = self.after_erased_pointers.get_mut(page_index) {
265 *after_erased_pointer = None;
266 }
267 if let Some(after_written_pointer) = self.after_written_pointers.get_mut(page_index) {
268 *after_written_pointer = None;
269 }
270 }
271 }
272
273 fn invalidate_cache_state(&mut self) {
274 for pointers in self.after_erased_pointers.iter_mut() {
275 *pointers = None;
276 }
277 for pointers in self.after_written_pointers.iter_mut() {
278 *pointers = None;
279 }
280 }
281}