1use crate::error::Error;
2use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
3use lru::LruCache;
4use std::fs::{File, OpenOptions};
5use std::io::prelude::{Read as StdRead, Seek as StdSeek, Write as StdWrite};
6use std::io::ErrorKind;
7use std::num::NonZeroUsize;
8use std::path::{Path, PathBuf};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum FileStrategy {
13 Single,
15 Multi,
18}
19
20pub struct StdStorage {
24 strategy: FileStrategy,
25 db_name: String,
26 base_path: PathBuf,
27 sec_size: u32,
28 capacity: u32,
29 file_cache: LruCache<u32, File>,
30}
31
32impl StdStorage {
33 pub fn new<P: AsRef<Path>>(
35 path: P,
36 db_name: &str,
37 sec_size: u32,
38 capacity: u32,
39 strategy: FileStrategy,
40 ) -> Result<Self, std::io::Error> {
41 let base_path = path.as_ref().to_path_buf();
42 if strategy == FileStrategy::Multi {
43 std::fs::create_dir_all(&base_path)?;
44 } else {
45 if let Some(parent) = base_path.parent() {
47 std::fs::create_dir_all(parent)?;
48 }
49 }
50 Ok(Self {
51 strategy,
52 db_name: db_name.to_string(),
53 sec_size,
54 capacity,
55 base_path,
56 file_cache: LruCache::new(NonZeroUsize::new(8).unwrap()),
57 })
58 }
59
60 fn get_file_and_offset(&mut self, addr: u32) -> Result<(&mut File, u64), std::io::Error> {
62 let (sector_index, offset_in_file) = match self.strategy {
63 FileStrategy::Single => (0, addr as u64),
64 FileStrategy::Multi => {
65 let sector_index = addr / self.sec_size;
66 let offset = (addr % self.sec_size) as u64;
67 (sector_index, offset)
68 }
69 };
70
71 if !self.file_cache.contains(§or_index) {
72 let file_path = match self.strategy {
73 FileStrategy::Single => self.base_path.clone(),
74 FileStrategy::Multi => self
75 .base_path
76 .join(format!("{}.fdb.{}", self.db_name, sector_index)),
77 };
78 let file = OpenOptions::new()
79 .read(true)
80 .write(true)
81 .create(true)
82 .open(&file_path)?;
83 self.file_cache.put(sector_index, file);
84 }
85
86 let file = self.file_cache.get_mut(§or_index).unwrap();
87 Ok((file, offset_in_file))
88 }
89}
90
91impl ErrorType for StdStorage {
92 type Error = Error;
93}
94
95impl ReadNorFlash for StdStorage {
96 const READ_SIZE: usize = 1;
97
98 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
99 let (file, file_offset) = self.get_file_and_offset(offset)?;
100 file.seek(std::io::SeekFrom::Start(file_offset))?;
101 match file.read_exact(bytes) {
102 Ok(_) => Ok(()),
103 Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
104 bytes.fill(0xFF);
106 Ok(())
107 }
108 Err(e) => Err(e.into()),
109 }
110 }
111
112 fn capacity(&self) -> usize {
113 self.capacity as usize
114 }
115}
116
117impl NorFlash for StdStorage {
118 const WRITE_SIZE: usize = 1;
119 const ERASE_SIZE: usize = 4096; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
122 let size = to - from;
123 let (sector_index, offset) = match self.strategy {
125 FileStrategy::Single => (0, from as u64),
126 FileStrategy::Multi => {
127 if from % self.sec_size != 0 || size != self.sec_size {
128 return Err(Error::InvalidArgument);
129 }
130 (from / self.sec_size, 0)
131 }
132 };
133
134 self.file_cache.pop(§or_index);
135 let file_path = match self.strategy {
136 FileStrategy::Single => self.base_path.clone(),
137 FileStrategy::Multi => self
138 .base_path
139 .join(format!("{}.fdb.{}", self.db_name, sector_index)),
140 };
141
142 let mut file = OpenOptions::new().write(true).create(true).open(&file_path)?;
143
144 if self.strategy == FileStrategy::Multi {
145 file.set_len(0)?;
146 }
147
148 file.seek(std::io::SeekFrom::Start(offset))?;
149 let buf = vec![0xFF; size as usize];
151 file.write_all(&buf)?;
152 file.flush()?;
153 Ok(())
154 }
155
156 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
157 let (file, file_offset) = self.get_file_and_offset(offset)?;
158 file.seek(std::io::SeekFrom::Start(file_offset))?;
159 file.write_all(bytes)?;
160 file.flush()?;
161 Ok(())
162 }
163}