191 lines
5.9 KiB
Rust
191 lines
5.9 KiB
Rust
#![no_std]
|
|
|
|
use core::{fmt::Write, mem::size_of, str};
|
|
use byteorder::{ByteOrder, BigEndian};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
mod error;
|
|
pub use error::{Error, ReadError, WriteError};
|
|
mod iter;
|
|
use iter::Iter;
|
|
mod fmt;
|
|
use fmt::FmtWrapper;
|
|
pub mod no_flash;
|
|
mod test;
|
|
|
|
pub trait StoreBackend {
|
|
type Data: Sized + AsRef<[u8]> + Clone;
|
|
|
|
/// Memory-mapped
|
|
fn data(&self) -> &Self::Data;
|
|
fn len(&self) -> usize {
|
|
size_of::<Self::Data>()
|
|
}
|
|
|
|
type Error;
|
|
fn erase(&mut self) -> Result<(), Self::Error>;
|
|
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error>;
|
|
/// called after repeated `program()` invocations to allow for eg. cache flushing
|
|
fn program_done(&mut self) {}
|
|
}
|
|
|
|
/// Simple Flash Key Value Store
|
|
pub struct Store<B: StoreBackend> {
|
|
backend: B,
|
|
}
|
|
|
|
impl<B: StoreBackend> Store<B> {
|
|
/// wrap a `StoreBackend` into a store
|
|
pub fn new(backend: B) -> Self {
|
|
Store { backend }
|
|
}
|
|
|
|
/// read from `key`
|
|
pub fn read(&self, key: &str) -> Result<Option<&[u8]>, ReadError> {
|
|
let mut iter = Iter::new(self.backend.data().as_ref());
|
|
let mut value = None;
|
|
while let Some(result) = iter.next() {
|
|
let (record_key, record_value) = result?;
|
|
if key.as_bytes() == record_key {
|
|
// last write wins
|
|
value = Some(record_value)
|
|
}
|
|
}
|
|
Ok(value)
|
|
}
|
|
|
|
/// read from `key`, decode UTF-8
|
|
pub fn read_str(&self, key: &str) -> Result<Option<&str>, ReadError> {
|
|
self.read(key)
|
|
.and_then(|value| value
|
|
.map(|value| str::from_utf8(value)
|
|
.map_err(ReadError::Utf8Error)
|
|
)
|
|
.transpose())
|
|
}
|
|
|
|
/// read from `key`, decode with `postcard`
|
|
pub fn read_value<'a, T: Deserialize<'a>>(&'a self, key: &str) -> Result<Option<T>, Error<B::Error>> {
|
|
self.read(key)?
|
|
.map(
|
|
|data| postcard::from_bytes(data)
|
|
.map_err(Error::Decode)
|
|
).transpose()
|
|
}
|
|
|
|
/// how many bytes are currently used
|
|
///
|
|
/// equally, offset of free space
|
|
pub fn get_bytes_used(&self) -> Result<usize, ReadError> {
|
|
let mut iter = Iter::new(self.backend.data().as_ref());
|
|
while let Some(result) = iter.next() {
|
|
let _ = result?;
|
|
}
|
|
Ok(iter.offset)
|
|
}
|
|
|
|
unsafe fn append_at(&mut self, mut offset: usize,
|
|
key: &[u8], value: &[u8]) -> Result<usize, WriteError<B::Error>> {
|
|
let record_size = 4 + key.len() + 1 + value.len();
|
|
if offset + record_size > self.backend.len() {
|
|
return Err(WriteError::SpaceExhausted)
|
|
}
|
|
|
|
let mut record_size_bytes = [0u8; 4];
|
|
BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32);
|
|
|
|
{
|
|
let mut write = |payload| -> Result<(), WriteError<B::Error>> {
|
|
self.backend.program(offset, payload)
|
|
.map_err(WriteError::Backend)?;
|
|
offset += payload.len();
|
|
Ok(())
|
|
};
|
|
|
|
write(&record_size_bytes[..])?;
|
|
write(key)?;
|
|
write(&[0])?;
|
|
write(value)?;
|
|
}
|
|
self.backend.program_done();
|
|
|
|
Ok(offset)
|
|
}
|
|
|
|
fn compact(&mut self) -> Result<(), Error<B::Error>> {
|
|
let old_data = self.backend.data().clone();
|
|
|
|
self.erase()?;
|
|
|
|
// This is worst-case quadratic, but we're limited by a small SPI flash sector size,
|
|
// so it does not really matter.
|
|
let mut offset = 0;
|
|
let mut iter = Iter::new(old_data.as_ref());
|
|
'iter: while let Some(result) = iter.next() {
|
|
let (key, value) = result?;
|
|
if value.is_empty() {
|
|
// This is a removed entry, ignore it.
|
|
continue
|
|
}
|
|
|
|
let mut next_iter = iter.clone();
|
|
while let Some(next_result) = next_iter.next() {
|
|
let (next_key, _) = next_result?;
|
|
if key == next_key {
|
|
// There's another entry that overwrites this one, ignore this one.
|
|
continue 'iter
|
|
}
|
|
}
|
|
offset = unsafe { self.append_at(offset, key, value)? };
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn append(&mut self, key: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
|
|
let free_offset = self.get_bytes_used()?;
|
|
unsafe { self.append_at(free_offset, key.as_bytes(), value)? };
|
|
Ok(())
|
|
}
|
|
|
|
/// store a buffer `value` at `key`
|
|
pub fn write(&mut self, key: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
|
|
match self.append(key, value) {
|
|
Err(Error::Write(WriteError::SpaceExhausted)) => {
|
|
self.compact()?;
|
|
self.append(key, value)
|
|
}
|
|
res => res
|
|
}
|
|
}
|
|
|
|
/// serialize 32-bit as ASCII, store at `key`
|
|
pub fn write_int(&mut self, key: &str, value: u32) -> Result<(), Error<B::Error>> {
|
|
let mut buf = [0; 16];
|
|
let mut wrapper = FmtWrapper::new(&mut buf);
|
|
write!(&mut wrapper, "{}", value).unwrap();
|
|
self.write(key, wrapper.contents())
|
|
}
|
|
|
|
/// encode with `postcard`, write at `key`
|
|
pub fn write_value<T: Serialize>(&mut self, key: &str, value: T) -> Result<(), Error<B::Error>> {
|
|
let mut buf = [0; 64];
|
|
let data = postcard::to_slice(&value, &mut buf)
|
|
.map_err(Error::Encode)?;
|
|
self.write(key, data)
|
|
}
|
|
|
|
/// store a 0-byte tombstone value at `key`
|
|
pub fn remove(&mut self, key: &str) -> Result<(), Error<B::Error>> {
|
|
self.write(key, &[])
|
|
}
|
|
|
|
/// invokes erase on the backend (this is no compaction!)
|
|
pub fn erase(&mut self) -> Result<(), WriteError<B::Error>> {
|
|
self.backend.erase()
|
|
.map_err(WriteError::Backend)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|