flashdb_rs/kvdb/
mod.rs

1mod reader;
2pub use reader::*;
3mod types;
4pub use types::*;
5mod iter;
6pub use iter::*;
7
8use crate::{
9    fdb_blob, fdb_blob__bindgen_ty_1, fdb_blob_read, fdb_db_t, fdb_kv, fdb_kv_del, fdb_kv_get_obj,
10    fdb_kv_set_blob, fdb_kv_set_default, fdb_kvdb, fdb_kvdb_control_read, fdb_kvdb_control_write,
11    fdb_kvdb_deinit, fdb_kvdb_init, Error, FlashDispatch, RawHandle, FDB_KVDB_CTRL_SET_MAX_SIZE,
12    FDB_KVDB_CTRL_SET_NOT_FORMAT, FDB_KVDB_CTRL_SET_SEC_SIZE, FDB_KV_NAME_MAX,
13};
14use core::{
15    ffi::{c_char, c_void, CStr},
16    marker::PhantomData,
17};
18
19use embedded_storage::nor_flash::NorFlash;
20
21pub struct KVDB<S: NorFlash> {
22    inner: fdb_kvdb,
23    storage: S,
24    user_data: FlashDispatch,
25    key_buf: [u8; FDB_KV_NAME_MAX as usize + 1],
26    #[cfg(feature = "log")]
27    name_buf: [u8; FDB_KV_NAME_MAX as usize + 1],
28    initialized: bool,
29    // 由于fdb_kvdb内部引用了 storage 和 name_buf 所以结构体无法移动,否则会导致悬空指针
30    _marker: PhantomData<*const ()>, // for !Send and !Sync
31}
32
33#[cfg(feature = "std")]
34impl KVDB<crate::storage::StdStorage> {
35    /// 在 `std` 环境下,创建一个基于文件的 KVDB 实例。
36    ///
37    /// 此函数返回一个 `Box<KVDB<StdStorage>>`,以确保数据库实例在内存中的地址是稳定的,
38    /// 防止因栈帧移动导致传递给 C 库的内部指针失效。
39    ///
40    /// # 参数
41    /// - `name`: 数据库名称
42    /// - `path`: 数据库文件存储的目录
43    /// - `sec_size`: 扇区大小
44    /// - `max_size`: 数据库最大容量
45    /// - `default_kvs`: 可选的默认键值对
46    pub fn new_file(
47        name: &str,
48        path: &str,
49        sec_size: u32,
50        max_size: u32,
51        default_kvs: Option<&'static crate::fdb_default_kv>,
52    ) -> Result<Box<Self>, Error> {
53        let storage = crate::storage::StdStorage::new(
54            path,
55            name,
56            sec_size,
57            max_size,
58            crate::storage::FileStrategy::Multi,
59        )?;
60
61        let mut db = Box::new(KVDB::new(storage));
62        db.set_name(name)?;
63        db.init(default_kvs)?;
64        Ok(db)
65    }
66}
67
68impl<S: NorFlash> KVDB<S> {
69    /// 创建一个未初始化的 KVDB 实例。
70    ///
71    /// 在 `no_std` 环境下,这是创建数据库实例的主要方式。
72    /// 实例创建后,必须调用 `.init()` 方法才能使用。
73    ///
74    /// # 参数
75    /// * `storage` - 一个实现了 `embedded_storage::nor_flash::NorFlash` trait 的存储后端实例。
76    pub fn new(storage: S) -> Self {
77        Self {
78            inner: Default::default(),
79            storage,
80            user_data: FlashDispatch::new::<S>(),
81            key_buf: [0; FDB_KV_NAME_MAX as usize + 1],
82            #[cfg(feature = "log")]
83            name_buf: [0; FDB_KV_NAME_MAX as usize + 1],
84            initialized: false,
85            _marker: PhantomData,
86        }
87    }
88
89    /// 设置数据库名称,仅用于日志输出。
90    ///
91    /// **注意**: 此方法必须在 `init()` 之前调用。
92    pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
93        if name.len() > FDB_KV_NAME_MAX as usize {
94            return Err(Error::KvNameError);
95        }
96        #[cfg(feature = "log")]
97        {
98            self.name_buf[..name.len()].copy_from_slice(name.as_bytes());
99            self.name_buf[name.len()] = b'\0';
100        }
101        Ok(())
102    }
103
104    /// 设置数据库为不可格式化模式。
105    ///
106    /// 在此模式下,如果数据库初始化时发现头部信息损坏,将返回错误而不是自动格式化。
107    /// **注意**: 此方法必须在 `init()` 之前调用。
108    pub fn set_not_formatable(&mut self, enable: bool) {
109        self.fdb_kvdb_control_write(FDB_KVDB_CTRL_SET_NOT_FORMAT, enable);
110    }
111
112    /// 检查数据库是否处于不可格式化模式。
113    pub fn not_formatable(&mut self) -> bool {
114        let mut enable = false;
115        self.fdb_kvdb_control_read(FDB_KVDB_CTRL_SET_NOT_FORMAT, &mut enable);
116        return enable;
117    }
118    /// 初始化数据库。
119    ///
120    /// 此方法会加载现有数据库或根据 `storage` 的容量创建一个新的数据库。
121    /// 它是实际与底层 C 库交互的入口。
122    ///
123    /// # 参数
124    /// - `default_kvs`: (可选) 提供一组默认键值对。如果数据库是首次创建,
125    ///   这些键值对将被写入数据库。
126    pub fn init(
127        &mut self,
128        default_kvs: Option<&'static crate::fdb_default_kv>,
129    ) -> Result<(), Error> {
130        if self.initialized {
131            return Ok(());
132        }
133        // 从 NorFlash trait 获取扇区大小和总容量
134        let sec_size = S::ERASE_SIZE as u32;
135        let max_size = self.storage.capacity() as u32;
136
137        unsafe {
138            let db_ptr = self.handle() as fdb_db_t;
139            (*db_ptr).mode = crate::fdb_storage_type_FDB_STORAGE_CUSTOM;
140
141            // 设置 flashdb 的配置
142            self.fdb_kvdb_control_write(FDB_KVDB_CTRL_SET_SEC_SIZE, sec_size);
143            self.fdb_kvdb_control_write(FDB_KVDB_CTRL_SET_MAX_SIZE, max_size);
144
145            // 只有这里获取才不会导致悬空指针
146            self.user_data.instance = &mut self.storage as *mut _ as *mut c_void;
147
148            #[cfg(feature = "log")]
149            let name = self.name_buf.as_ptr() as *const c_char;
150            #[cfg(not(feature = "log"))]
151            let name = b"\0".as_ptr() as *const c_char;
152
153            let default_kvs_ptr = match default_kvs {
154                Some(kvs) => kvs as *const _ as *mut _,
155                None => core::ptr::null_mut(),
156            };
157
158            let result = fdb_kvdb_init(
159                db_ptr as *mut fdb_kvdb,
160                name,
161                core::ptr::null(),
162                default_kvs_ptr,
163                &mut self.user_data as *mut _ as *mut c_void,
164            );
165
166            if result == crate::fdb_err_t_FDB_NO_ERR {
167                self.initialized = true;
168                Ok(())
169            } else {
170                Err(result.into())
171            }
172        }
173    }
174}
175impl<S: NorFlash> KVDB<S> {
176    /// 内部辅助函数:将 &str 转换为 CStr
177    fn to_cstr(&mut self, key: &str) -> Result<&CStr, Error> {
178        let key_len = key.len();
179        if key_len > FDB_KV_NAME_MAX as usize {
180            return Err(Error::KvNameError);
181        }
182        self.key_buf[..key_len].copy_from_slice(key.as_bytes());
183        self.key_buf[key_len] = 0;
184        // 安全:我们刚刚确保了 key_buf 是一个有效的以 null 结尾的字符串
185        Ok(unsafe { CStr::from_bytes_with_nul_unchecked(&self.key_buf[..key_len + 1]) })
186    }
187
188    /// 内部方法:获取键对应的KV对象
189    #[inline]
190    fn fdb_kv_get_obj(&mut self, key: &str) -> Result<Option<KVEntry>, Error> {
191        let handle = self.handle();
192        let cstr_key = self.to_cstr(key)?;
193        let mut kv_obj = unsafe { core::mem::zeroed::<fdb_kv>() };
194        // 调用底层C函数获取KV对象
195        if unsafe { fdb_kv_get_obj(handle, cstr_key.as_ptr(), &mut kv_obj) }
196            == core::ptr::null_mut()
197        {
198            return Ok(None);
199        };
200        Ok(Some(kv_obj.into()))
201    }
202
203    /// 内部方法:通过blob写入键值对
204    #[inline]
205    fn fdb_blob_write(&mut self, key: &str, blob: &mut fdb_blob) -> Result<(), Error> {
206        let handle = self.handle();
207        let cstr_key = self.to_cstr(key)?;
208        Error::convert(unsafe { fdb_kv_set_blob(handle, cstr_key.as_ptr(), blob) })
209    }
210
211    /// 内部方法:从blob读取数据
212    #[inline]
213    fn fdb_blob_read(&mut self, blob: &mut fdb_blob) -> usize {
214        unsafe { fdb_blob_read(self.handle() as *mut _, blob) }
215    }
216
217    /// 内部方法:控制数据库写入操作
218    #[inline]
219    fn fdb_kvdb_control_write<T>(&mut self, cmd: u32, arg: T) {
220        fdb_kvdb_control_write(self.handle(), cmd, arg)
221    }
222
223    /// 内部方法:控制数据库读取操作
224    #[inline]
225    #[allow(dead_code)]
226    fn fdb_kvdb_control_read<T>(&self, cmd: u32, arg: &mut T) {
227        fdb_kvdb_control_read(self.handle(), cmd, arg)
228    }
229}
230
231impl<S: NorFlash> KVDB<S> {
232    /// 存储一个键值对。
233    ///
234    /// 如果键已存在,其值将被覆盖。
235    ///
236    /// # 参数
237    /// - `key`: 键
238    /// - `value`: 值,一个字节切片。
239    pub fn set(&mut self, key: &str, value: &[u8]) -> Result<(), Error> {
240        let mut blob = fdb_blob_make_write(value); // 创建写入用的blob结构
241        self.fdb_blob_write(key, &mut blob)
242    }
243
244    /// 根据键获取其值。
245    ///
246    /// # 参数
247    /// - `key`: 要查询的键。
248    ///
249    /// # 返回
250    /// - `Ok(Some(Vec<u8>))`: 找到键,返回其值。
251    /// - `Ok(None)`: 未找到键。
252    /// - `Err(Error)`: 读取时发生错误。
253    #[cfg(feature = "alloc")]
254    pub fn get(&mut self, key: &str) -> Result<Option<alloc::vec::Vec<u8>>, Error> {
255        match self.fdb_kv_get_obj(key)? {
256            Some(kv) => match kv.status() {
257                // 处理预写入或已写入状态的值
258                KVStatus::PRE_WRITE | KVStatus::Write => {
259                    // 初始化缓冲区
260
261                    let mut data: alloc::vec::Vec<u8> =
262                        alloc::vec::Vec::with_capacity(kv.value_len());
263                    unsafe { data.set_len(kv.value_len()) }; // 预分配缓冲区大小
264
265                    // 创建读取用的blob结构
266                    let mut blob = fdb_blob_make_by(&mut data, &kv.into(), 0);
267
268                    // 读取数据
269                    let read_len = self.fdb_blob_read(&mut blob);
270                    if read_len != data.len() {
271                        return Err(Error::ReadError);
272                    }
273                    Ok(Some(data))
274                }
275                _ => Ok(None), // 其他状态(如已删除)返回None
276            },
277            None => return Ok(None), // 键不存在
278        }
279    }
280
281    /// 删除一个键值对。
282    ///
283    /// 这是一个逻辑删除,数据占用的空间将在未来的垃圾回收 (GC) 过程中被回收。
284    pub fn delete(&mut self, key: &str) -> Result<(), Error> {
285        let handle = self.handle();
286        let cstr_key = self.to_cstr(key)?;
287        Error::convert(unsafe { fdb_kv_del(handle, cstr_key.as_ptr()) })
288    }
289
290    /// 重置数据库到其默认状态。
291    ///
292    /// 如果初始化时提供了默认键值对,数据库将恢复到这些值。
293    /// 否则,数据库将被清空。
294    ///
295    /// **警告**: 此操作会删除所有当前数据。
296    pub fn reset(&mut self) -> Result<(), Error> {
297        Error::convert(unsafe { fdb_kv_set_default(self.handle()) })
298    }
299
300    /// 获取一个用于流式读取键值的 `KVReader`。
301    ///
302    /// 这对于读取大尺寸的值非常有用,可以避免一次性将整个值加载到内存中。
303    pub fn get_reader<'a>(&'_ mut self, key: &str) -> Result<KVReader<'_, S>, Error> {
304        let handle = self.handle();
305        let cstr_key = self.to_cstr(key)?;
306        let mut kv_obj = unsafe { core::mem::zeroed::<fdb_kv>() };
307        if unsafe { fdb_kv_get_obj(handle, cstr_key.as_ptr(), &mut kv_obj) }
308            == core::ptr::null_mut()
309        {
310            return Err(Error::ReadError);
311        };
312
313        Ok(KVReader::new(self, kv_obj.into()))
314    }
315
316    pub fn iter(&mut self) -> KVDBIterator<'_, S> {
317        KVDBIterator::new(self)
318    }
319}
320
321impl<S: NorFlash> RawHandle for KVDB<S> {
322    type Handle = *mut fdb_kvdb;
323    fn handle(&self) -> Self::Handle {
324        &self.inner as *const _ as *mut _
325    }
326}
327
328impl<S: NorFlash> Drop for KVDB<S> {
329    fn drop(&mut self) {
330        if self.initialized {
331            unsafe {
332                fdb_kvdb_deinit(self.handle());
333            }
334        }
335    }
336}
337
338pub fn fdb_blob_make_by(v: &mut [u8], kv: &KVEntry, offset: usize) -> fdb_blob {
339    fdb_blob {
340        buf: v.as_mut_ptr() as *mut _,
341        size: v.len(),
342        saved: fdb_blob__bindgen_ty_1 {
343            meta_addr: kv.inner.addr.start,
344            addr: kv.inner.addr.value + offset as u32,
345            len: kv.inner.value_len as usize - offset,
346        },
347    }
348}
349
350pub fn fdb_blob_make_read(v: &mut [u8]) -> fdb_blob {
351    fdb_blob {
352        buf: v.as_mut_ptr() as *mut _,
353        size: v.len(),
354        saved: unsafe { core::mem::zeroed() },
355    }
356}
357pub fn fdb_blob_make_write(v: &[u8]) -> fdb_blob {
358    fdb_blob {
359        buf: v.as_ptr() as *const _ as *mut _,
360        size: v.len(),
361        saved: unsafe { core::mem::zeroed() },
362    }
363}