Skip to content

Commit

Permalink
Better cache dealing in repair functions.
Browse files Browse the repository at this point in the history
Add test for map with cache
  • Loading branch information
diondokter committed Jan 13, 2024
1 parent 733cc49 commit 07d940e
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 8 deletions.
141 changes: 140 additions & 1 deletion src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ mod queue_tests {
use crate::{
mock_flash::{self, WriteCountCheck},
queue::{peek, pop, push},
AlignedBuf,
};

use super::*;
Expand All @@ -203,7 +204,7 @@ mod queue_tests {
let mut flash =
mock_flash::MockFlashBase::<NUM_PAGES, 1, 256>::new(WriteCountCheck::Twice, None);
const FLASH_RANGE: Range<u32> = 0x00..0x400;
let mut data_buffer = [0; 1024];
let mut data_buffer = AlignedBuf([0; 1024]);

for i in 0..LOOP_COUNT {
println!("{i}");
Expand Down Expand Up @@ -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::<NUM_PAGES>::new()).await,
(171543, 5201, 198)
);
}

#[derive(Debug, PartialEq, Eq)]
struct MockStorageItem {
key: u8,
value: Vec<u8>,
}

#[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<usize, Self::Error> {
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<Self, Self::Error>
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::<NUM_PAGES, 1, 256>::new(WriteCountCheck::Twice, None);
const FLASH_RANGE: Range<u32> = 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::<MockStorageItem, _>(
&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)
}
}
7 changes: 4 additions & 3 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pub async fn store_item<I: StorageItem, S: NorFlash>(
#[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::<S>(flash_range.clone(), 0).count() {
return Err(MapError::FullStorage);
}
Expand Down Expand Up @@ -599,8 +599,9 @@ pub async fn try_repair<I: StorageItem, S: NorFlash>(
data_buffer: &mut [u8],
) -> Result<(), MapError<I::Error, S::Error>> {
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(
Expand All @@ -620,7 +621,7 @@ pub async fn try_repair<I: StorageItem, S: NorFlash>(
{
// 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?;
Expand Down
11 changes: 7 additions & 4 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,10 @@ pub async fn try_repair<S: NorFlash>(
flash_range: Range<u32>,
query: &mut impl Cache,
) -> Result<(), Error<S::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(())
}

Expand Down Expand Up @@ -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}");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 07d940e

Please sign in to comment.