|
| 1 | +--- |
| 2 | +id: database |
| 3 | +title: 闪存数据库 |
| 4 | +sidebar_position: 5 |
| 5 | +--- |
| 6 | + |
| 7 | +# Database 闪存数据库 |
| 8 | + |
| 9 | +LibXR 提供了两种轻量级的嵌入式键值数据库实现:`DatabaseRawSequential` 和 `DatabaseRaw<N>`。 |
| 10 | +它们均继承自抽象接口类 `Database`,用于嵌入式 Flash 等顺序写入存储介质,具备主备冗余、断电保护、类型安全封装,适配不同的存储对齐约束。 |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## 主要功能 |
| 15 | + |
| 16 | +- 支持主/备块数据冗余与校验,断电后可自动恢复; |
| 17 | +- 提供统一接口 `Database` 与模板封装 `Database::Key<T>`,支持类型安全读写; |
| 18 | +- 两种实现模式: |
| 19 | + - `DatabaseRawSequential`:顺序写入,支持任意数据长度; |
| 20 | + - `DatabaseRaw<N>`:页对齐写入,适用于 NOR Flash 等要求写入对齐的场景; |
| 21 | +- 所有数据操作通过 `Save()` 写入,`Restore()` 清空,支持完整恢复流程。 |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 使用示例 |
| 26 | + |
| 27 | +### 创建数据库对象 |
| 28 | + |
| 29 | +```cpp |
| 30 | +LinuxBinaryFileFlash<2048> flash("/tmp/flash.bin", 512, 8); |
| 31 | +Database& db = *(new DatabaseRawSequential(flash)); |
| 32 | +``` |
| 33 | +
|
| 34 | +或使用页对齐版本: |
| 35 | +
|
| 36 | +```cpp |
| 37 | +LinuxBinaryFileFlash<2048> flash2("/tmp/flash2.bin", 512, 16); |
| 38 | +Database& db = *(new DatabaseRaw<16>(flash2)); |
| 39 | +``` |
| 40 | + |
| 41 | +### 定义类型安全的键 |
| 42 | + |
| 43 | +```cpp |
| 44 | +int value = 42; |
| 45 | +Database::Key<int> key(db, "my_key", value); |
| 46 | + |
| 47 | +// 写入新值 |
| 48 | +key = 123; |
| 49 | + |
| 50 | +// 读取回变量 |
| 51 | +key.Load(); |
| 52 | +printf("value = %d\n", static_cast<int>(key)); |
| 53 | +``` |
| 54 | +
|
| 55 | +--- |
| 56 | +
|
| 57 | +## 类型安全封装:Key |
| 58 | +
|
| 59 | +模板类 `Database::Key<T>` 提供类型安全的键值封装,构造时会尝试从数据库中加载键值,若不存在则添加新键。 |
| 60 | +
|
| 61 | +```cpp |
| 62 | +struct Config { uint32_t baudrate; uint8_t mode; }; |
| 63 | +Database::Key<Config> cfg(db, "uart_cfg", {9600, 1}); |
| 64 | +
|
| 65 | +// 写入配置 |
| 66 | +cfg = {115200, 0}; |
| 67 | +
|
| 68 | +// 加载配置 |
| 69 | +cfg.Load(); |
| 70 | +``` |
| 71 | + |
| 72 | +- 支持所有 **可平铺内存的 POD 类型**(如结构体、数组、基本类型); |
| 73 | +- 赋值操作 `key = value` 会自动更新数据库; |
| 74 | +- 可通过 `key.Load()` 显式从数据库刷新内容。 |
| 75 | + |
| 76 | +--- |
| 77 | + |
| 78 | +## 方法一览(基类接口) |
| 79 | + |
| 80 | +以下为抽象接口 `Database` 及其模板类 `Key<T>` 提供的完整方法: |
| 81 | + |
| 82 | +| 方法/操作 | 功能描述 | |
| 83 | +|----------------------------------|----------------------------------------------------------------| |
| 84 | +| `Database::Add(KeyBase&)` | 添加键值(仅在数据库不存在该键时调用) | |
| 85 | +| `Database::Set(KeyBase&, RawData)` | 更新键值内容,要求名称和大小一致 | |
| 86 | +| `Database::Get(KeyBase&)` | 从数据库加载键值到内存 | |
| 87 | +| `Key<T>::Set(const T&)` | 设置键值并写入数据库 | |
| 88 | +| `Key<T>::Load()` | 从数据库加载键值至变量 | |
| 89 | +| `Key<T>::operator=(const T&)` | 设置键值(等效于 `Set()`) | |
| 90 | +| `Key<T>::operator T()` | 类型转换操作,返回当前变量值(不自动加载) | |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## 工作机制概述 |
| 95 | + |
| 96 | +尽管不同实现的底层机制有所区别,但统一遵循如下设计原则: |
| 97 | + |
| 98 | +- 使用主块和备块交替写入,确保写入过程可恢复; |
| 99 | +- 所有键值存储以原始名值对序列化,并带有校验位; |
| 100 | +- `Init()` 内部自动判断有效块并尝试恢复数据; |
| 101 | +- `Restore()` 可主动清空主备数据并初始化空数据库; |
| 102 | +- 每个派生类自动处理页对齐、可用空间等底层逻辑,用户无需关心。 |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## 注意事项 |
| 107 | + |
| 108 | +- 请优先使用 `Database::Key<T>` 类型封装进行读写; |
| 109 | +- 写入仅在调用 `key = val` 或 `key.Set(val)` 时发生; |
| 110 | +- 对所有键值数据类型要求为 POD 且可拷贝存储; |
| 111 | +- 若存储已满或写入失败,`Set()` 会返回对应错误码; |
| 112 | +- 若需手动释放数据库对象,注意释放派生类指针。 |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## 应用场景 |
| 117 | + |
| 118 | +- 嵌入式设备参数存储; |
| 119 | +- 状态断点续存; |
| 120 | +- 多模块配置持久化; |
| 121 | +- 无动态内存环境下的安全键值存储; |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +## 示例测试 |
| 126 | + |
| 127 | +详见 [`test_database.cpp`],展示了键值读写、结构体支持、掉电恢复等场景的完整用例。 |
0 commit comments