Skip to main content

esp_nvs/
mem_flash.rs

1//! A minimal in-memory NOR flash implementation for host-side tooling and tests.
2//!
3//! [`MemFlash`] implements [`embedded_storage::nor_flash::NorFlash`] and
4//! [`crate::platform::Crc`], making it a fully functional [`crate::platform::Platform`]
5//! that can be used with [`crate::Nvs`] on any host platform without hardware
6//! dependencies.
7
8use alloc::vec;
9use alloc::vec::Vec;
10
11use embedded_storage::nor_flash::{
12    ErrorType,
13    NorFlash,
14    NorFlashError,
15    NorFlashErrorKind,
16    ReadNorFlash,
17};
18
19use crate::FLASH_SECTOR_SIZE;
20use crate::platform::{
21    Crc,
22    software_crc32,
23};
24
25const WORD_SIZE: usize = 4;
26
27/// In-memory NOR flash that simulates real flash semantics:
28///
29/// - Erased state is all `0xFF`.
30/// - Writes can only flip bits from 1 → 0 (bitwise AND).
31/// - Erases restore a full sector to `0xFF`.
32/// - Read/write alignment is 4 bytes (word size).
33/// - Erase granularity is 4096 bytes (sector size).
34pub struct MemFlash {
35    buf: Vec<u8>,
36}
37
38impl MemFlash {
39    /// Create a fresh flash of the given number of pages, filled with `0xFF`.
40    pub fn new(pages: usize) -> Self {
41        Self {
42            buf: vec![0xFF; FLASH_SECTOR_SIZE * pages],
43        }
44    }
45
46    /// Wrap existing binary data as a flash image.
47    ///
48    /// The data length must be a multiple of [`FLASH_SECTOR_SIZE`].
49    ///
50    /// # Panics
51    /// Panics if `data.len()` is not a multiple of `FLASH_SECTOR_SIZE`.
52    pub fn from_bytes(data: Vec<u8>) -> Self {
53        assert!(
54            data.len().is_multiple_of(FLASH_SECTOR_SIZE),
55            "MemFlash data length {} is not a multiple of sector size {}",
56            data.len(),
57            FLASH_SECTOR_SIZE
58        );
59        Self { buf: data }
60    }
61
62    /// Consume the flash and return the underlying buffer.
63    pub fn into_inner(self) -> Vec<u8> {
64        self.buf
65    }
66
67    /// Return the total size of the flash in bytes.
68    pub fn len(&self) -> usize {
69        self.buf.len()
70    }
71
72    /// Returns whether the flash is empty (zero bytes).
73    pub fn is_empty(&self) -> bool {
74        self.buf.is_empty()
75    }
76}
77
78#[derive(Debug)]
79pub struct MemFlashError;
80
81impl NorFlashError for MemFlashError {
82    fn kind(&self) -> NorFlashErrorKind {
83        NorFlashErrorKind::Other
84    }
85}
86
87impl ErrorType for MemFlash {
88    type Error = MemFlashError;
89}
90
91impl ReadNorFlash for MemFlash {
92    const READ_SIZE: usize = WORD_SIZE;
93
94    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
95        let offset = offset as usize;
96        bytes.copy_from_slice(&self.buf[offset..offset + bytes.len()]);
97        Ok(())
98    }
99
100    fn capacity(&self) -> usize {
101        self.buf.len()
102    }
103}
104
105impl NorFlash for MemFlash {
106    const WRITE_SIZE: usize = WORD_SIZE;
107    const ERASE_SIZE: usize = FLASH_SECTOR_SIZE;
108
109    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
110        for addr in from..to {
111            self.buf[addr as usize] = 0xFF;
112        }
113        Ok(())
114    }
115
116    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
117        let offset = offset as usize;
118        for (i, &val) in bytes.iter().enumerate() {
119            // Real NOR flash can only flip bits from 1 to 0
120            self.buf[offset + i] &= val;
121        }
122        Ok(())
123    }
124}
125
126impl Crc for MemFlash {
127    fn crc32(init: u32, data: &[u8]) -> u32 {
128        software_crc32(init, data)
129    }
130}