acid/firmware/acid-firmware/src/db/mod.rs

320 lines
9 KiB
Rust
Raw Normal View History

2026-01-24 21:12:25 +01:00
use core::{
iter::Chain,
ops::{Deref, DerefMut, Range},
};
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
2026-01-25 18:43:07 +01:00
use ekv::{
Database, ReadTransaction,
flash::{Flash, PageID},
};
2026-01-24 21:12:25 +01:00
use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition};
2026-01-31 15:36:36 +01:00
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
2026-01-24 21:12:25 +01:00
use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash};
use esp_hal::rng::Trng;
use esp_storage::FlashStorage;
2026-01-25 18:43:07 +01:00
use log::{debug, info};
2026-01-24 21:12:25 +01:00
pub type PartitionAcid =
Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>;
// Workaround for alignment requirements.
#[repr(C, align(4))]
struct AlignedBuf<const N: usize>(pub [u8; N]);
pub struct EkvFlash<T> {
flash: T,
buffer: Box<AlignedBuf<{ ekv::config::PAGE_SIZE }>>,
}
impl<T> EkvFlash<T> {
fn new(flash: T) -> Self {
Self {
flash,
buffer: {
// Allocate the buffer directly on the heap.
let buffer = Box::new_zeroed();
unsafe { buffer.assume_init() }
},
}
}
}
impl<T> Deref for EkvFlash<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.flash
}
}
impl<T> DerefMut for EkvFlash<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.flash
}
}
impl<T: NorFlash + ReadNorFlash> ekv::flash::Flash for EkvFlash<T> {
type Error = T::Error;
fn page_count(&self) -> usize {
ekv::config::MAX_PAGE_COUNT
}
async fn erase(
&mut self,
page_id: PageID,
) -> Result<(), <EkvFlash<T> as ekv::flash::Flash>::Error> {
self.flash
.erase(
(page_id.index() * ekv::config::PAGE_SIZE) as u32,
((page_id.index() + 1) * ekv::config::PAGE_SIZE) as u32,
)
.await
}
async fn read(
&mut self,
page_id: PageID,
offset: usize,
data: &mut [u8],
) -> Result<(), <EkvFlash<T> as ekv::flash::Flash>::Error> {
let address = page_id.index() * ekv::config::PAGE_SIZE + offset;
self.flash
.read(address as u32, &mut self.buffer.0[..data.len()])
.await?;
data.copy_from_slice(&self.buffer.0[..data.len()]);
Ok(())
}
async fn write(
&mut self,
page_id: PageID,
offset: usize,
data: &[u8],
) -> Result<(), <EkvFlash<T> as ekv::flash::Flash>::Error> {
let address = page_id.index() * ekv::config::PAGE_SIZE + offset;
self.buffer.0[..data.len()].copy_from_slice(data);
self.flash
.write(address as u32, &self.buffer.0[..data.len()])
.await
}
}
pub struct AcidDatabase {
db: Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>,
}
impl Deref for AcidDatabase {
type Target = Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>;
fn deref(&self) -> &Self::Target {
&self.db
}
}
impl DerefMut for AcidDatabase {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.db
}
}
impl AcidDatabase {
pub async fn mount(flash: PartitionAcid) -> AcidDatabase {
let mut db_config = ekv::Config::default();
db_config.random_seed = Trng::try_new()
.expect("A `TrngSource` was not initialized before constructing this `Trng`.")
.random();
let db = Database::<_, esp_sync::RawMutex>::new(EkvFlash::new(flash), db_config);
#[cfg(feature = "format-db")]
{
2026-01-25 01:45:25 +01:00
log::warn!("Formatting EKV database...");
2026-01-24 21:12:25 +01:00
db.format()
.await
.unwrap_or_else(|error| panic!("Failed to format the EKV database: {error:?}"));
2026-01-25 01:45:25 +01:00
log::warn!("EKV database formatted successfully.");
2026-01-24 21:12:25 +01:00
}
match db.mount().await {
Ok(()) => info!("EKV database mounted."),
Err(error) => panic!("Failed to mount the EKV database: {error:?}"),
};
Self { db }
}
}
type DbPathSegment<'a> = Cow<'a, str>;
type DbPathBuf<'a> = Vec<DbPathSegment<'a>>;
type DbPath<'a> = [DbPathSegment<'a>];
2026-01-25 01:45:25 +01:00
pub struct DbKey {
2026-01-24 21:47:21 +01:00
/// Segments separated by `0x00`, with the whole thing suffixed with `[0x00, 0xFF]`.
bytes: Vec<u8>,
}
2026-01-24 21:12:25 +01:00
impl Deref for DbKey {
type Target = [u8];
fn deref(&self) -> &Self::Target {
2026-01-24 21:47:21 +01:00
&self.bytes[0..(self.bytes.len() - 2)]
2026-01-24 21:12:25 +01:00
}
}
impl DbKey {
2026-01-25 01:45:25 +01:00
pub fn from_raw(mut key: Vec<u8>) -> Self {
2026-01-24 21:47:21 +01:00
key.extend_from_slice(&[0x00, 0xFF]);
Self { bytes: key }
2026-01-24 21:12:25 +01:00
}
2026-01-25 01:45:25 +01:00
pub fn new<'a>(path: impl IntoIterator<Item = DbPathSegment<'a>>) -> Self {
2026-01-24 21:12:25 +01:00
// Null bytes are not allowed within path segments, and will cause a panic.
2026-01-24 21:47:21 +01:00
// Bytes of `0xFF` cannot appear in valid UTF-8.
// The byte vector stored in a `DbKey` is formed by interspersing the segments with `0x00`, and suffixing the key with `[0x00, 0xFF]`.
// This lets us represent three significant keys with a single allocation:
// * The key for querying the path itself: `bytes[..bytes.len() - 2]`, e.g. `b"first\x00second"`
// * The keys for range-querying the path's children:
// * Start (inclusive): `bytes[..bytes.len() - 1]`, e.g. `b"first\x00second\x00"`
// * End (exclusive): `bytes[..]`, e.g. `b"first\x00second\x00\xFF"`
2026-01-24 21:12:25 +01:00
let mut bytes = Vec::new();
for segment in path {
assert!(
!segment.as_bytes().contains(&0x00),
"A path segment must not contain null bytes."
);
2026-01-24 21:47:21 +01:00
// No need to check for `0xFF` bytes in UTF-8 strings.
2026-01-24 21:12:25 +01:00
bytes.extend_from_slice(segment.as_bytes());
2026-01-24 21:47:21 +01:00
bytes.push(0x00);
2026-01-24 21:12:25 +01:00
}
2026-01-25 01:45:25 +01:00
assert!(!bytes.is_empty(), "An empty path is not a valid path.");
bytes.push(0xFF);
2026-01-24 21:12:25 +01:00
2026-01-24 21:47:21 +01:00
DbKey { bytes }
2026-01-24 21:12:25 +01:00
}
2026-01-25 01:45:25 +01:00
pub fn range_of_children(&self) -> Range<&[u8]> {
2026-01-24 21:47:21 +01:00
(&self.bytes[0..(self.bytes.len() - 1)])..(&self.bytes[..])
2026-01-24 21:12:25 +01:00
}
2026-01-25 01:45:25 +01:00
pub fn segments(&self) -> impl Iterator<Item = DbPathSegment<'_>> {
2026-01-24 21:12:25 +01:00
struct SegmentIterator<'a> {
rest: &'a [u8],
}
impl<'a> Iterator for SegmentIterator<'a> {
type Item = DbPathSegment<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(end_index) = self.rest.iter().position(|byte| *byte == 0) {
let segment = &self.rest[..end_index];
let segment = str::from_utf8(segment).unwrap();
2026-01-24 21:47:21 +01:00
self.rest = &self.rest[end_index + 1..];
2026-01-24 21:12:25 +01:00
Some(Cow::Borrowed(segment))
} else {
None
}
}
}
SegmentIterator {
2026-01-24 21:47:21 +01:00
rest: self.bytes.as_slice(),
2026-01-24 21:12:25 +01:00
}
}
}
pub struct DbPathSpectreUsers;
impl<'a> IntoIterator for DbPathSpectreUsers {
type Item = DbPathSegment<'static>;
type IntoIter = core::array::IntoIter<DbPathSegment<'static>, 2>;
fn into_iter(self) -> Self::IntoIter {
[
DbPathSegment::Borrowed("spectre"),
DbPathSegment::Borrowed("users"),
]
.into_iter()
}
}
pub struct DbPathSpectreUserSites<'a> {
2026-01-25 18:43:07 +01:00
pub username: DbPathSegment<'a>,
2026-01-24 21:12:25 +01:00
}
impl<'a> IntoIterator for DbPathSpectreUserSites<'a> {
type Item = DbPathSegment<'a>;
type IntoIter = core::array::IntoIter<DbPathSegment<'a>, 4>;
fn into_iter(self) -> Self::IntoIter {
[
DbPathSegment::Borrowed("spectre"),
DbPathSegment::Borrowed("user"),
self.username,
DbPathSegment::Borrowed("site"),
]
.into_iter()
}
}
pub struct DbPathSpectreUserSite<'a> {
2026-01-25 18:43:07 +01:00
pub user_sites: DbPathSpectreUserSites<'a>,
pub site: DbPathSegment<'a>,
2026-01-24 21:12:25 +01:00
}
impl<'a> IntoIterator for DbPathSpectreUserSite<'a> {
type Item = DbPathSegment<'a>;
type IntoIter =
Chain<core::array::IntoIter<DbPathSegment<'a>, 4>, core::iter::Once<DbPathSegment<'a>>>;
fn into_iter(self) -> Self::IntoIter {
self.user_sites
.into_iter()
.chain(core::iter::once(self.site))
}
}
2026-01-25 18:43:07 +01:00
pub trait ReadTransactionExt<F>
where
F: Flash,
{
async fn read_to_vec<'b>(
&self,
key: &[u8],
buffer: &'b mut Vec<u8>,
) -> Result<&'b mut [u8], ekv::ReadError<F::Error>>;
}
impl<'a, F, M> ReadTransactionExt<F> for ReadTransaction<'a, F, M>
where
F: Flash + 'a,
M: embassy_sync_old::blocking_mutex::raw::RawMutex + 'a,
{
async fn read_to_vec<'b>(
&self,
key: &[u8],
buffer: &'b mut Vec<u8>,
) -> Result<&'b mut [u8], ekv::ReadError<F::Error>> {
2026-01-31 15:36:36 +01:00
if buffer.is_empty() {
2026-01-25 18:43:07 +01:00
buffer.resize(1, 0);
}
loop {
match self.read(key, buffer.as_mut_slice()).await {
Ok(size) => break Ok(&mut buffer[..size]),
Err(ekv::ReadError::BufferTooSmall) => {
let new_size = buffer.len() * 2;
debug!("Resizing read buffer to {new_size} bytes.");
buffer.resize(new_size, 0)
}
Err(error) => break Err(error),
}
}
}
}