From 259cfc613004ecefbf75db9f01080d0ec0e6571e Mon Sep 17 00:00:00 2001
From: Emil Fresk <emil.fresk@gmail.com>
Date: Sun, 4 Aug 2024 10:56:21 +0200
Subject: [PATCH] Relax the `MultiwriteNorFlash` to only need clearing of words

---
 src/cache/mod.rs           |   4 +-
 src/cache/page_pointers.rs |  16 ++--
 src/cache/tests.rs         |   6 +-
 src/item.rs                | 155 ++++++++++++++++++++++---------------
 src/lib.rs                 |  10 ++-
 src/map.rs                 |  16 ++--
 src/mock_flash.rs          |  10 ++-
 src/queue.rs               |  23 +++---
 8 files changed, 135 insertions(+), 105 deletions(-)

diff --git a/src/cache/mod.rs b/src/cache/mod.rs
index 339a0ac..9f14f12 100644
--- a/src/cache/mod.rs
+++ b/src/cache/mod.rs
@@ -96,7 +96,7 @@ pub(crate) trait PrivateCacheImpl: Invalidate {
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     ) {
         self.mark_dirty();
         self.page_pointers()
@@ -108,7 +108,7 @@ pub(crate) trait PrivateCacheImpl: Invalidate {
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     ) {
         self.mark_dirty();
         self.page_pointers()
diff --git a/src/cache/page_pointers.rs b/src/cache/page_pointers.rs
index 34f4280..e9dcef8 100644
--- a/src/cache/page_pointers.rs
+++ b/src/cache/page_pointers.rs
@@ -14,13 +14,13 @@ pub(crate) trait PagePointersCache: Debug {
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     );
     fn notice_item_erased<S: NorFlash>(
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     );
 
     fn notice_page_state(&mut self, page_index: usize, new_state: PageState);
@@ -89,11 +89,11 @@ impl<const PAGE_COUNT: usize> PagePointersCache for CachedPagePointers<PAGE_COUN
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     ) {
         let page_index = calculate_page_index::<S>(flash_range, item_address);
 
-        let next_item_address = item_header.next_item_address::<S>(item_address);
+        let next_item_address = item_header.next_item_address(item_address);
 
         // We only care about the furthest written item, so discard if this is an earlier item
         if let Some(first_item_after_written) = self.first_item_after_written(page_index) {
@@ -109,7 +109,7 @@ impl<const PAGE_COUNT: usize> PagePointersCache for CachedPagePointers<PAGE_COUN
         &mut self,
         flash_range: Range<u32>,
         item_address: u32,
-        item_header: &ItemHeader,
+        item_header: &ItemHeader<S>,
     ) {
         let page_index = calculate_page_index::<S>(flash_range.clone(), item_address);
 
@@ -120,7 +120,7 @@ impl<const PAGE_COUNT: usize> PagePointersCache for CachedPagePointers<PAGE_COUN
 
         if item_address == next_unerased_item {
             self.after_erased_pointers[page_index] =
-                NonZeroU32::new(item_header.next_item_address::<S>(item_address));
+                NonZeroU32::new(item_header.next_item_address(item_address));
         }
     }
 
@@ -155,7 +155,7 @@ impl PagePointersCache for UncachedPagePointers {
         &mut self,
         _flash_range: Range<u32>,
         _item_address: u32,
-        _item_header: &ItemHeader,
+        _item_header: &ItemHeader<S>,
     ) {
     }
 
@@ -163,7 +163,7 @@ impl PagePointersCache for UncachedPagePointers {
         &mut self,
         _flash_range: Range<u32>,
         _item_address: u32,
-        _item_header: &ItemHeader,
+        _item_header: &ItemHeader<S>,
     ) {
     }
 
diff --git a/src/cache/tests.rs b/src/cache/tests.rs
index 73d2f25..34a9773 100644
--- a/src/cache/tests.rs
+++ b/src/cache/tests.rs
@@ -23,7 +23,7 @@ mod queue_tests {
                 reads: 594934,
                 writes: 6299,
                 bytes_read: 2766058,
-                bytes_written: 53299
+                bytes_written: 45299,
             }
         );
     }
@@ -37,7 +37,7 @@ mod queue_tests {
                 reads: 308740,
                 writes: 6299,
                 bytes_read: 2479864,
-                bytes_written: 53299
+                bytes_written: 45299
             }
         );
     }
@@ -51,7 +51,7 @@ mod queue_tests {
                 reads: 211172,
                 writes: 6299,
                 bytes_read: 1699320,
-                bytes_written: 53299
+                bytes_written: 45299
             }
         );
     }
diff --git a/src/item.rs b/src/item.rs
index 6361fb0..76c8e49 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -6,10 +6,12 @@
 //! ```text
 //! ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
 //! │      :      :      :      │      :      │      :      │  :  :  :  :  :  │  :  :  :  :  :  :  :  :  :  │  :  :  :  :  :  │
-//! │            CRC            │   Length    │   Length'   │Pad to word align│            Data             │Pad to word align│
+//! │            CRC       ...  │   Length    │   Length'   │Pad to word align│            Data             │Pad to word align│
 //! │      :      :      :      │      :      │      :      │  :  :  :  :  :  │  :  :  :  :  :   :  :  :  : │  :  :  :  :  :  │
 //! └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴───┴──┴──┴──┴─┴──┴──┴──┴──┴──┴──┘
-//! 0      1      2      3      4      5      6      7      8                 8+padding                     8+padding+length  8+padding+length+endpadding
+//! 0      1      2      3  ...  WS            WS+2          WS+4              WS+4+padding                  WS+4+padding+length  WS+4+padding+length+endpadding
+//!
+//! WS = max(4, WORD_SIZE)
 //! ```
 //!
 //! An item consists of an [ItemHeader] and some data.
@@ -21,36 +23,42 @@
 //! and has some other modifications to make corruption less likely to happen.
 //!
 
-use core::num::NonZeroU32;
 use core::ops::Range;
+use core::{marker::PhantomData, num::NonZeroU32};
 
-use embedded_storage_async::nor_flash::{MultiwriteNorFlash, NorFlash};
+use embedded_storage_async::nor_flash::NorFlash;
 
 use crate::{
     cache::PrivateCacheImpl, calculate_page_address, calculate_page_end_address,
     calculate_page_index, get_page_state, round_down_to_alignment, round_down_to_alignment_usize,
     round_up_to_alignment, round_up_to_alignment_usize, AlignedBuf, Error, NorFlashExt, PageState,
-    MAX_WORD_SIZE,
+    WordclearNorFlash, MAX_WORD_SIZE,
 };
 
 #[derive(Debug, Clone)]
-pub struct ItemHeader {
+pub struct ItemHeader<S> {
     /// Length of the item payload (so not including the header and not including word alignment)
     pub length: u16,
     pub crc: Option<NonZeroU32>,
+    _p: PhantomData<S>,
 }
 
-impl ItemHeader {
-    const LENGTH: usize = 8;
+impl<S: NorFlash> ItemHeader<S> {
+    const DATA_CRC_LENGTH: usize = if <S as NorFlashExt>::WORD_SIZE < 4 {
+        4 // The minimum is 4 to hold the CRC32.
+    } else {
+        <S as NorFlashExt>::WORD_SIZE
+    };
+    const LENGTH: usize = Self::DATA_CRC_LENGTH + 4;
 
-    const DATA_CRC_FIELD: Range<usize> = 0..4;
-    const LENGTH_FIELD: Range<usize> = 4..6;
-    const LENGTH_CRC_FIELD: Range<usize> = 6..8;
+    const DATA_CRC_FIELD: Range<usize> = 0..Self::DATA_CRC_LENGTH;
+    const LENGTH_FIELD: Range<usize> = Self::DATA_CRC_LENGTH..Self::DATA_CRC_LENGTH + 2;
+    const LENGTH_CRC_FIELD: Range<usize> = Self::DATA_CRC_LENGTH + 2..Self::DATA_CRC_LENGTH + 4;
 
     /// Read the header from the flash at the given address.
     ///
     /// If the item doesn't exist or doesn't fit between the address and the end address, none is returned.
-    pub async fn read_new<S: NorFlash>(
+    pub async fn read_new(
         flash: &mut S,
         address: u32,
         end_address: u32,
@@ -90,25 +98,28 @@ impl ItemHeader {
         Ok(Some(Self {
             length: u16::from_le_bytes(header_slice[Self::LENGTH_FIELD].try_into().unwrap()),
             crc: {
-                match u32::from_le_bytes(header_slice[Self::DATA_CRC_FIELD].try_into().unwrap()) {
+                match u32::from_le_bytes(
+                    header_slice[Self::DATA_CRC_FIELD][..4].try_into().unwrap(),
+                ) {
                     0 => None,
                     value => Some(NonZeroU32::new(value).unwrap()),
                 }
             },
+            _p: PhantomData,
         }))
     }
 
-    pub async fn read_item<'d, S: NorFlash>(
+    pub async fn read_item<'d>(
         self,
         flash: &mut S,
         data_buffer: &'d mut [u8],
         address: u32,
         end_address: u32,
-    ) -> Result<MaybeItem<'d>, Error<S::Error>> {
+    ) -> Result<MaybeItem<'d, S>, Error<S::Error>> {
         match self.crc {
             None => Ok(MaybeItem::Erased(self, data_buffer)),
             Some(header_crc) => {
-                let data_address = ItemHeader::data_address::<S>(address);
+                let data_address = Self::data_address(address);
                 let read_len = round_up_to_alignment_usize::<S>(self.length as usize);
                 if data_buffer.len() < read_len {
                     return Err(Error::BufferTooSmall(read_len));
@@ -141,7 +152,7 @@ impl ItemHeader {
         }
     }
 
-    async fn write<S: NorFlash>(&self, flash: &mut S, address: u32) -> Result<(), Error<S::Error>> {
+    async fn write(&self, flash: &mut S, address: u32) -> Result<(), Error<S::Error>> {
         let mut buffer = AlignedBuf([0xFF; MAX_WORD_SIZE]);
 
         buffer[Self::DATA_CRC_FIELD]
@@ -163,47 +174,62 @@ impl ItemHeader {
             })
     }
 
-    /// Erase this item by setting the crc to none and overwriting the header with it
-    pub async fn erase_data<S: MultiwriteNorFlash>(
-        mut self,
-        flash: &mut S,
-        flash_range: Range<u32>,
-        cache: &mut impl PrivateCacheImpl,
-        address: u32,
-    ) -> Result<Self, Error<S::Error>> {
-        self.crc = None;
-        cache.notice_item_erased::<S>(flash_range.clone(), address, &self);
-        self.write(flash, address).await?;
-        Ok(self)
+    async fn clear_data_crc(&self, flash: &mut S, address: u32) -> Result<(), Error<S::Error>> {
+        let buffer = AlignedBuf([0x0; MAX_WORD_SIZE]);
+
+        flash
+            .write(address, &buffer[Self::DATA_CRC_FIELD])
+            .await
+            .map_err(|e| Error::Storage {
+                value: e,
+                #[cfg(feature = "_test")]
+                backtrace: std::backtrace::Backtrace::capture(),
+            })
     }
 
     /// Get the address of the start of the data for this item
-    pub const fn data_address<S: NorFlash>(address: u32) -> u32 {
+    pub const fn data_address(address: u32) -> u32 {
         address + round_up_to_alignment::<S>(Self::LENGTH as u32)
     }
 
     /// Get the location of the next item in flash
-    pub const fn next_item_address<S: NorFlash>(&self, address: u32) -> u32 {
-        let data_address = ItemHeader::data_address::<S>(address);
+    pub const fn next_item_address(&self, address: u32) -> u32 {
+        let data_address = Self::data_address(address);
         data_address + round_up_to_alignment::<S>(self.length as u32)
     }
 
     /// Calculates the amount of bytes available for data.
     /// Essentially, it's the given amount minus the header and minus some alignment padding.
-    pub const fn available_data_bytes<S: NorFlash>(total_available: u32) -> Option<u32> {
-        let data_start = Self::data_address::<S>(0);
+    pub const fn available_data_bytes(total_available: u32) -> Option<u32> {
+        let data_start = Self::data_address(0);
         let data_end = round_down_to_alignment::<S>(total_available);
 
         data_end.checked_sub(data_start)
     }
 }
 
-pub struct Item<'d> {
-    pub header: ItemHeader,
+impl<S: WordclearNorFlash> ItemHeader<S> {
+    /// Erase this item by setting the crc to none and overwriting the header with it
+    pub async fn erase_data(
+        mut self,
+        flash: &mut S,
+        flash_range: Range<u32>,
+        cache: &mut impl PrivateCacheImpl,
+        address: u32,
+    ) -> Result<Self, Error<S::Error>> {
+        self.crc = None;
+        cache.notice_item_erased::<S>(flash_range.clone(), address, &self);
+        self.clear_data_crc(flash, address).await?;
+        Ok(self)
+    }
+}
+
+pub struct Item<'d, S> {
+    pub header: ItemHeader<S>,
     data_buffer: &'d mut [u8],
 }
 
-impl<'d> Item<'d> {
+impl<'d, S: NorFlash> Item<'d, S> {
     pub fn data(&self) -> &[u8] {
         &self.data_buffer[..self.header.length as usize]
     }
@@ -213,20 +239,21 @@ impl<'d> Item<'d> {
     }
 
     /// Destruct the item to get back the full data buffer
-    pub fn destruct(self) -> (ItemHeader, &'d mut [u8]) {
+    pub fn destruct(self) -> (ItemHeader<S>, &'d mut [u8]) {
         (self.header, self.data_buffer)
     }
 
-    pub async fn write_new<S: NorFlash>(
+    pub async fn write_new(
         flash: &mut S,
         flash_range: Range<u32>,
         cache: &mut impl PrivateCacheImpl,
         address: u32,
         data: &'d [u8],
-    ) -> Result<ItemHeader, Error<S::Error>> {
+    ) -> Result<ItemHeader<S>, Error<S::Error>> {
         let header = ItemHeader {
             length: data.len() as u16,
             crc: Some(adapted_crc32(data)),
+            _p: PhantomData,
         };
 
         Self::write_raw(flash, flash_range, cache, &header, data, address).await?;
@@ -234,11 +261,11 @@ impl<'d> Item<'d> {
         Ok(header)
     }
 
-    async fn write_raw<S: NorFlash>(
+    async fn write_raw(
         flash: &mut S,
         flash_range: Range<u32>,
         cache: &mut impl PrivateCacheImpl,
-        header: &ItemHeader,
+        header: &ItemHeader<S>,
         data: &[u8],
         address: u32,
     ) -> Result<(), Error<S::Error>> {
@@ -247,7 +274,7 @@ impl<'d> Item<'d> {
 
         let (data_block, data_left) = data.split_at(round_down_to_alignment_usize::<S>(data.len()));
 
-        let data_address = ItemHeader::data_address::<S>(address);
+        let data_address = ItemHeader::<S>::data_address(address);
         flash
             .write(data_address, data_block)
             .await
@@ -276,7 +303,7 @@ impl<'d> Item<'d> {
         Ok(())
     }
 
-    pub async fn write<S: NorFlash>(
+    pub async fn write(
         &self,
         flash: &mut S,
         flash_range: Range<u32>,
@@ -294,7 +321,7 @@ impl<'d> Item<'d> {
         .await
     }
 
-    pub fn unborrow(self) -> ItemUnborrowed {
+    pub fn unborrow(self) -> ItemUnborrowed<S> {
         ItemUnborrowed {
             header: self.header,
             data_buffer_len: self.data_buffer.len(),
@@ -302,7 +329,7 @@ impl<'d> Item<'d> {
     }
 }
 
-impl<'d> core::fmt::Debug for Item<'d> {
+impl<'d, S: core::fmt::Debug> core::fmt::Debug for Item<'d, S> {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("Item")
             .field("header", &self.header)
@@ -315,14 +342,14 @@ impl<'d> core::fmt::Debug for Item<'d> {
 }
 
 /// A version of [Item] that does not borrow the data. This is to circumvent the borrowchecker in some places.
-pub struct ItemUnborrowed {
-    pub header: ItemHeader,
+pub struct ItemUnborrowed<S> {
+    pub header: ItemHeader<S>,
     data_buffer_len: usize,
 }
 
-impl ItemUnborrowed {
+impl<S> ItemUnborrowed<S> {
     /// Reborrows the data. Watch out! Make sure the data buffer hasn't changed since unborrowing!
-    pub fn reborrow(self, data_buffer: &mut [u8]) -> Item<'_> {
+    pub fn reborrow(self, data_buffer: &mut [u8]) -> Item<'_, S> {
         Item {
             header: self.header,
             data_buffer: &mut data_buffer[..self.data_buffer_len],
@@ -359,7 +386,7 @@ pub async fn find_next_free_item_spot<S: NorFlash>(
         }
     };
 
-    if let Some(available) = ItemHeader::available_data_bytes::<S>(end_address - free_item_address)
+    if let Some(available) = ItemHeader::<S>::available_data_bytes(end_address - free_item_address)
     {
         if available >= data_length {
             Ok(Some(free_item_address))
@@ -371,13 +398,13 @@ pub async fn find_next_free_item_spot<S: NorFlash>(
     }
 }
 
-pub enum MaybeItem<'d> {
-    Corrupted(ItemHeader, &'d mut [u8]),
-    Erased(ItemHeader, &'d mut [u8]),
-    Present(Item<'d>),
+pub enum MaybeItem<'d, S> {
+    Corrupted(ItemHeader<S>, &'d mut [u8]),
+    Erased(ItemHeader<S>, &'d mut [u8]),
+    Present(Item<'d, S>),
 }
 
-impl<'d> core::fmt::Debug for MaybeItem<'d> {
+impl<'d, S: core::fmt::Debug> core::fmt::Debug for MaybeItem<'d, S> {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
             Self::Corrupted(arg0, arg1) => f
@@ -391,8 +418,8 @@ impl<'d> core::fmt::Debug for MaybeItem<'d> {
     }
 }
 
-impl<'d> MaybeItem<'d> {
-    pub fn unwrap<E>(self) -> Result<Item<'d>, Error<E>> {
+impl<'d, S> MaybeItem<'d, S> {
+    pub fn unwrap<E>(self) -> Result<Item<'d, S>, Error<E>> {
         match self {
             MaybeItem::Corrupted(_, _) => Err(Error::Corrupted {
                 #[cfg(feature = "_test")]
@@ -521,7 +548,7 @@ impl ItemIter {
         &mut self,
         flash: &mut S,
         data_buffer: &'m mut [u8],
-    ) -> Result<Option<(Item<'m>, u32)>, Error<S::Error>> {
+    ) -> Result<Option<(Item<'m, S>, u32)>, Error<S::Error>> {
         let mut data_buffer = Some(data_buffer);
         while let (Some(header), address) = self.header.next(flash).await? {
             let buffer = data_buffer.take().unwrap();
@@ -558,7 +585,7 @@ impl ItemHeaderIter {
     pub async fn next<S: NorFlash>(
         &mut self,
         flash: &mut S,
-    ) -> Result<(Option<ItemHeader>, u32), Error<S::Error>> {
+    ) -> Result<(Option<ItemHeader<S>>, u32), Error<S::Error>> {
         self.traverse(flash, |_, _| false).await
     }
 
@@ -569,12 +596,12 @@ impl ItemHeaderIter {
     pub async fn traverse<S: NorFlash>(
         &mut self,
         flash: &mut S,
-        callback: impl Fn(&ItemHeader, u32) -> bool,
-    ) -> Result<(Option<ItemHeader>, u32), Error<S::Error>> {
+        callback: impl Fn(&ItemHeader<S>, u32) -> bool,
+    ) -> Result<(Option<ItemHeader<S>>, u32), Error<S::Error>> {
         loop {
             match ItemHeader::read_new(flash, self.current_address, self.end_address).await {
                 Ok(Some(header)) => {
-                    let next_address = header.next_item_address::<S>(self.current_address);
+                    let next_address = header.next_item_address(self.current_address);
                     if callback(&header, self.current_address) {
                         self.current_address = next_address;
                     } else {
@@ -587,7 +614,7 @@ impl ItemHeaderIter {
                     return Ok((None, self.current_address));
                 }
                 Err(Error::Corrupted { .. }) => {
-                    self.current_address = ItemHeader::data_address::<S>(self.current_address);
+                    self.current_address = ItemHeader::<S>::data_address(self.current_address);
                 }
                 Err(e) => return Err(e),
             }
diff --git a/src/lib.rs b/src/lib.rs
index 567c4ab..879a525 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,7 +12,7 @@ use core::{
     fmt::Debug,
     ops::{Deref, DerefMut, Range},
 };
-use embedded_storage_async::nor_flash::NorFlash;
+use embedded_storage_async::nor_flash::{MultiwriteNorFlash, NorFlash};
 use map::SerializationError;
 
 #[cfg(feature = "arrayvec")]
@@ -32,6 +32,12 @@ pub mod mock_flash;
 /// Many flashes have 4-byte or 1-byte words.
 const MAX_WORD_SIZE: usize = 32;
 
+// TODO: Move this to `embedded-storage`.
+/// Marker trait that guarantees that a word can be cleared to all 0s.
+pub trait WordclearNorFlash: NorFlash {}
+
+impl<T> WordclearNorFlash for T where T: MultiwriteNorFlash {}
+
 /// Resets the flash in the entire given flash range.
 ///
 /// This is just a thin helper function as it just calls the flash's erase function.
@@ -54,7 +60,7 @@ pub async fn erase_all<S: NorFlash>(
 /// The associated data of each item is additionally padded to a full flash word size, but that's not part of this number.  
 /// This means the full item length is `returned number + (data length).next_multiple_of(S::WORD_SIZE)`.
 pub const fn item_overhead_size<S: NorFlash>() -> u32 {
-    item::ItemHeader::data_address::<S>(0)
+    item::ItemHeader::<S>::data_address(0)
 }
 
 // Type representing buffer aligned to 4 byte boundary.
diff --git a/src/map.rs b/src/map.rs
index 5e7f59f..e7c53ae 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -97,8 +97,6 @@
 //! For your convenience there are premade implementations for the [Key] and [Value] traits.
 //!
 
-use embedded_storage_async::nor_flash::MultiwriteNorFlash;
-
 use crate::item::{find_next_free_item_spot, Item, ItemHeader, ItemIter};
 
 use self::{
@@ -161,7 +159,7 @@ async fn fetch_item_with_location<'d, K: Key, S: NorFlash>(
     cache: &mut impl PrivateKeyCacheImpl<K>,
     data_buffer: &'d mut [u8],
     search_key: &K,
-) -> Result<Option<(ItemUnborrowed, u32, Option<usize>)>, Error<S::Error>> {
+) -> Result<Option<(ItemUnborrowed<S>, u32, Option<usize>)>, Error<S::Error>> {
     assert_eq!(flash_range.start % S::ERASE_SIZE as u32, 0);
     assert_eq!(flash_range.end % S::ERASE_SIZE as u32, 0);
     assert!(flash_range.end - flash_range.start >= S::ERASE_SIZE as u32 * 2);
@@ -415,7 +413,7 @@ async fn store_item_inner<'d, K: Key, S: NorFlash>(
             if item_data_length > u16::MAX as usize
                 || item_data_length
                     > calculate_page_size::<S>()
-                        .saturating_sub(ItemHeader::data_address::<S>(0) as usize)
+                        .saturating_sub(ItemHeader::<S>::data_address(0) as usize)
             {
                 cache.unmark_dirty();
                 return Err(Error::ItemTooBig);
@@ -540,7 +538,7 @@ async fn store_item_inner<'d, K: Key, S: NorFlash>(
 /// Also watch out for using integers. This function will take any integer and it's easy to pass the wrong type.
 ///
 /// </div>
-pub async fn remove_item<K: Key, S: MultiwriteNorFlash>(
+pub async fn remove_item<K: Key, S: WordclearNorFlash>(
     flash: &mut S,
     flash_range: Range<u32>,
     cache: &mut impl KeyCacheImpl<K>,
@@ -573,7 +571,7 @@ pub async fn remove_item<K: Key, S: MultiwriteNorFlash>(
 /// *multiple [Value] types. See the module-level docs for more information about this.*
 ///
 /// </div>
-pub async fn remove_all_items<K: Key, S: MultiwriteNorFlash>(
+pub async fn remove_all_items<K: Key, S: WordclearNorFlash>(
     flash: &mut S,
     flash_range: Range<u32>,
     cache: &mut impl KeyCacheImpl<K>,
@@ -587,7 +585,7 @@ pub async fn remove_all_items<K: Key, S: MultiwriteNorFlash>(
 }
 
 /// If `search_key` is None, then all items will be removed
-async fn remove_item_inner<K: Key, S: MultiwriteNorFlash>(
+async fn remove_item_inner<K: Key, S: WordclearNorFlash>(
     flash: &mut S,
     flash_range: Range<u32>,
     cache: &mut impl KeyCacheImpl<K>,
@@ -908,9 +906,7 @@ async fn migrate_items<K: Key, S: NorFlash>(
             found_item
                 .write(flash, flash_range.clone(), cache, next_page_write_address)
                 .await?;
-            next_page_write_address = found_item
-                .header
-                .next_item_address::<S>(next_page_write_address);
+            next_page_write_address = found_item.header.next_item_address(next_page_write_address);
         }
     }
 
diff --git a/src/mock_flash.rs b/src/mock_flash.rs
index 427fc59..a53d9fb 100644
--- a/src/mock_flash.rs
+++ b/src/mock_flash.rs
@@ -1,6 +1,6 @@
 use core::ops::{Add, AddAssign, Range};
 use embedded_storage_async::nor_flash::{
-    ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
+    ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
 };
 
 /// State of a word in the flash.
@@ -16,6 +16,8 @@ enum Writable {
 
 use Writable::*;
 
+use crate::WordclearNorFlash;
+
 /// Base type for in memory flash that can be used for mocking.
 #[derive(Debug, Clone)]
 pub struct MockFlashBase<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize> {
@@ -154,7 +156,7 @@ impl<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize>
             let mut it = crate::item::ItemHeaderIter::new(page_data_start, page_data_end);
             while let (Some(header), item_address) = it.traverse(self, |_, _| false).await.unwrap()
             {
-                let next_item_address = header.next_item_address::<Self>(item_address);
+                let next_item_address = header.next_item_address(item_address);
                 let maybe_item = header
                     .read_item(self, &mut buf, item_address, page_data_end)
                     .await
@@ -198,7 +200,7 @@ impl<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize>
         let mut found_item = None;
         let mut it = crate::item::ItemHeaderIter::new(page_data_start, page_data_end);
         while let (Some(header), item_address) = it.traverse(self, |_, _| false).await.unwrap() {
-            let next_item_address = header.next_item_address::<Self>(item_address);
+            let next_item_address = header.next_item_address(item_address);
 
             if (item_address..next_item_address).contains(&target_item_address) {
                 let maybe_item = header
@@ -255,7 +257,7 @@ impl<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize> R
     }
 }
 
-impl<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize> MultiwriteNorFlash
+impl<const PAGES: usize, const BYTES_PER_WORD: usize, const PAGE_WORDS: usize> WordclearNorFlash
     for MockFlashBase<PAGES, BYTES_PER_WORD, PAGE_WORDS>
 {
 }
diff --git a/src/queue.rs b/src/queue.rs
index a56d965..3d39c98 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -60,7 +60,6 @@ use crate::item::{find_next_free_item_spot, is_page_empty, Item, ItemHeader, Ite
 use self::{cache::CacheImpl, item::ItemUnborrowed};
 
 use super::*;
-use embedded_storage_async::nor_flash::MultiwriteNorFlash;
 
 /// Push data into the queue in the given flash memory with the given range.
 /// The data can only be taken out with the [pop] function.
@@ -110,7 +109,7 @@ async fn push_inner<S: NorFlash>(
     // Data must fit in a single page
     if data.len() > u16::MAX as usize
         || data.len()
-            > calculate_page_size::<S>().saturating_sub(ItemHeader::data_address::<S>(0) as usize)
+            > calculate_page_size::<S>().saturating_sub(ItemHeader::<S>::data_address(0) as usize)
     {
         cache.unmark_dirty();
         return Err(Error::ItemTooBig);
@@ -242,7 +241,7 @@ pub async fn peek<'d, S: NorFlash>(
 /// You should not depend on that data.
 ///
 /// If the data buffer is not big enough an error is returned.
-pub async fn pop<'d, S: MultiwriteNorFlash>(
+pub async fn pop<'d, S: WordclearNorFlash>(
     flash: &mut S,
     flash_range: Range<u32>,
     cache: &mut impl CacheImpl,
@@ -353,7 +352,7 @@ impl<'s, S: NorFlash, CI: CacheImpl> QueueIterator<'s, S, CI> {
     async fn next_inner(
         &mut self,
         data_buffer: &mut [u8],
-    ) -> Result<Option<(ItemUnborrowed, u32)>, Error<S::Error>> {
+    ) -> Result<Option<(ItemUnborrowed<S>, u32)>, Error<S::Error>> {
         let mut data_buffer = Some(data_buffer);
 
         if self.cache.is_dirty() {
@@ -421,7 +420,7 @@ impl<'s, S: NorFlash, CI: CacheImpl> QueueIterator<'s, S, CI> {
 
                 match maybe_item {
                     item::MaybeItem::Corrupted(header, db) => {
-                        let next_address = header.next_item_address::<S>(found_item_address);
+                        let next_address = header.next_item_address(found_item_address);
                         self.next_address = if next_address >= page_data_end_address {
                             NextAddress::PageAfter(current_page)
                         } else {
@@ -431,7 +430,7 @@ impl<'s, S: NorFlash, CI: CacheImpl> QueueIterator<'s, S, CI> {
                     }
                     item::MaybeItem::Erased(_, _) => unreachable!("Item is already erased"),
                     item::MaybeItem::Present(item) => {
-                        let next_address = item.header.next_item_address::<S>(found_item_address);
+                        let next_address = item.header.next_item_address(found_item_address);
                         self.next_address = if next_address >= page_data_end_address {
                             NextAddress::PageAfter(current_page)
                         } else {
@@ -453,7 +452,7 @@ impl<'s, S: NorFlash, CI: CacheImpl> QueueIterator<'s, S, CI> {
 pub struct QueueIteratorEntry<'s, 'd, 'q, S: NorFlash, CI: CacheImpl> {
     iter: &'q mut QueueIterator<'s, S, CI>,
     address: u32,
-    item: Item<'d>,
+    item: Item<'d, S>,
 }
 
 impl<'s, 'd, 'q, S: NorFlash, CI: CacheImpl> Deref for QueueIteratorEntry<'s, 'd, 'q, S, CI> {
@@ -482,7 +481,7 @@ impl<'s, 'd, 'q, S: NorFlash, CI: CacheImpl> QueueIteratorEntry<'s, 'd, 'q, S, C
     /// future peeks won't find this data anymore.
     pub async fn pop(self) -> Result<&'d mut [u8], Error<S::Error>>
     where
-        S: MultiwriteNorFlash,
+        S: WordclearNorFlash,
     {
         let (header, data_buffer) = self.item.destruct();
         let ret = &mut data_buffer[..header.length as usize];
@@ -579,7 +578,7 @@ async fn find_max_fit_inner<S: NorFlash>(
     };
 
     cache.unmark_dirty();
-    Ok(ItemHeader::available_data_bytes::<S>(
+    Ok(ItemHeader::<S>::available_data_bytes(
         page_data_end_address - next_item_address,
     ))
 }
@@ -657,7 +656,7 @@ async fn space_left_inner<S: NorFlash>(
             }
         };
 
-        if ItemHeader::available_data_bytes::<S>(page_data_end_address - next_item_address)
+        if ItemHeader::<S>::available_data_bytes(page_data_end_address - next_item_address)
             .is_none()
         {
             // No data fits on this partial open page anymore.
@@ -1237,7 +1236,7 @@ mod tests {
                 avg_reads: 8.0188,
                 avg_writes: 1.0,
                 avg_bytes_read: 96.4224,
-                avg_bytes_written: 8.0
+                avg_bytes_written: 4.0
             }
         );
     }
@@ -1347,7 +1346,7 @@ mod tests {
                 avg_reads: 82.618,
                 avg_writes: 1.0,
                 avg_bytes_read: 567.9904,
-                avg_bytes_written: 8.0
+                avg_bytes_written: 4.0
             }
         );
     }