diff --git a/src/cache.rs b/src/cache.rs index a6a2042..3920651 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -178,6 +178,7 @@ mod queue_tests { use crate::{ mock_flash::{self, WriteCountCheck}, queue::{peek, pop, push}, + AlignedBuf, }; use super::*; @@ -203,7 +204,7 @@ mod queue_tests { let mut flash = mock_flash::MockFlashBase::::new(WriteCountCheck::Twice, None); const FLASH_RANGE: Range = 0x00..0x400; - let mut data_buffer = [0; 1024]; + let mut data_buffer = AlignedBuf([0; 1024]); for i in 0..LOOP_COUNT { println!("{i}"); @@ -244,3 +245,141 @@ mod queue_tests { (flash.reads, flash.writes, flash.erases) } } + +#[cfg(test)] +mod map_tests { + use crate::{ + map::{fetch_item, store_item, StorageItem}, + mock_flash::{self, WriteCountCheck}, + AlignedBuf, + }; + + use super::*; + use futures_test::test; + + const NUM_PAGES: usize = 4; + + #[test] + async fn no_cache() { + assert_eq!(run_test(NoCache).await, (224161, 5201, 198)); + } + + #[test] + async fn page_state_cache() { + assert_eq!( + run_test(PageStateCache::::new()).await, + (171543, 5201, 198) + ); + } + + #[derive(Debug, PartialEq, Eq)] + struct MockStorageItem { + key: u8, + value: Vec, + } + + #[derive(Debug, PartialEq, Eq)] + enum MockStorageItemError { + BufferTooSmall, + InvalidKey, + BufferTooBig, + } + + impl StorageItem for MockStorageItem { + type Key = u8; + + type Error = MockStorageItemError; + + fn serialize_into(&self, buffer: &mut [u8]) -> Result { + if buffer.len() < 2 + self.value.len() { + return Err(MockStorageItemError::BufferTooSmall); + } + + if self.value.len() > 255 { + return Err(MockStorageItemError::BufferTooBig); + } + + // The serialized value must not be all 0xFF + if self.key == 0xFF { + return Err(MockStorageItemError::InvalidKey); + } + + buffer[0] = self.key; + buffer[1] = self.value.len() as u8; + buffer[2..][..self.value.len()].copy_from_slice(&self.value); + + Ok(2 + self.value.len()) + } + + fn deserialize_from(buffer: &[u8]) -> Result + where + Self: Sized, + { + if buffer.len() < 2 { + return Err(MockStorageItemError::BufferTooSmall); + } + + if buffer[0] == 0xFF { + return Err(MockStorageItemError::InvalidKey); + } + + let len = buffer[1]; + + if buffer.len() < 2 + len as usize { + return Err(MockStorageItemError::BufferTooSmall); + } + + Ok(Self { + key: buffer[0], + value: buffer[2..][..len as usize].to_vec(), + }) + } + + fn key(&self) -> Self::Key { + self.key + } + } + + async fn run_test(mut cache: impl Cache) -> (u32, u32, u32) { + let mut flash = + mock_flash::MockFlashBase::::new(WriteCountCheck::Twice, None); + const FLASH_RANGE: Range = 0x00..0x400; + let mut data_buffer = AlignedBuf([0; 128]); + + const LENGHT_PER_KEY: [usize; 24] = [ + 11, 13, 6, 13, 13, 10, 2, 3, 5, 36, 1, 65, 4, 6, 1, 15, 10, 7, 3, 15, 9, 3, 4, 5, + ]; + + for _ in 0..100 { + for i in 0..24 { + let item = MockStorageItem { + key: i as u8, + value: vec![i as u8; LENGHT_PER_KEY[i]], + }; + + store_item::<_, _>(&mut flash, FLASH_RANGE, &mut cache, &mut data_buffer, item) + .await + .unwrap(); + } + + for i in 0..24 { + let item = fetch_item::( + &mut flash, + FLASH_RANGE, + &mut cache, + &mut data_buffer, + i as u8, + ) + .await + .unwrap() + .unwrap(); + + println!("Fetched {item:?}"); + + assert_eq!(item.value, vec![i as u8; LENGHT_PER_KEY[i]]); + } + } + + (flash.reads, flash.writes, flash.erases) + } +} diff --git a/src/map.rs b/src/map.rs index 8d2519c..e8a53ed 100644 --- a/src/map.rs +++ b/src/map.rs @@ -273,7 +273,7 @@ pub async fn store_item( #[cfg(feature = "defmt")] defmt::trace!("Store item inner. Recursion: {}", recursion_level); - // Check if we're in an infinite recursion which happens when + // Check if we're in an infinite recursion which happens when we don't have enough space to store the new data if recursion_level == get_pages::(flash_range.clone(), 0).count() { return Err(MapError::FullStorage); } @@ -599,8 +599,9 @@ pub async fn try_repair( data_buffer: &mut [u8], ) -> Result<(), MapError> { query.invalidate_cache_state(); + drop(query); - crate::try_general_repair(flash, flash_range.clone(), query).await?; + crate::try_general_repair(flash, flash_range.clone(), &mut NoCache).await?; // Let's check if we corrupted in the middle of a migration if let Some(partial_open_page) = find_first_page( @@ -620,7 +621,7 @@ pub async fn try_repair( { // Yes, the migration got interrupted. Let's redo it. // To do that, we erase the partial open page first because it contains incomplete data. - open_page(flash, flash_range.clone(), query, partial_open_page).await?; + open_page(flash, flash_range.clone(), &mut NoCache, partial_open_page).await?; // Then partially close it again partial_close_page(flash, flash_range.clone(), &mut NoCache, partial_open_page).await?; diff --git a/src/queue.rs b/src/queue.rs index fe84a8e..754454e 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -559,7 +559,10 @@ pub async fn try_repair( flash_range: Range, query: &mut impl Cache, ) -> Result<(), Error> { - crate::try_general_repair(flash, flash_range.clone(), query).await?; + query.invalidate_cache_state(); + drop(query); + + crate::try_general_repair(flash, flash_range.clone(), &mut NoCache).await?; Ok(()) } @@ -792,7 +795,7 @@ mod tests { async fn push_pop_tiny() { let mut flash = MockFlashTiny::new(WriteCountCheck::Twice, None); let flash_range = 0x00..0x40; - let mut data_buffer = [0; 1024]; + let mut data_buffer = AlignedBuf([0; 1024]); for i in 0..2000 { println!("{i}"); @@ -851,7 +854,7 @@ mod tests { async fn push_peek_pop_many() { let mut flash = MockFlashBig::new(WriteCountCheck::Twice, None); let flash_range = 0x000..0x1000; - let mut data_buffer = [0; 1024]; + let mut data_buffer = AlignedBuf([0; 1024]); let mut push_ops = (0, 0, 0, 0); let mut peek_ops = (0, 0, 0, 0); @@ -945,7 +948,7 @@ mod tests { async fn push_lots_then_pop_lots() { let mut flash = MockFlashBig::new(WriteCountCheck::Twice, None); let flash_range = 0x000..0x1000; - let mut data_buffer = [0; 1024]; + let mut data_buffer = AlignedBuf([0; 1024]); let mut push_ops = (0, 0, 0, 0); let mut pop_ops = (0, 0, 0, 0);