sequential_storage/cache/
page_states.rs1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6use core::fmt::Debug;
7
8use crate::PageState;
9
10pub(crate) trait SealedPageStatesCache {
11 fn get_page_state(&self, page_index: usize) -> Option<PageState>;
12 fn notice_page_state(&mut self, page_index: usize, new_state: PageState);
13 fn invalidate_cache_state(&mut self);
14}
15
16#[allow(private_bounds)]
18pub trait PageStatesCache: SealedPageStatesCache {}
19
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub struct CalculatedPageStates {
44 page_count: usize,
45 anchor: Option<usize>,
46 anchor_is_partial: bool,
47 anchor_is_guess: bool,
48 closed_pages: usize,
49 open_pages: usize,
50}
51
52impl Debug for CalculatedPageStates {
53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 write!(f, "[")?;
55 for i in 0..self.page_count {
56 write!(
57 f,
58 "{}{i}: {:?}",
59 if i == 0 { "" } else { ", " },
60 self.get_page_state(i)
61 )?;
62 }
63 write!(f, "]")?;
64
65 Ok(())
66 }
67}
68
69impl CalculatedPageStates {
70 pub const fn new(page_count: usize) -> Self {
72 Self {
73 page_count,
74 anchor: None,
75 anchor_is_partial: false,
76 anchor_is_guess: true,
77 closed_pages: 0,
78 open_pages: 0,
79 }
80 }
81
82 fn places_in_front_of_anchor(&self, page_index: usize) -> Option<usize> {
83 let anchor = self.anchor?;
84
85 if page_index >= anchor {
86 Some(page_index - anchor)
87 } else {
88 Some(self.page_count - anchor + page_index)
89 }
90 }
91
92 fn places_behind_of_anchor(&self, page_index: usize) -> Option<usize> {
93 let anchor = self.anchor?;
94
95 if page_index <= anchor {
96 Some(anchor - page_index)
97 } else {
98 Some(anchor + 1 + self.page_count - page_index)
99 }
100 }
101}
102
103impl SealedPageStatesCache for CalculatedPageStates {
104 fn get_page_state(&self, page_index: usize) -> Option<PageState> {
105 if self.anchor == Some(page_index) {
106 if self.anchor_is_partial {
107 Some(PageState::PartialOpen)
108 } else {
109 Some(PageState::Open)
110 }
111 } else if let Some(in_front) = self.places_in_front_of_anchor(page_index)
112 && in_front < self.open_pages
113 {
114 Some(PageState::Open)
115 } else if let Some(behind) = self.places_behind_of_anchor(page_index)
116 && behind <= self.closed_pages
117 {
118 Some(PageState::Closed)
119 } else {
120 None
121 }
122 }
123
124 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
125 #[cfg(fuzzing)]
126 eprintln!("Noticing page state: {page_index} -> {new_state:?}");
127 #[cfg(fuzzing)]
128 eprintln!("Before {self:?}");
129
130 match new_state {
131 PageState::PartialOpen => {
132 match self.get_page_state(page_index) {
134 Some(PageState::PartialOpen) => {
135 }
137 Some(PageState::Open) => {
138 self.anchor = Some(page_index);
139 self.anchor_is_partial = true;
140 }
141 Some(PageState::Closed) => {
142 unreachable!("Can't partially open a closed page")
143 }
144 None => {
145 self.anchor = Some(page_index);
147 self.anchor_is_partial = true;
148 self.open_pages = 1;
149 }
150 }
151
152 self.anchor_is_guess = false;
153 }
154 PageState::Closed => {
155 match self.get_page_state(page_index) {
157 Some(PageState::Closed) => {
158 }
160 Some(PageState::PartialOpen) => {
161 let anchor = self.anchor.as_mut().unwrap();
162 self.closed_pages += 1;
164 self.open_pages -= 1;
165 *anchor = (*anchor + 1) % self.page_count;
166 self.anchor_is_partial = false;
167 }
168 Some(PageState::Open) => {
169 unreachable!("We never outright close a fully open page")
170 }
171 None => {
172 if !self.anchor_is_guess {
174 if let Some(behind) = self.places_behind_of_anchor(page_index) {
175 self.closed_pages = self.closed_pages.max(behind);
176 }
177 }
178 }
179 }
180 }
181 PageState::Open => {
182 match self.get_page_state(page_index) {
184 Some(PageState::Open) => {
185 }
187 Some(PageState::PartialOpen) => {
188 self.anchor_is_partial = false;
190 }
191 Some(PageState::Closed) => {
192 if let Some(in_front) = self.places_in_front_of_anchor(page_index) {
193 self.open_pages = self.open_pages.max(in_front + 1);
194 }
195 if let Some(behind) =
196 self.places_behind_of_anchor((page_index + 1) % self.page_count)
197 {
198 self.closed_pages = self.closed_pages.min(behind);
199 }
200 }
201 None => {
202 match self.anchor {
203 Some(anchor) => {
204 if self.anchor_is_guess {
205 if page_index == (anchor + self.open_pages) % self.page_count {
209 self.open_pages += 1;
210 }
211 } else {
212 if let Some(in_front) =
213 self.places_in_front_of_anchor(page_index)
214 {
215 self.open_pages = self.open_pages.max(in_front + 1);
216 }
217 }
218 }
219 None => {
220 self.anchor = Some(page_index);
222 self.anchor_is_partial = false;
223 self.anchor_is_guess = true;
224 self.open_pages = 1;
225 self.closed_pages = 0;
226 }
227 }
228 }
229 }
230 }
231 }
232
233 #[cfg(fuzzing)]
234 eprintln!("Afterwards {self:?}");
235 }
236
237 fn invalidate_cache_state(&mut self) {
238 *self = Self {
239 page_count: self.page_count,
240 ..Self::new(0)
241 };
242 }
243}
244impl PageStatesCache for CalculatedPageStates {}
245
246#[derive(Debug)]
248#[cfg_attr(feature = "defmt", derive(defmt::Format))]
249pub struct ArrayPageStates<const PAGE_COUNT: usize> {
250 pages: [Option<PageState>; PAGE_COUNT],
251}
252
253impl<const PAGE_COUNT: usize> ArrayPageStates<PAGE_COUNT> {
254 pub const fn new() -> Self {
256 Self {
257 pages: [None; PAGE_COUNT],
258 }
259 }
260}
261
262impl<const PAGE_COUNT: usize> Default for ArrayPageStates<PAGE_COUNT> {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268impl<const PAGE_COUNT: usize> SealedPageStatesCache for ArrayPageStates<PAGE_COUNT> {
269 fn get_page_state(&self, page_index: usize) -> Option<PageState> {
270 self.pages[page_index]
271 }
272
273 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
274 self.pages[page_index] = Some(new_state);
275 }
276
277 fn invalidate_cache_state(&mut self) {
278 for page in self.pages.iter_mut() {
279 *page = None;
280 }
281 }
282}
283impl<const PAGE_COUNT: usize> PageStatesCache for ArrayPageStates<PAGE_COUNT> {}
284
285#[cfg(feature = "alloc")]
286#[derive(Debug)]
288#[cfg_attr(feature = "defmt", derive(defmt::Format))]
289pub struct HeapPageStates {
290 pages: alloc::vec::Vec<Option<PageState>>,
291}
292
293#[cfg(feature = "alloc")]
294impl HeapPageStates {
295 pub fn new(page_count: usize) -> Self {
297 Self {
298 pages: alloc::vec![None; page_count],
299 }
300 }
301}
302
303#[cfg(feature = "alloc")]
304impl SealedPageStatesCache for HeapPageStates {
305 fn get_page_state(&self, page_index: usize) -> Option<PageState> {
306 self.pages[page_index]
307 }
308
309 fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
310 self.pages[page_index] = Some(new_state);
311 }
312
313 fn invalidate_cache_state(&mut self) {
314 for page in self.pages.iter_mut() {
315 *page = None;
316 }
317 }
318}
319#[cfg(feature = "alloc")]
320impl PageStatesCache for HeapPageStates {}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn page_states_correct_from_erased() {
328 let mut page_states = CalculatedPageStates::new(4);
329
330 assert_eq!(page_states.get_page_state(0), None);
331 assert_eq!(page_states.get_page_state(1), None);
332 assert_eq!(page_states.get_page_state(2), None);
333 assert_eq!(page_states.get_page_state(3), None);
334
335 page_states.notice_page_state(0, PageState::Open);
336 page_states.notice_page_state(1, PageState::Open);
337 page_states.notice_page_state(2, PageState::Open);
338 page_states.notice_page_state(3, PageState::Open);
339
340 assert_eq!(page_states.get_page_state(0), Some(PageState::Open));
341 assert_eq!(page_states.get_page_state(1), Some(PageState::Open));
342 assert_eq!(page_states.get_page_state(2), Some(PageState::Open));
343 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
344
345 page_states.notice_page_state(0, PageState::PartialOpen);
346
347 assert_eq!(page_states.get_page_state(0), Some(PageState::PartialOpen));
348 assert_eq!(page_states.get_page_state(1), Some(PageState::Open));
349 assert_eq!(page_states.get_page_state(2), Some(PageState::Open));
350 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
351
352 page_states.notice_page_state(0, PageState::Closed);
353
354 assert_eq!(page_states.get_page_state(0), Some(PageState::Closed));
355 assert_eq!(page_states.get_page_state(1), Some(PageState::Open));
356 assert_eq!(page_states.get_page_state(2), Some(PageState::Open));
357 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
358
359 page_states.notice_page_state(1, PageState::PartialOpen);
360
361 assert_eq!(page_states.get_page_state(0), Some(PageState::Closed));
362 assert_eq!(page_states.get_page_state(1), Some(PageState::PartialOpen));
363 assert_eq!(page_states.get_page_state(2), Some(PageState::Open));
364 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
365
366 page_states.notice_page_state(0, PageState::Open);
367
368 assert_eq!(page_states.get_page_state(0), Some(PageState::Open));
369 assert_eq!(page_states.get_page_state(1), Some(PageState::PartialOpen));
370 assert_eq!(page_states.get_page_state(2), Some(PageState::Open));
371 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
372 }
373
374 #[test]
375 fn page_states_correct_from_recover() {
376 let mut page_states = CalculatedPageStates::new(4);
377
378 println!("{page_states:?}");
379 page_states.notice_page_state(3, PageState::PartialOpen);
380 println!("{page_states:?}");
381 page_states.notice_page_state(0, PageState::Closed);
382 println!("{page_states:?}");
383 page_states.notice_page_state(3, PageState::Open);
384 println!("{page_states:?}");
385
386 assert_eq!(page_states.get_page_state(0), Some(PageState::Closed));
387 assert_eq!(page_states.get_page_state(1), Some(PageState::Closed));
388 assert_eq!(page_states.get_page_state(2), Some(PageState::Closed));
389 assert_eq!(page_states.get_page_state(3), Some(PageState::Open));
390 }
391}