diff --git a/src/item.rs b/src/item.rs index 2b15ede..6361fb0 100644 --- a/src/item.rs +++ b/src/item.rs @@ -178,19 +178,19 @@ impl ItemHeader { } /// Get the address of the start of the data for this item - pub fn data_address(address: u32) -> u32 { + pub const fn data_address(address: u32) -> u32 { address + round_up_to_alignment::(Self::LENGTH as u32) } /// Get the location of the next item in flash - pub fn next_item_address(&self, address: u32) -> u32 { + pub const fn next_item_address(&self, address: u32) -> u32 { let data_address = ItemHeader::data_address::(address); data_address + round_up_to_alignment::(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 fn available_data_bytes(total_available: u32) -> Option { + pub const fn available_data_bytes(total_available: u32) -> Option { let data_start = Self::data_address::(0); let data_end = round_down_to_alignment::(total_available); diff --git a/src/lib.rs b/src/lib.rs index 21db652..e021a45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,6 +148,11 @@ const fn calculate_page_index(flash_range: Range, address: u32 (address - flash_range.start) as usize / S::ERASE_SIZE } +const fn calculate_page_size() -> usize { + // Page minus the two page status words + S::ERASE_SIZE - S::WORD_SIZE * 2 +} + /// The marker being used for page states const MARKER: u8 = 0; @@ -366,7 +371,6 @@ pub enum Error { backtrace: std::backtrace::Backtrace, }, /// The item cannot be stored anymore because the storage is full. - /// If you get this error some data may be lost. FullStorage, /// It's been detected that the memory is likely corrupted. /// You may want to erase the memory to recover. @@ -381,6 +385,9 @@ pub enum Error { BufferTooSmall(usize), /// A serialization error (from the key or value) SerializationError(SerializationError), + /// The item does not fit in flash, ever. + /// This is different from [Error::FullStorage] because this item is too big to fit even in empty flash. + ItemTooBig, } impl From for Error { @@ -416,6 +423,7 @@ where "A provided buffer was to small to be used. Needed was {needed}" ), Error::SerializationError(value) => write!(f, "Map value error: {value}"), + Error::ItemTooBig => write!(f, "The item is too big to fit in the flash"), } } } diff --git a/src/map.rs b/src/map.rs index f8862e4..51b8a78 100644 --- a/src/map.rs +++ b/src/map.rs @@ -425,6 +425,15 @@ async fn store_item_inner<'d, K: Key, S: NorFlash>( .serialize_into(&mut data_buffer[key_len..]) .map_err(Error::SerializationError)?; + if item_data_length > u16::MAX as usize + || item_data_length + > calculate_page_size::() + .saturating_sub(ItemHeader::data_address::(0) as usize) + { + cache.unmark_dirty(); + return Err(Error::ItemTooBig); + } + let free_spot_address = find_next_free_item_spot( flash, flash_range.clone(), @@ -1347,4 +1356,34 @@ mod tests { .is_none()); } } + + #[test] + async fn store_too_big_item() { + let mut flash = MockFlashBig::new(mock_flash::WriteCountCheck::Twice, None, true); + const FLASH_RANGE: Range = 0x000..0x1000; + + store_item( + &mut flash, + FLASH_RANGE, + &mut cache::NoCache::new(), + &mut [0; 1024], + 0u8, + &[0; 1024 - 4 * 2 - 8 - 1], + ) + .await + .unwrap(); + + assert_eq!( + store_item( + &mut flash, + FLASH_RANGE, + &mut cache::NoCache::new(), + &mut [0; 1024], + 0u8, + &[0; 1024 - 4 * 2 - 8 - 1 + 1], + ) + .await, + Err(Error::ItemTooBig) + ); + } } diff --git a/src/queue.rs b/src/queue.rs index a4efe6a..02eced7 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -108,12 +108,12 @@ async fn push_inner( } // Data must fit in a single page - if data.len() - > ItemHeader::available_data_bytes::((S::ERASE_SIZE - S::WORD_SIZE * 2) as u32).unwrap() - as usize + if data.len() > u16::MAX as usize + || data.len() + > calculate_page_size::().saturating_sub(ItemHeader::data_address::(0) as usize) { cache.unmark_dirty(); - return Err(Error::BufferTooBig); + return Err(Error::ItemTooBig); } let current_page = find_youngest_page(flash, flash_range.clone(), cache).await?; @@ -1300,4 +1300,32 @@ mod tests { 0 ); } + + #[test] + async fn store_too_big_item() { + let mut flash = MockFlashBig::new(WriteCountCheck::Twice, None, true); + const FLASH_RANGE: Range = 0x000..0x1000; + + push( + &mut flash, + FLASH_RANGE, + &mut cache::NoCache::new(), + &[0; 1024 - 4 * 2 - 8], + false, + ) + .await + .unwrap(); + + assert_eq!( + push( + &mut flash, + FLASH_RANGE, + &mut cache::NoCache::new(), + &[0; 1024 - 4 * 2 - 8 + 1], + false, + ) + .await, + Err(Error::ItemTooBig) + ); + } }