sfkv/src/lib.rs

168 lines
5.0 KiB
Rust

#![no_std]
use core::{fmt::Write, mem::size_of, str};
use byteorder::{ByteOrder, BigEndian};
mod error;
pub use error::{Error, ReadError, WriteError};
mod iter;
use iter::Iter;
mod fmt;
use fmt::FmtWrapper;
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())
}
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 = {
let mut iter = Iter::new(self.backend.data().as_ref());
while let Some(result) = iter.next() {
let _ = result?;
}
iter.offset
};
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())
}
/// store a 0-byte tombstone value at `key`
pub fn remove(&mut self, key: &str) -> Result<(), Error<B::Error>> {
self.write(key, &[])
}
pub fn erase(&mut self) -> Result<(), WriteError<B::Error>> {
self.backend.erase()
.map_err(WriteError::Backend)?;
Ok(())
}
}