1#![doc = include_str ! ("../README.md")]
2#![cfg_attr(not(target_arch = "x86_64"), no_std)]
3
4pub mod error;
5mod get;
6mod internal;
7pub mod platform;
8mod raw;
9mod set;
10mod u24;
11
12const MAX_KEY_LENGTH: usize = 15;
14const MAX_KEY_NUL_TERMINATED_LENGTH: usize = MAX_KEY_LENGTH + 1;
15
16#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct Key([u8; MAX_KEY_NUL_TERMINATED_LENGTH]);
19
20impl Key {
21 pub const fn from_array<const M: usize>(src: &[u8; M]) -> Self {
28 assert!(M <= MAX_KEY_LENGTH);
29 let mut dst = [0u8; MAX_KEY_NUL_TERMINATED_LENGTH];
30 let mut i = 0;
31 while i < M {
32 dst[i] = src[i];
33 i += 1;
34 }
35 Self(dst)
36 }
37
38 pub const fn from_slice(src: &[u8]) -> Self {
45 assert!(src.len() <= MAX_KEY_LENGTH);
46 let mut dst = [0u8; MAX_KEY_NUL_TERMINATED_LENGTH];
47 let mut i = 0;
48 while i < src.len() {
49 dst[i] = src[i];
50 i += 1;
51 }
52 Self(dst)
53 }
54
55 pub const fn from_str(s: &str) -> Self {
62 let bytes = s.as_bytes();
63 Self::from_slice(bytes)
64 }
65
66 pub const fn as_bytes(&self) -> &[u8; MAX_KEY_NUL_TERMINATED_LENGTH] {
68 &self.0
69 }
70}
71
72impl fmt::Debug for Key {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "Key(b\"")?;
76
77 for &byte in &self.0[..self.0.len() - 1] {
81 if byte == 0 {
83 write!(f, "\\0")?;
84 continue;
85 }
86
87 write!(f, "{}", core::ascii::escape_default(byte))?;
88 }
89
90 write!(f, "\")")
91 }
92}
93
94#[cfg(feature = "defmt")]
95impl defmt::Format for Key {
96 fn format(&self, f: defmt::Formatter) {
97 defmt::write!(f, "Key(b\"");
99
100 for &byte in &self.0[..self.0.len() - 1] {
105 match byte {
106 b'\t' => defmt::write!(f, "\\t"),
107 b'\n' => defmt::write!(f, "\\n"),
108 b'\r' => defmt::write!(f, "\\r"),
109 b'\\' => defmt::write!(f, "\\\\"),
110 b'"' => defmt::write!(f, "\\\""),
111 0x20..=0x7e => defmt::write!(f, "{}", byte as char),
112 _ => defmt::write!(f, "\\x{:02x}", byte),
113 }
114 }
115
116 defmt::write!(f, "\")");
117 }
118}
119
120impl AsRef<[u8]> for Key {
121 fn as_ref(&self) -> &[u8] {
122 self.as_bytes()
123 }
124}
125
126pub use get::Get;
127pub use set::Set;
128
129extern crate alloc;
130
131use crate::error::Error;
132use crate::internal::{ChunkIndex, IterPageItems, ThinPage, VersionOffset};
133use crate::platform::Platform;
134use crate::raw::{ENTRIES_PER_PAGE, FLASH_SECTOR_SIZE, Item, ItemType};
135use alloc::collections::{BTreeMap, BinaryHeap};
136use alloc::vec::Vec;
137use core::fmt;
138
139#[derive(Debug, Clone, PartialEq)]
140pub struct NvsStatistics {
141 pub pages: PageStatistics,
142 pub entries_per_page: Vec<EntryStatistics>,
143 pub entries_overall: EntryStatistics,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub struct PageStatistics {
148 pub empty: u16,
149 pub active: u16,
150 pub full: u16,
151 pub erasing: u16,
152 pub corrupted: u16,
153}
154
155#[derive(Debug, Clone, PartialEq)]
156pub struct EntryStatistics {
157 pub empty: u32,
158 pub written: u32,
159 pub erased: u32,
160 pub illegal: u32,
161}
162
163pub struct Nvs<T: Platform> {
166 pub(crate) hal: T,
167 pub(crate) base_address: usize,
168 pub(crate) sectors: u16,
169 pub(crate) faulted: bool,
170
171 pub(crate) namespaces: BTreeMap<Key, u8>,
173 pub(crate) free_pages: BinaryHeap<ThinPage>,
174 pub(crate) pages: Vec<ThinPage>,
175}
176
177impl<T: Platform> Nvs<T> {
178 pub fn new(partition_offset: usize, partition_size: usize, hal: T) -> Result<Nvs<T>, Error> {
186 if !partition_offset.is_multiple_of(FLASH_SECTOR_SIZE) {
187 return Err(Error::InvalidPartitionOffset);
188 }
189
190 if !partition_size.is_multiple_of(FLASH_SECTOR_SIZE) {
191 return Err(Error::InvalidPartitionSize);
192 }
193
194 let sectors = partition_size / FLASH_SECTOR_SIZE;
195 if sectors > u16::MAX as usize {
196 return Err(Error::InvalidPartitionSize);
197 }
198
199 let mut nvs: Nvs<T> = Self {
200 hal,
201 base_address: partition_offset,
202 sectors: sectors as u16,
203 namespaces: BTreeMap::new(),
204 free_pages: Default::default(),
205 pages: Default::default(),
206 faulted: false,
207 };
208
209 match nvs.load_sectors() {
210 Ok(()) => Ok(nvs),
211 Err(Error::FlashError) => {
212 nvs.faulted = true;
213 Err(Error::FlashError)
214 }
215 Err(e) => Err(e),
216 }
217 }
218
219 pub fn get<R>(&mut self, namespace: &Key, key: &Key) -> Result<R, Error>
225 where
226 Nvs<T>: Get<R>,
227 {
228 match Get::get(self, namespace, key) {
229 Ok(val) => Ok(val),
230 Err(Error::FlashError) => {
231 self.faulted = true;
232 Err(Error::FlashError)
233 }
234 Err(e) => Err(e),
235 }
236 }
237
238 pub fn set<R>(&mut self, namespace: &Key, key: &Key, value: R) -> Result<(), Error>
245 where
246 Nvs<T>: Set<R>,
247 {
248 if self.faulted {
249 return Err(Error::FlashError);
250 }
251
252 match Set::set(self, namespace, key, value) {
253 Ok(()) => Ok(()),
254 Err(Error::FlashError) => {
255 self.faulted = true;
256 Err(Error::FlashError)
257 }
258 Err(e) => Err(e),
259 }
260 }
261
262 pub fn namespaces(&self) -> impl Iterator<Item = &Key> {
264 self.namespaces.keys()
265 }
266
267 pub fn keys(&mut self) -> impl Iterator<Item = Result<(Key, Key), Error>> {
273 IterKeys::new(&self.pages, &mut self.hal, &self.namespaces)
274 }
275
276 pub fn delete(&mut self, namespace: &Key, key: &Key) -> Result<(), Error> {
280 if self.faulted {
281 return Err(Error::FlashError);
282 }
283
284 if key.0[MAX_KEY_LENGTH] != b'\0' {
285 return Err(Error::KeyMalformed);
286 }
287 if namespace.0[MAX_KEY_LENGTH] != b'\0' {
288 return Err(Error::NamespaceMalformed);
289 }
290
291 let namespace_index = match self.namespaces.get(namespace) {
292 Some(&idx) => idx,
293 None => return Ok(()), };
295 let result = self.delete_key(namespace_index, key, ChunkIndex::Any);
296 match result {
297 Err(Error::KeyNotFound) => Ok(()),
298 Err(Error::FlashError) => {
299 self.faulted = true;
300 Err(Error::FlashError)
301 }
302 other => other,
303 }
304 }
305
306 pub fn statistics(&mut self) -> Result<NvsStatistics, Error> {
308 if self.faulted {
309 return Err(Error::FlashError);
310 }
311
312 let mut page_stats = PageStatistics {
313 empty: 0,
314 active: 0,
315 full: 0,
316 erasing: 0,
317 corrupted: 0,
318 };
319
320 let mut all_pages: Vec<&ThinPage> = Vec::with_capacity(self.sectors as _);
321 all_pages.extend(self.pages.iter());
322 all_pages.extend(self.free_pages.iter());
323 all_pages.sort_by_key(|page| page.address);
325
326 let entries_per_page = all_pages
327 .into_iter()
328 .map(|page| {
329 match page.get_state() {
330 internal::ThinPageState::Active => page_stats.active += 1,
331 internal::ThinPageState::Full => page_stats.full += 1,
332 internal::ThinPageState::Freeing => page_stats.erasing += 1,
333 internal::ThinPageState::Corrupt => page_stats.corrupted += 1,
334 internal::ThinPageState::Invalid => page_stats.corrupted += 1,
335 internal::ThinPageState::Uninitialized => page_stats.empty += 1,
336 }
337
338 if *page.get_state() == internal::ThinPageState::Corrupt {
339 EntryStatistics {
340 empty: 0,
341 written: 0,
342 erased: 0,
343 illegal: ENTRIES_PER_PAGE as _,
344 }
345 } else {
346 let (empty, written, erased, illegal) = page.get_entry_statistics();
347 EntryStatistics {
348 empty,
349 written,
350 erased,
351 illegal,
352 }
353 }
354 })
355 .collect::<Vec<_>>();
356
357 let entries_overall = entries_per_page.iter().fold(
358 EntryStatistics {
359 empty: 0,
360 written: 0,
361 erased: 0,
362 illegal: 0,
363 },
364 |acc, x| EntryStatistics {
365 empty: acc.empty + x.empty,
366 written: acc.written + x.written,
367 erased: acc.erased + x.erased,
368 illegal: acc.illegal + x.illegal,
369 },
370 );
371
372 Ok(NvsStatistics {
373 pages: page_stats,
374 entries_per_page,
375 entries_overall,
376 })
377 }
378}
379
380struct IterLoadedItems<'a, T: Platform> {
381 pages: &'a [ThinPage],
382 current: Option<IterPageItems<'a, T>>,
383}
384
385impl<'a, T: Platform> IterLoadedItems<'a, T> {
386 fn new(mut pages: &'a [ThinPage], hal: &'a mut T) -> Self {
387 let first = pages.split_off_first();
388
389 Self {
390 pages,
391 current: first.map(|page| page.items(hal)),
392 }
393 }
394}
395
396impl<'a, T: Platform> Iterator for IterLoadedItems<'a, T> {
397 type Item = Result<Item, Error>;
398
399 fn next(&mut self) -> Option<Self::Item> {
400 let current = self.current.as_mut()?;
402
403 while current.is_empty() {
405 let next_page = self.pages.split_off_first()?;
406
407 current.switch_to_page(next_page);
408 }
409
410 current.next()
411 }
412}
413
414struct IterKeys<'a, T: Platform> {
415 items: IterLoadedItems<'a, T>,
416 namespaces: &'a BTreeMap<Key, u8>,
417}
418
419impl<'a, T: Platform> IterKeys<'a, T> {
420 fn new(pages: &'a [ThinPage], hal: &'a mut T, namespaces: &'a BTreeMap<Key, u8>) -> Self {
421 Self {
422 items: IterLoadedItems::new(pages, hal),
423 namespaces,
424 }
425 }
426
427 fn item_to_keys(&self, item: Item) -> (Key, Key) {
428 let (namespace_key, _) = self
429 .namespaces
430 .iter()
431 .find(|(_, idx)| **idx == item.namespace_index)
432 .unwrap();
434
435 (*namespace_key, item.key)
436 }
437}
438
439impl<'a, T: Platform> Iterator for IterKeys<'a, T> {
440 type Item = Result<(Key, Key), Error>;
441
442 fn next(&mut self) -> Option<Self::Item> {
443 loop {
444 return match self.items.next()? {
445 Ok(item) => {
446 if item.namespace_index == 0
448 || item.type_ == ItemType::Blob
449 || item.type_ == ItemType::BlobIndex
450 {
451 continue;
452 }
453
454 if item.type_ == ItemType::BlobData
455 && item.chunk_index != VersionOffset::V0 as u8
456 && item.chunk_index != VersionOffset::V1 as u8
457 {
458 continue;
459 }
460
461 Some(Ok(self.item_to_keys(item)))
462 }
463 Err(err) => Some(Err(err)),
464 };
465 }
466 }
467}