use core::marker::PhantomData;
use core::{fmt::Debug, ops::Range};
use embedded_storage_async::nor_flash::NorFlash;
use crate::cache::key_pointers::KeyPointersCache;
use crate::cache::page_pointers::SealedPagePointersCache;
use crate::cache::page_states::SealedPageStatesCache;
use crate::{PageState, item::ItemHeader};
use key_pointers::SealedKeyPointersCache;
use page_pointers::PagePointersCache;
use page_states::PageStatesCache;
pub mod key_pointers;
pub mod page_pointers;
pub mod page_states;
mod tests;
pub(crate) trait SealedCacheImpl {
fn is_dirty(&mut self) -> bool;
fn mark_dirty(&mut self);
fn unmark_dirty(&mut self);
fn get_page_state(&mut self, page_index: usize) -> Option<PageState>;
fn notice_page_state(&mut self, page_index: usize, new_state: PageState, dirty: bool);
fn first_item_after_erased(&mut self, page_index: usize) -> Option<u32>;
fn first_item_after_written(&mut self, page_index: usize) -> Option<u32>;
fn notice_item_written<S: NorFlash>(
&mut self,
flash_range: Range<u32>,
item_address: u32,
item_header: &ItemHeader,
);
fn notice_item_erased<S: NorFlash>(
&mut self,
flash_range: Range<u32>,
item_address: u32,
item_header: &ItemHeader,
);
fn invalidate_cache_state(&mut self);
}
pub(crate) trait SealedKeyCacheImpl<KEY> {
fn key_location(&mut self, key: &KEY) -> Option<u32>;
fn notice_key_location(&mut self, key: &KEY, item_address: u32, dirty: bool);
fn notice_key_erased(&mut self, key: &KEY);
}
#[allow(private_bounds)]
pub trait CacheImpl<KEY>: SealedCacheImpl + SealedKeyCacheImpl<KEY> {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cache<
PAGE: PageStatesCache,
POINTERS: PagePointersCache,
KEYS: KeyPointersCache<KEY>,
KEY = (),
> {
dirt_tracker: DirtTracker,
page_states: PAGE,
page_pointers: POINTERS,
key_pointers: KEYS,
_phantom: PhantomData<KEY>,
}
impl<PAGE: PageStatesCache, POINTERS: PagePointersCache, KEYS: KeyPointersCache<KEY>, KEY>
Cache<PAGE, POINTERS, KEYS, KEY>
{
pub fn new(page_states: PAGE, page_pointers: POINTERS, key_pointers: KEYS) -> Self {
Self {
dirt_tracker: DirtTracker::new(),
page_states,
page_pointers,
key_pointers,
_phantom: PhantomData,
}
}
}
impl<KEY> Cache<Uncached, Uncached, Uncached, KEY> {
pub const fn new_uncached() -> Self {
Self {
dirt_tracker: DirtTracker::new(),
page_states: Uncached,
page_pointers: Uncached,
key_pointers: Uncached,
_phantom: PhantomData,
}
}
}
impl<PAGE: PageStatesCache, POINTERS: PagePointersCache, KEYS: KeyPointersCache<KEY>, KEY>
SealedCacheImpl for Cache<PAGE, POINTERS, KEYS, KEY>
{
fn is_dirty(&mut self) -> bool {
self.dirt_tracker.is_dirty()
}
fn mark_dirty(&mut self) {
self.dirt_tracker.mark_dirty();
}
fn unmark_dirty(&mut self) {
self.dirt_tracker.unmark_dirty();
}
fn get_page_state(&mut self, page_index: usize) -> Option<PageState> {
self.page_states.get_page_state(page_index)
}
fn notice_page_state(&mut self, page_index: usize, new_state: PageState, dirty: bool) {
if dirty {
self.mark_dirty();
}
self.page_states.notice_page_state(page_index, new_state);
self.page_pointers.notice_page_state(page_index, new_state);
}
fn first_item_after_erased(&mut self, page_index: usize) -> Option<u32> {
self.page_pointers.first_item_after_erased(page_index)
}
fn first_item_after_written(&mut self, page_index: usize) -> Option<u32> {
self.page_pointers.first_item_after_written(page_index)
}
fn notice_item_written<S: NorFlash>(
&mut self,
flash_range: Range<u32>,
item_address: u32,
item_header: &ItemHeader,
) {
self.mark_dirty();
self.page_pointers
.notice_item_written::<S>(flash_range, item_address, item_header);
}
fn notice_item_erased<S: NorFlash>(
&mut self,
flash_range: Range<u32>,
item_address: u32,
item_header: &ItemHeader,
) {
self.mark_dirty();
self.page_pointers
.notice_item_erased::<S>(flash_range, item_address, item_header);
}
fn invalidate_cache_state(&mut self) {
self.dirt_tracker.unmark_dirty();
self.page_states.invalidate_cache_state();
self.page_pointers.invalidate_cache_state();
self.key_pointers.invalidate_cache_state();
}
}
impl<PAGE: PageStatesCache, POINTERS: PagePointersCache, KEYS: KeyPointersCache<KEY>, KEY>
SealedKeyCacheImpl<KEY> for Cache<PAGE, POINTERS, KEYS, KEY>
{
fn key_location(&mut self, key: &KEY) -> Option<u32> {
self.key_pointers.key_location(key)
}
fn notice_key_location(&mut self, key: &KEY, item_address: u32, dirty: bool) {
if dirty {
self.mark_dirty();
}
self.key_pointers.notice_key_location(key, item_address);
}
fn notice_key_erased(&mut self, key: &KEY) {
self.mark_dirty();
self.key_pointers.notice_key_erased(key);
}
}
impl<PAGE: PageStatesCache, POINTERS: PagePointersCache, KEYS: KeyPointersCache<KEY>, KEY>
CacheImpl<KEY> for Cache<PAGE, POINTERS, KEYS, KEY>
{
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct DirtTracker {
dirty: bool,
}
impl DirtTracker {
pub const fn new() -> Self {
DirtTracker { dirty: false }
}
pub fn is_dirty(&self) -> bool {
self.dirty
}
pub fn mark_dirty(&mut self) {
self.dirty = true;
}
pub fn unmark_dirty(&mut self) {
self.dirty = false;
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Uncached;
impl SealedPageStatesCache for Uncached {
fn get_page_state(&self, _page_index: usize) -> Option<PageState> {
None
}
fn notice_page_state(&mut self, _page_index: usize, _new_state: PageState) {}
fn invalidate_cache_state(&mut self) {}
}
impl PageStatesCache for Uncached {}
impl SealedPagePointersCache for Uncached {
fn first_item_after_erased(&self, _page_index: usize) -> Option<u32> {
None
}
fn first_item_after_written(&self, _page_index: usize) -> Option<u32> {
None
}
fn notice_item_written<S: NorFlash>(
&mut self,
_flash_range: Range<u32>,
_item_address: u32,
_item_header: &ItemHeader,
) {
}
fn notice_item_erased<S: NorFlash>(
&mut self,
_flash_range: Range<u32>,
_item_address: u32,
_item_header: &ItemHeader,
) {
}
fn notice_page_state(&mut self, _page_index: usize, _new_state: PageState) {}
fn invalidate_cache_state(&mut self) {}
}
impl PagePointersCache for Uncached {}
impl<KEY> SealedKeyPointersCache<KEY> for Uncached {
fn key_location(&self, _key: &KEY) -> Option<u32> {
None
}
fn notice_key_location(&mut self, _key: &KEY, _item_address: u32) {}
fn notice_key_erased(&mut self, _key: &KEY) {}
fn invalidate_cache_state(&mut self) {}
}
impl<KEY> KeyPointersCache<KEY> for Uncached {}