Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ defmt = ["dep:defmt"]
std = []
# Enable the implementation of the map Key trait for ArrayVec and ArrayString
arrayvec = ["dep:arrayvec"]
alloc = []
alloc = ["defmt/alloc"]
heapless = ["dep:heapless"]
heapless-09 = ["dep:heapless-09"]
_test = ["dep:futures", "dep:approx", "std", "arrayvec", "alloc", "heapless"]
Expand Down
28 changes: 21 additions & 7 deletions src/cache/key_pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use core::{fmt::Debug, num::NonZeroU32};

use crate::map::Key;

use super::list::List;

pub(crate) trait KeyPointersCache<KEY: Key> {
fn key_location(&self, key: &KEY) -> Option<u32>;

Expand All @@ -14,20 +16,31 @@ pub(crate) trait KeyPointersCache<KEY: Key> {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct CachedKeyPointers<KEY: Eq, const KEYS: usize> {
key_pointers: [Option<(KEY, NonZeroU32)>; KEYS],
key_pointers: List<Option<(KEY, NonZeroU32)>, KEYS>,
}

impl<KEY: Eq, const KEYS: usize> CachedKeyPointers<KEY, KEYS> {
const ARRAY_REPEAT_VALUE: Option<(KEY, NonZeroU32)> = None;

pub(crate) const fn new() -> Self {
Self {
key_pointers: [Self::ARRAY_REPEAT_VALUE; KEYS],
key_pointers: List::Arr([Self::ARRAY_REPEAT_VALUE; KEYS]),
}
}

#[cfg(feature = "alloc")]
pub(crate) fn new_heap(n: usize) -> Self
where
KEY: Clone,
{
Self {
key_pointers: List::from_elem_vec(Self::ARRAY_REPEAT_VALUE, n),
}
}

fn key_index(&self, key: &KEY) -> Option<usize> {
self.key_pointers
.as_slice()
.iter()
.enumerate()
.filter_map(|(index, val)| val.as_ref().map(|val| (index, val)))
Expand All @@ -39,8 +52,9 @@ impl<KEY: Eq, const KEYS: usize> CachedKeyPointers<KEY, KEYS> {
}

fn insert_front(&mut self, value: (KEY, NonZeroU32)) {
self.key_pointers[KEYS - 1] = Some(value);
move_to_front(&mut self.key_pointers, KEYS - 1);
let keys = self.key_pointers.len();
self.key_pointers[keys - 1] = Some(value);
move_to_front(self.key_pointers.as_mut_slice(), keys - 1);
}
}

Expand All @@ -55,7 +69,7 @@ impl<KEY: Key, const KEYS: usize> KeyPointersCache<KEY> for CachedKeyPointers<KE
Some(existing_index) => {
self.key_pointers[existing_index] =
Some((key.clone(), NonZeroU32::new(item_address).unwrap()));
move_to_front(&mut self.key_pointers, existing_index);
move_to_front(self.key_pointers.as_mut_slice(), existing_index);
}
None => self.insert_front((key.clone(), NonZeroU32::new(item_address).unwrap())),
}
Expand All @@ -64,12 +78,12 @@ impl<KEY: Key, const KEYS: usize> KeyPointersCache<KEY> for CachedKeyPointers<KE
fn notice_key_erased(&mut self, key: &KEY) {
if let Some(existing_index) = self.key_index(key) {
self.key_pointers[existing_index] = None;
move_to_back(&mut self.key_pointers, existing_index);
move_to_back(self.key_pointers.as_mut_slice(), existing_index);
}
}

fn invalidate_cache_state(&mut self) {
*self = Self::new();
self.key_pointers.as_mut_slice().fill(None);
}
}

Expand Down
103 changes: 103 additions & 0 deletions src/cache/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! A `List` enum which allows to treat a boxed slice or an array the same way
//! once created.
//!
//! It does this by having a generic usize parameter and discarding that
//! when being used as a boxed slice.
//!
//! BS stands for boxed slice.
//!
//! Obviously when alloc feature is disabled only the array is available.

#[cfg(feature = "alloc")]
use alloc::{boxed::Box, vec};

#[allow(unused)]
pub enum ListKind {
Arr,
/// Boxed Slice
#[cfg(feature = "alloc")]
BS(usize),
}

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum List<T, const N: usize = 0> {
Arr([T; N]),
#[cfg(feature = "alloc")]
BS(Box<[T]>),
}

impl<T, const N: usize> List<T, N> {
#[allow(unused)]
pub fn from_elem(elem: T, list_kind: ListKind) -> Self
where
T: Copy,
{
match list_kind {
ListKind::Arr => Self::Arr([elem; N]),
#[cfg(feature = "alloc")]
ListKind::BS(n) => Self::BS(vec![elem; n].into()),
}
}

#[cfg(feature = "alloc")]
pub fn from_elem_vec(elem: T, n: usize) -> Self
where
T: Clone,
{
Self::BS(vec![elem; n].into())
}

pub const fn from_elem_arr(elem: T) -> Self
where
T: Copy,
{
Self::Arr([elem; N])
}

pub fn as_slice(&self) -> &[T] {
match self {
Self::Arr(a) => &a[..],
#[cfg(feature = "alloc")]
Self::BS(bs) => bs,
}
}

pub fn as_mut_slice(&mut self) -> &mut [T] {
match self {
Self::Arr(a) => &mut a[..],
#[cfg(feature = "alloc")]
Self::BS(bs) => bs,
}
}

pub fn len(&self) -> usize {
match self {
Self::Arr(_) => N,
#[cfg(feature = "alloc")]
List::BS(bs) => bs.len(),
}
}
}

impl<T, const N: usize> core::ops::Index<usize> for List<T, N> {
type Output = T;

fn index(&self, index: usize) -> &Self::Output {
match self {
Self::Arr(a) => a.index(index),
#[cfg(feature = "alloc")]
Self::BS(bs) => bs.index(index),
}
}
}

impl<T, const N: usize> core::ops::IndexMut<usize> for List<T, N> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match self {
Self::Arr(a) => a.index_mut(index),
#[cfg(feature = "alloc")]
Self::BS(bs) => bs.index_mut(index),
}
}
}
35 changes: 35 additions & 0 deletions src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use self::{
page_states::{CachedPageStates, UncachedPageStates},
};

pub(crate) mod list;

pub(crate) mod key_pointers;
pub(crate) mod page_pointers;
pub(crate) mod page_states;
Expand Down Expand Up @@ -286,6 +288,17 @@ impl<const PAGE_COUNT: usize> PageStateCache<PAGE_COUNT> {
key_pointers: UncachedKeyPointers,
}
}

/// Construct a new instance on the heap, generics are discarded
#[cfg(feature = "alloc")]
pub fn new_heap(page_count: usize) -> Self {
Self {
dirt_tracker: DirtTracker::new(),
page_states: CachedPageStates::new_heap(page_count),
page_pointers: UncachedPagePointers,
key_pointers: UncachedKeyPointers,
}
}
}

impl<const PAGE_COUNT: usize> Default for PageStateCache<PAGE_COUNT> {
Expand Down Expand Up @@ -360,6 +373,17 @@ impl<const PAGE_COUNT: usize> PagePointerCache<PAGE_COUNT> {
key_pointers: UncachedKeyPointers,
}
}

/// Construct a new instance on the heap, generics are discarded
#[cfg(feature = "alloc")]
pub fn new_heap(page_count: usize) -> Self {
Self {
dirt_tracker: DirtTracker::new(),
page_states: CachedPageStates::new_heap(page_count),
page_pointers: CachedPagePointers::new_heap(page_count),
key_pointers: UncachedKeyPointers,
}
}
}

impl<const PAGE_COUNT: usize> Default for PagePointerCache<PAGE_COUNT> {
Expand Down Expand Up @@ -439,6 +463,17 @@ impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> KeyPointerCache<PAGE_
key_pointers: CachedKeyPointers::new(),
}
}

/// Construct a new instance on the heap, generics are discarded
#[cfg(feature = "alloc")]
pub fn new_heap(page_count: usize, keys: usize) -> Self {
Self {
dirt_tracker: DirtTracker::new(),
page_states: CachedPageStates::new_heap(page_count),
page_pointers: CachedPagePointers::new_heap(page_count),
key_pointers: CachedKeyPointers::new_heap(keys),
}
}
}

impl<const PAGE_COUNT: usize, KEY: Key, const KEYS: usize> Default
Expand Down
26 changes: 18 additions & 8 deletions src/cache/page_pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::{
NorFlashExt, PageState, calculate_page_address, calculate_page_index, item::ItemHeader,
};

use super::list::List;

pub(crate) trait PagePointersCache: Debug {
fn first_item_after_erased(&self, page_index: usize) -> Option<u32>;
fn first_item_after_written(&self, page_index: usize) -> Option<u32>;
Expand All @@ -31,14 +33,14 @@ pub(crate) trait PagePointersCache: Debug {
// and so Option can make use of the niche so we save bytes
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct CachedPagePointers<const PAGE_COUNT: usize> {
after_erased_pointers: [Option<NonZeroU32>; PAGE_COUNT],
after_written_pointers: [Option<NonZeroU32>; PAGE_COUNT],
after_erased_pointers: List<Option<NonZeroU32>, PAGE_COUNT>,
after_written_pointers: List<Option<NonZeroU32>, PAGE_COUNT>,
}

impl<const PAGE_COUNT: usize> Debug for CachedPagePointers<PAGE_COUNT> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{{ after_erased_pointers: [")?;
for (i, val) in self.after_erased_pointers.iter().enumerate() {
for (i, val) in self.after_erased_pointers.as_slice().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
Expand All @@ -50,7 +52,7 @@ impl<const PAGE_COUNT: usize> Debug for CachedPagePointers<PAGE_COUNT> {
}
}
write!(f, "], after_written_pointers: [")?;
for (i, val) in self.after_written_pointers.iter().enumerate() {
for (i, val) in self.after_written_pointers.as_slice().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
Expand All @@ -70,8 +72,16 @@ impl<const PAGE_COUNT: usize> Debug for CachedPagePointers<PAGE_COUNT> {
impl<const PAGE_COUNT: usize> CachedPagePointers<PAGE_COUNT> {
pub const fn new() -> Self {
Self {
after_erased_pointers: [None; PAGE_COUNT],
after_written_pointers: [None; PAGE_COUNT],
after_erased_pointers: List::from_elem_arr(None),
after_written_pointers: List::from_elem_arr(None),
}
}

#[cfg(feature = "alloc")]
pub fn new_heap(n: usize) -> Self {
Self {
after_erased_pointers: List::from_elem_vec(None, n),
after_written_pointers: List::from_elem_vec(None, n),
}
}
}
Expand Down Expand Up @@ -133,8 +143,8 @@ impl<const PAGE_COUNT: usize> PagePointersCache for CachedPagePointers<PAGE_COUN
}

fn invalidate_cache_state(&mut self) {
self.after_erased_pointers = [None; PAGE_COUNT];
self.after_written_pointers = [None; PAGE_COUNT];
self.after_erased_pointers.as_mut_slice().fill(None);
self.after_written_pointers.as_mut_slice().fill(None);
}
}

Expand Down
16 changes: 12 additions & 4 deletions src/cache/page_states.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::fmt::Debug;

use crate::PageState;
use crate::cache::list::List;

pub(crate) trait PageStatesCache: Debug {
fn get_page_state(&self, page_index: usize) -> Option<PageState>;
Expand All @@ -10,13 +11,13 @@ pub(crate) trait PageStatesCache: Debug {

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct CachedPageStates<const PAGE_COUNT: usize> {
pages: [Option<PageState>; PAGE_COUNT],
pages: List<Option<PageState>, PAGE_COUNT>,
}

impl<const PAGE_COUNT: usize> Debug for CachedPageStates<PAGE_COUNT> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "[")?;
for (i, val) in self.pages.iter().enumerate() {
for (i, val) in self.pages.as_slice().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
Expand All @@ -36,7 +37,14 @@ impl<const PAGE_COUNT: usize> Debug for CachedPageStates<PAGE_COUNT> {
impl<const PAGE_COUNT: usize> CachedPageStates<PAGE_COUNT> {
pub const fn new() -> Self {
Self {
pages: [None; PAGE_COUNT],
pages: List::from_elem_arr(None),
}
}

#[cfg(feature = "alloc")]
pub fn new_heap(n: usize) -> Self {
Self {
pages: List::from_elem_vec(None, n),
}
}
}
Expand All @@ -51,7 +59,7 @@ impl<const PAGE_COUNT: usize> PageStatesCache for CachedPageStates<PAGE_COUNT> {
}

fn invalidate_cache_state(&mut self) {
*self = Self::new();
self.pages.as_mut_slice().fill(None);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use core::{
use embedded_storage_async::nor_flash::NorFlash;
use map::SerializationError;

#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod alloc_impl;
#[cfg(feature = "arrayvec")]
Expand Down
Loading