Compare commits

..

No commits in common. "91d69474eda5191f3782be52fc089254d3179b5a" and "57758cdc9a0ab7725bb50aa90e3a15d174526fef" have entirely different histories.

7 changed files with 74 additions and 172 deletions

View File

@ -11,11 +11,5 @@ categories = ["embedded", "database"]
license = "GPL-3"
edition = "2018"
[features]
default = ["postcard-values"]
postcard-values = ["postcard", "serde"]
[dependencies]
byteorder = { version = "1", default-features = false }
serde = { version = "1.0", default-features = false, optional = true }
postcard = { version = "0.5", optional = true }
byteorder = { version = "1.0", default-features = false }

View File

@ -11,7 +11,15 @@ automatically erasing and rewriting when full.
For details see `trait StoreBackend`.
## Ideas
## TODO
* NoFlash backend
* read_int()
* write_str()
* automatic value coercion
* support for floats
### Ideas
* iterator (quadratic)
* compaction to a second backend instead on stack

View File

@ -1,13 +1,9 @@
use core::{fmt, str};
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error<B> {
Read(ReadError),
Write(WriteError<B>),
#[cfg(feature = "postcard-values")]
Encode(postcard::Error),
#[cfg(feature = "postcard-values")]
Decode(postcard::Error),
}
impl<B> From<ReadError> for Error<B> {
@ -58,11 +54,11 @@ pub enum WriteError<B> {
impl<B: fmt::Display> fmt::Display for WriteError<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
match self {
&WriteError::SpaceExhausted =>
write!(f, "space exhausted"),
&WriteError::Backend(err) =>
write!(f, "{}", err)
&WriteError::Backend(ref e) =>
e.fmt(f),
}
}
}

30
src/fmt.rs Normal file
View File

@ -0,0 +1,30 @@
use core::fmt;
pub struct FmtWrapper<'a> {
buf: &'a mut [u8],
offset: usize,
}
impl<'a> FmtWrapper<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
FmtWrapper {
buf: buf,
offset: 0,
}
}
pub fn contents(&self) -> &[u8] {
&self.buf[..self.offset]
}
}
impl<'a> fmt::Write for FmtWrapper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let remainder = &mut self.buf[self.offset..];
let remainder = &mut remainder[..bytes.len()];
remainder.copy_from_slice(bytes);
self.offset += bytes.len();
Ok(())
}
}

View File

@ -1,21 +1,19 @@
//#![no_std]
#![no_std]
use core::{mem::size_of, str};
use core::{fmt::Write, mem::size_of, str};
use byteorder::{ByteOrder, BigEndian};
#[cfg(feature = "postcard-values")]
use serde::{Deserialize, Serialize};
mod error;
pub use error::{Error, ReadError, WriteError};
mod iter;
use iter::Iter;
pub mod no_flash;
mod fmt;
use fmt::FmtWrapper;
mod test;
/// Backend interface for `Store`
pub trait StoreBackend {
type Data: Sized + AsRef<[u8]>;
type Data: Sized + AsRef<[u8]> + Clone;
/// Memory-mapped
fn data(&self) -> &Self::Data;
fn len(&self) -> usize {
@ -23,17 +21,10 @@ pub trait StoreBackend {
}
type Error;
/// erase flash
fn erase(&mut self) -> Result<(), Self::Error>;
/// program flash
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) {}
/// need for automatic compaction
///
/// leaves memory management to the implementer
fn backup_space(&self) -> &'static mut [u8];
}
/// Simple Flash Key Value Store
@ -68,29 +59,7 @@ impl<B: StoreBackend> Store<B> {
.map(|value| str::from_utf8(value)
.map_err(ReadError::Utf8Error)
)
.transpose()
)
}
/// read from `key`, decode with `postcard`
#[cfg(feature = "postcard-values")]
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)
.transpose())
}
unsafe fn append_at(&mut self, mut offset: usize,
@ -122,8 +91,7 @@ impl<B: StoreBackend> Store<B> {
}
fn compact(&mut self) -> Result<(), Error<B::Error>> {
let old_data = self.backend.backup_space();
old_data.copy_from_slice(self.backend.data().as_ref());
let old_data = self.backend.data().clone();
self.erase()?;
@ -153,14 +121,21 @@ impl<B: StoreBackend> Store<B> {
}
fn append(&mut self, key: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
let free_offset = self.get_bytes_used()?;
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<V: AsRef<[u8]>>(&mut self, key: &str, value: V) -> Result<(), Error<B::Error>> {
let value = value.as_ref();
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()?;
@ -170,13 +145,12 @@ impl<B: StoreBackend> Store<B> {
}
}
/// encode with `postcard`, write at `key`
#[cfg(feature = "postcard-values")]
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)
/// 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`
@ -184,7 +158,6 @@ impl<B: StoreBackend> Store<B> {
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)?;

View File

@ -1,37 +0,0 @@
use crate::StoreBackend;
use core::fmt;
/// expect this for any write operation to `NoFlash`
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoFlashError;
impl fmt::Display for NoFlashError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "no flash")
}
}
/// a valid `StoreBackend` that can be used for configurations lacking a flash
#[derive(Debug, Clone, Copy)]
pub struct NoFlash;
impl StoreBackend for NoFlash {
type Data = [u8; 0];
fn data(&self) -> &Self::Data {
&[]
}
type Error = NoFlashError;
fn erase(&mut self) -> Result<(), Self::Error> {
Err(NoFlashError)
}
fn program(&mut self, _: usize, _: &[u8]) -> Result<(), Self::Error> {
Err(NoFlashError)
}
fn backup_space(&self) -> &'static mut [u8] {
&mut []
}
}

View File

@ -1,9 +1,8 @@
#![cfg(test)]
use crate::{Error, no_flash, Store, StoreBackend, WriteError};
use crate::{Store, StoreBackend};
const SIZE: usize = 1024;
static mut BACKUP_SPACE: [u8; SIZE] = [0; SIZE];
pub struct TestBackend {
data: [u8; SIZE],
@ -40,11 +39,6 @@ impl StoreBackend for TestBackend {
self.data[offset..end].copy_from_slice(payload);
Ok(())
}
fn backup_space(&self) -> &'static mut [u8] {
unsafe { &mut BACKUP_SPACE }
}
}
fn make_store() -> Store<TestBackend> {
@ -58,65 +52,19 @@ fn empty_read_not_found() {
assert_eq!(store.read("foo").unwrap(), None)
}
#[test]
fn write_read() {
let mut store = make_store();
store.write("foo", b"bar").unwrap();
assert_eq!(store.read("foo").unwrap().unwrap(), b"bar");
}
#[test]
fn write_read_str() {
fn write_int_read_str() {
let mut store = make_store();
store.write("foo", "bar").unwrap();
assert_eq!(store.read_str("foo").unwrap().unwrap(), "bar");
}
#[test]
fn write_read_str_utf8() {
let mut store = make_store();
store.write("réseau", "point à point").unwrap();
assert_eq!(store.read_str("réseau").unwrap().unwrap(), "point à point");
}
#[test]
#[cfg(feature = "postcard-values")]
fn write_read_value_str() {
let mut store = make_store();
store.write_value("foo", "bar").unwrap();
assert_eq!(store.read_value("foo"), Ok(Some("bar")));
}
#[test]
#[cfg(feature = "postcard-values")]
fn write_read_value_u32() {
let mut store = make_store();
store.write_value("foo", 42005u32).unwrap();
assert_eq!(store.read_value("foo"), Ok(Some(42005u32)));
}
#[test]
#[cfg(feature = "postcard-values")]
fn write_read_value_i64() {
let mut store = make_store();
store.write_value("foo", -99999999i64).unwrap();
assert_eq!(store.read_value("foo"), Ok(Some(-99999999i64)));
}
#[test]
#[cfg(feature = "postcard-values")]
fn write_read_value_f32() {
let mut store = make_store();
store.write_value("foo", 3.75e17f32).unwrap();
assert_eq!(store.read_value("foo"), Ok(Some(3.75e17f32)));
}
#[test]
#[cfg(feature = "postcard-values")]
fn write_read_value_f64() {
let mut store = make_store();
store.write_value("foo", -1.999e-13f64).unwrap();
assert_eq!(store.read_value("foo"), Ok(Some(-1.999e-13f64)));
store.write_int("foo", 42).unwrap();
assert_eq!(store.read_str("foo").unwrap().unwrap(), "42");
}
#[test]
@ -129,13 +77,3 @@ fn write_many() {
assert_eq!(store.read("foo").unwrap().unwrap(), b"do not compact away");
assert_eq!(store.read("bar").unwrap().unwrap(), b"SPAM");
}
#[test]
fn no_flash() {
let mut store = Store::new(no_flash::NoFlash);
assert_eq!(store.read("foo").unwrap(), None);
assert_eq!(
store.write("foo", b"bar"),
Err(Error::Write(WriteError::Backend(no_flash::NoFlashError)))
);
}