doc
This commit is contained in:
parent
c0da68f48c
commit
57758cdc9a
|
@ -0,0 +1,26 @@
|
||||||
|
# Simple Flash Key Value store
|
||||||
|
|
||||||
|
Tries to keep flash wear low by continuously appending data,
|
||||||
|
automatically erasing and rewriting when full.
|
||||||
|
|
||||||
|
## Store requirements
|
||||||
|
|
||||||
|
* Reading must be possible by memory-mapping a `&[u8]` slice
|
||||||
|
* Must support `erase()` for compaction,
|
||||||
|
* Writing must support `program()` with an offset and `&[u8]` payload
|
||||||
|
|
||||||
|
For details see `trait StoreBackend`.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* NoFlash backend
|
||||||
|
* read_int()
|
||||||
|
* write_str()
|
||||||
|
* automatic value coercion
|
||||||
|
* support for floats
|
||||||
|
|
||||||
|
### Ideas
|
||||||
|
|
||||||
|
* iterator (quadratic)
|
||||||
|
* compaction to a second backend instead on stack
|
||||||
|
* StoreBackend-configurable erased data detection
|
|
@ -18,10 +18,14 @@ impl<B> From<WriteError<B>> for Error<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors decoding the store
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ReadError {
|
pub enum ReadError {
|
||||||
|
/// invalid record size
|
||||||
InvalidSize { offset: usize, size: usize },
|
InvalidSize { offset: usize, size: usize },
|
||||||
|
/// missing separator
|
||||||
MissingSeparator { offset: usize },
|
MissingSeparator { offset: usize },
|
||||||
|
/// `str::from_utf8` error
|
||||||
Utf8Error(str::Utf8Error),
|
Utf8Error(str::Utf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +42,13 @@ impl fmt::Display for ReadError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors when writing to the store
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum WriteError<B> {
|
pub enum WriteError<B> {
|
||||||
|
/// store is full even after automatic compaction
|
||||||
SpaceExhausted,
|
SpaceExhausted,
|
||||||
|
/// error passed from the StoreBackend's `erase()` and `program()`
|
||||||
|
/// methods
|
||||||
Backend(B),
|
Backend(B),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -20,26 +20,25 @@ pub trait StoreBackend {
|
||||||
size_of::<Self::Data>()
|
size_of::<Self::Data>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// utility for compaction, requires compile-time knowledge of the data length
|
|
||||||
|
|
||||||
type Error;
|
type Error;
|
||||||
fn erase(&mut self) -> Result<(), Self::Error>;
|
fn erase(&mut self) -> Result<(), Self::Error>;
|
||||||
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error>;
|
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error>;
|
||||||
/// called after many `program()` invocations to allow for eg. cache flushing
|
/// called after repeated `program()` invocations to allow for eg. cache flushing
|
||||||
fn program_done(&mut self) {}
|
fn program_done(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: NoFlash
|
/// Simple Flash Key Value Store
|
||||||
|
|
||||||
pub struct Store<B: StoreBackend> {
|
pub struct Store<B: StoreBackend> {
|
||||||
backend: B,
|
backend: B,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: StoreBackend> Store<B> {
|
impl<B: StoreBackend> Store<B> {
|
||||||
|
/// wrap a `StoreBackend` into a store
|
||||||
pub fn new(backend: B) -> Self {
|
pub fn new(backend: B) -> Self {
|
||||||
Store { backend }
|
Store { backend }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read from `key`
|
||||||
pub fn read(&self, key: &str) -> Result<Option<&[u8]>, ReadError> {
|
pub fn read(&self, key: &str) -> Result<Option<&[u8]>, ReadError> {
|
||||||
let mut iter = Iter::new(self.backend.data().as_ref());
|
let mut iter = Iter::new(self.backend.data().as_ref());
|
||||||
let mut value = None;
|
let mut value = None;
|
||||||
|
@ -53,6 +52,7 @@ impl<B: StoreBackend> Store<B> {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// read from `key`, decode UTF-8
|
||||||
pub fn read_str(&self, key: &str) -> Result<Option<&str>, ReadError> {
|
pub fn read_str(&self, key: &str) -> Result<Option<&str>, ReadError> {
|
||||||
self.read(key)
|
self.read(key)
|
||||||
.and_then(|value| value
|
.and_then(|value| value
|
||||||
|
@ -134,6 +134,7 @@ impl<B: StoreBackend> Store<B> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// store a buffer `value` at `key`
|
||||||
pub fn write(&mut self, key: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
|
pub fn write(&mut self, key: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
|
||||||
match self.append(key, value) {
|
match self.append(key, value) {
|
||||||
Err(Error::Write(WriteError::SpaceExhausted)) => {
|
Err(Error::Write(WriteError::SpaceExhausted)) => {
|
||||||
|
@ -144,6 +145,7 @@ impl<B: StoreBackend> Store<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// serialize 32-bit as ASCII, store at `key`
|
||||||
pub fn write_int(&mut self, key: &str, value: u32) -> Result<(), Error<B::Error>> {
|
pub fn write_int(&mut self, key: &str, value: u32) -> Result<(), Error<B::Error>> {
|
||||||
let mut buf = [0; 16];
|
let mut buf = [0; 16];
|
||||||
let mut wrapper = FmtWrapper::new(&mut buf);
|
let mut wrapper = FmtWrapper::new(&mut buf);
|
||||||
|
@ -151,6 +153,7 @@ impl<B: StoreBackend> Store<B> {
|
||||||
self.write(key, wrapper.contents())
|
self.write(key, wrapper.contents())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// store a 0-byte tombstone value at `key`
|
||||||
pub fn remove(&mut self, key: &str) -> Result<(), Error<B::Error>> {
|
pub fn remove(&mut self, key: &str) -> Result<(), Error<B::Error>> {
|
||||||
self.write(key, &[])
|
self.write(key, &[])
|
||||||
}
|
}
|
||||||
|
@ -162,5 +165,3 @@ impl<B: StoreBackend> Store<B> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: quadratic iterator
|
|
||||||
|
|
Loading…
Reference in New Issue