Skip to content

Commit 8826bda

Browse files
committed
Merge remote-tracking branch 'tweedegolf/master' into feature/remove-all-items
2 parents 8fffc91 + 61194c5 commit 8826bda

File tree

9 files changed

+121
-12
lines changed

9 files changed

+121
-12
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
## Unreleased
66

7+
- Added a new `map::remove_all_items()` API to remove all stored items in flash.
8+
9+
# 2.0.2 07-05-24
10+
11+
- Added check for too big items that won't ever fit in flash so it returns a good clear error.
12+
13+
# 2.0.1 06-05-24
14+
15+
- Implemented the `get_len` function for all built-in key types
16+
17+
# 2.0.0 06-05-24
18+
719
- *Breaking:* Made the cache API a bit more strict. Caches now always have to be passed as a mutable reference.
820
The API before would lead to a lot of extra unncesessary binary size.
921
- *Breaking:* Removed the `StorageItem` trait in favor of two separate `Key` and `Value` traits. This helps cut
@@ -13,7 +25,6 @@
1325
- Added `erase_all` function as a helper to erase the flash in a region.
1426
- *Breaking:* Changed the way that queue iteration works. Now there's an `iter` function instead of two separate `peek_many` and `pop_many` functions. The new iter returns an entry from which you can get the data that was just peeked. If you want to pop it, then call the pop function on the entry.
1527
- Added `arrayvec` feature that when activated impls the `Key` trait for `ArrayVec` and `ArrayString`.
16-
- Added a new `map::remove_all_items()` API to remove all stored items in flash.
1728

1829
## 1.0.0 01-03-24
1930

@@ -116,4 +127,4 @@
116127

117128
## 0.1.0 - 12-01-23
118129

119-
- Initial release
130+
- Initial release

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sequential-storage"
3-
version = "2.0.0"
3+
version = "2.0.2"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
description = "A crate for storing data in flash with minimal erase cycles."

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ That way you've covered the worst-case execution time for that part of your appl
3939

4040
A cache performance regression might be a bug though. Open an issue to discus your situation if you find a regression.
4141

42+
## MSRV
43+
44+
This crate has no further guarantees other than being able to run on the latest stable compiler.
45+
Increasing the MSRV is not seen as a breaking change semver-wise.
46+
If you find yourself in trouble with this, feel free to open an issue.
47+
48+
## Example
49+
50+
See the `map` and `queue` module level documentation for examples.
51+
4252
## Features
4353

4454
- Key value datastore (Map)

build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("cargo::rustc-check-cfg=cfg(fuzzing_repro)");
3+
}

example/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/item.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,19 +178,19 @@ impl ItemHeader {
178178
}
179179

180180
/// Get the address of the start of the data for this item
181-
pub fn data_address<S: NorFlash>(address: u32) -> u32 {
181+
pub const fn data_address<S: NorFlash>(address: u32) -> u32 {
182182
address + round_up_to_alignment::<S>(Self::LENGTH as u32)
183183
}
184184

185185
/// Get the location of the next item in flash
186-
pub fn next_item_address<S: NorFlash>(&self, address: u32) -> u32 {
186+
pub const fn next_item_address<S: NorFlash>(&self, address: u32) -> u32 {
187187
let data_address = ItemHeader::data_address::<S>(address);
188188
data_address + round_up_to_alignment::<S>(self.length as u32)
189189
}
190190

191191
/// Calculates the amount of bytes available for data.
192192
/// Essentially, it's the given amount minus the header and minus some alignment padding.
193-
pub fn available_data_bytes<S: NorFlash>(total_available: u32) -> Option<u32> {
193+
pub const fn available_data_bytes<S: NorFlash>(total_available: u32) -> Option<u32> {
194194
let data_start = Self::data_address::<S>(0);
195195
let data_end = round_down_to_alignment::<S>(total_available);
196196

src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ const fn calculate_page_index<S: NorFlash>(flash_range: Range<u32>, address: u32
148148
(address - flash_range.start) as usize / S::ERASE_SIZE
149149
}
150150

151+
const fn calculate_page_size<S: NorFlash>() -> usize {
152+
// Page minus the two page status words
153+
S::ERASE_SIZE - S::WORD_SIZE * 2
154+
}
155+
151156
/// The marker being used for page states
152157
const MARKER: u8 = 0;
153158

@@ -366,7 +371,6 @@ pub enum Error<S> {
366371
backtrace: std::backtrace::Backtrace,
367372
},
368373
/// The item cannot be stored anymore because the storage is full.
369-
/// If you get this error some data may be lost.
370374
FullStorage,
371375
/// It's been detected that the memory is likely corrupted.
372376
/// You may want to erase the memory to recover.
@@ -381,6 +385,11 @@ pub enum Error<S> {
381385
BufferTooSmall(usize),
382386
/// A serialization error (from the key or value)
383387
SerializationError(SerializationError),
388+
/// The item does not fit in flash, ever.
389+
/// This is different from [Error::FullStorage] because this item is too big to fit even in empty flash.
390+
///
391+
/// See the readme for more info about the constraints on item sizes.
392+
ItemTooBig,
384393
}
385394

386395
impl<S> From<SerializationError> for Error<S> {
@@ -416,6 +425,7 @@ where
416425
"A provided buffer was to small to be used. Needed was {needed}"
417426
),
418427
Error::SerializationError(value) => write!(f, "Map value error: {value}"),
428+
Error::ItemTooBig => write!(f, "The item is too big to fit in the flash"),
419429
}
420430
}
421431
}

src/map.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,15 @@ async fn store_item_inner<'d, K: Key, S: NorFlash>(
425425
.serialize_into(&mut data_buffer[key_len..])
426426
.map_err(Error::SerializationError)?;
427427

428+
if item_data_length > u16::MAX as usize
429+
|| item_data_length
430+
> calculate_page_size::<S>()
431+
.saturating_sub(ItemHeader::data_address::<S>(0) as usize)
432+
{
433+
cache.unmark_dirty();
434+
return Err(Error::ItemTooBig);
435+
}
436+
428437
let free_spot_address = find_next_free_item_spot(
429438
flash,
430439
flash_range.clone(),
@@ -710,6 +719,10 @@ macro_rules! impl_key_num {
710719
core::mem::size_of::<Self>(),
711720
))
712721
}
722+
723+
fn get_len(_buffer: &[u8]) -> Result<usize, SerializationError> {
724+
Ok(core::mem::size_of::<Self>())
725+
}
713726
}
714727
};
715728
}
@@ -741,6 +754,10 @@ impl<const N: usize> Key for [u8; N] {
741754

742755
Ok((buffer[..N].try_into().unwrap(), N))
743756
}
757+
758+
fn get_len(_buffer: &[u8]) -> Result<usize, SerializationError> {
759+
Ok(N)
760+
}
744761
}
745762

746763
/// The trait that defines how map values are serialized and deserialized.
@@ -1438,4 +1455,34 @@ mod tests {
14381455
.is_none());
14391456
}
14401457
}
1458+
1459+
#[test]
1460+
async fn store_too_big_item() {
1461+
let mut flash = MockFlashBig::new(mock_flash::WriteCountCheck::Twice, None, true);
1462+
const FLASH_RANGE: Range<u32> = 0x000..0x1000;
1463+
1464+
store_item(
1465+
&mut flash,
1466+
FLASH_RANGE,
1467+
&mut cache::NoCache::new(),
1468+
&mut [0; 1024],
1469+
0u8,
1470+
&[0; 1024 - 4 * 2 - 8 - 1],
1471+
)
1472+
.await
1473+
.unwrap();
1474+
1475+
assert_eq!(
1476+
store_item(
1477+
&mut flash,
1478+
FLASH_RANGE,
1479+
&mut cache::NoCache::new(),
1480+
&mut [0; 1024],
1481+
0u8,
1482+
&[0; 1024 - 4 * 2 - 8 - 1 + 1],
1483+
)
1484+
.await,
1485+
Err(Error::ItemTooBig)
1486+
);
1487+
}
14411488
}

src/queue.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ async fn push_inner<S: NorFlash>(
108108
}
109109

110110
// Data must fit in a single page
111-
if data.len()
112-
> ItemHeader::available_data_bytes::<S>((S::ERASE_SIZE - S::WORD_SIZE * 2) as u32).unwrap()
113-
as usize
111+
if data.len() > u16::MAX as usize
112+
|| data.len()
113+
> calculate_page_size::<S>().saturating_sub(ItemHeader::data_address::<S>(0) as usize)
114114
{
115115
cache.unmark_dirty();
116-
return Err(Error::BufferTooBig);
116+
return Err(Error::ItemTooBig);
117117
}
118118

119119
let current_page = find_youngest_page(flash, flash_range.clone(), cache).await?;
@@ -1300,4 +1300,32 @@ mod tests {
13001300
0
13011301
);
13021302
}
1303+
1304+
#[test]
1305+
async fn store_too_big_item() {
1306+
let mut flash = MockFlashBig::new(WriteCountCheck::Twice, None, true);
1307+
const FLASH_RANGE: Range<u32> = 0x000..0x1000;
1308+
1309+
push(
1310+
&mut flash,
1311+
FLASH_RANGE,
1312+
&mut cache::NoCache::new(),
1313+
&[0; 1024 - 4 * 2 - 8],
1314+
false,
1315+
)
1316+
.await
1317+
.unwrap();
1318+
1319+
assert_eq!(
1320+
push(
1321+
&mut flash,
1322+
FLASH_RANGE,
1323+
&mut cache::NoCache::new(),
1324+
&[0; 1024 - 4 * 2 - 8 + 1],
1325+
false,
1326+
)
1327+
.await,
1328+
Err(Error::ItemTooBig)
1329+
);
1330+
}
13031331
}

0 commit comments

Comments
 (0)