embedded_savegame/
mock.rs

1//! Mock flash implementations for testing
2//!
3//! This module provides mock flash devices that can be used for testing without
4//! real hardware. Available with the `mock` feature.
5//!
6//! - [`MockFlash`]: Simple byte-addressable mock flash (like EEPROM)
7//! - [`SectorMockFlash`]: Sector-based mock flash (like NOR flash)
8//! - [`MeasuredMockFlash`]: Mock flash that tracks operation statistics
9
10use crate::storage::Flash;
11use core::convert::Infallible;
12
13/// Simple mock flash device with byte-level operations
14///
15/// Simulates EEPROM-like flash where individual bytes can be written.
16/// Initialized with all bytes set to 0xFF (erased state).
17#[derive(Debug, PartialEq)]
18pub struct MockFlash<const SIZE: usize> {
19    data: [u8; SIZE],
20}
21
22impl<const SIZE: usize> MockFlash<SIZE> {
23    pub const fn new() -> Self {
24        Self { data: [0xFF; SIZE] }
25    }
26}
27
28impl<const SIZE: usize> Default for MockFlash<SIZE> {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl<const SIZE: usize> Flash for MockFlash<SIZE> {
35    type Error = Infallible;
36
37    fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
38        let addr = addr as usize;
39        let len = buf.len();
40        buf.copy_from_slice(&self.data[addr..addr + len]);
41        Ok(())
42    }
43
44    fn write(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Self::Error> {
45        let addr = addr as usize;
46        let len = data.len();
47        self.data[addr..addr + len].copy_from_slice(data);
48        Ok(())
49    }
50
51    fn erase(&mut self, addr: u32) -> Result<(), Self::Error> {
52        self.data[addr as usize] = 0xFF;
53        Ok(())
54    }
55}
56
57/// Sector-based mock flash device
58///
59/// Simulates NOR flash where writes can only set bits from 1 to 0, and entire
60/// sectors must be erased to set bits back to 1. This more accurately models
61/// real NOR flash behavior.
62#[derive(Debug, PartialEq)]
63pub struct SectorMockFlash<const SECTOR_SIZE: usize, const SECTOR_COUNT: usize> {
64    data: [[u8; SECTOR_SIZE]; SECTOR_COUNT],
65}
66
67impl<const SECTOR_SIZE: usize, const SECTOR_COUNT: usize>
68    SectorMockFlash<SECTOR_SIZE, SECTOR_COUNT>
69{
70    pub const fn new() -> Self {
71        Self {
72            data: [[0xFF; SECTOR_SIZE]; SECTOR_COUNT],
73        }
74    }
75
76    fn div_rem(addr: u32) -> (usize, usize) {
77        let addr = addr as usize;
78        let sector = addr / SECTOR_SIZE;
79        let offset = addr % SECTOR_SIZE;
80        (sector, offset)
81    }
82}
83
84impl<const SECTOR_SIZE: usize, const SECTOR_COUNT: usize> Default
85    for SectorMockFlash<SECTOR_SIZE, SECTOR_COUNT>
86{
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92impl<const SECTOR_SIZE: usize, const SECTOR_COUNT: usize> Flash
93    for SectorMockFlash<SECTOR_SIZE, SECTOR_COUNT>
94{
95    type Error = Infallible;
96
97    fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
98        let (sector, offset) = Self::div_rem(addr);
99        buf.copy_from_slice(&self.data[sector][offset..offset + buf.len()]);
100        Ok(())
101    }
102
103    fn write(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
104        let (sector, offset) = Self::div_rem(addr);
105
106        let mut flash = self.data[sector][offset..offset + buf.len()].iter_mut();
107        for byte in buf {
108            let flash_byte = flash.next().unwrap();
109            *flash_byte &= *byte;
110        }
111
112        Ok(())
113    }
114
115    fn erase(&mut self, addr: u32) -> Result<(), Self::Error> {
116        let (sector, _offset) = Self::div_rem(addr);
117        self.data[sector] = [0xFF; SECTOR_SIZE];
118        Ok(())
119    }
120}
121
122/// Mock flash device that tracks operation statistics
123///
124/// Wraps [`MockFlash`] and counts the number of bytes read/written and erase operations.
125/// Useful for analyzing storage efficiency and optimization.
126#[derive(Debug, Default)]
127pub struct MeasuredMockFlash<const SIZE: usize> {
128    flash: MockFlash<SIZE>,
129    /// Statistics for all flash operations performed
130    pub stats: MeasuredStats,
131}
132
133/// Statistics for flash operations
134///
135/// Tracks the total number of bytes read/written and erase operations.
136#[derive(Debug, Default, PartialEq)]
137pub struct MeasuredStats {
138    /// Total number of bytes read
139    pub read: usize,
140    /// Total number of bytes written
141    pub write: usize,
142    /// Total number of erase operations
143    pub erase: usize,
144}
145
146impl<const SIZE: usize> MeasuredMockFlash<SIZE> {
147    pub fn new() -> Self {
148        Self::default()
149    }
150}
151
152impl<const SIZE: usize> Flash for MeasuredMockFlash<SIZE> {
153    type Error = Infallible;
154
155    fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
156        self.stats.read = self.stats.read.saturating_add(buf.len());
157        self.flash.read(addr, buf)
158    }
159
160    fn write(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Self::Error> {
161        self.stats.write = self.stats.write.saturating_add(data.len());
162        self.flash.write(addr, data)
163    }
164
165    fn erase(&mut self, addr: u32) -> Result<(), Self::Error> {
166        self.stats.erase = self.stats.erase.saturating_add(1);
167        self.flash.erase(addr)
168    }
169}