#![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; 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::() } 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 { backend: B, } impl Store { /// wrap a `StoreBackend` into a store pub fn new(backend: B) -> Self { Store { backend } } /// read from `key` pub fn read(&self, key: &str) -> Result, 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, ReadError> { self.read(key) .and_then(|value| value .map(|value| str::from_utf8(value) .map_err(ReadError::Utf8Error) ) .transpose()) } /// how many bytes are currently used /// /// equally, offset of free space pub fn get_bytes_used(&self) -> Result { 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> { 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> { 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> { 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> { 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> { 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> { 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> { self.write(key, &[]) } pub fn erase(&mut self) -> Result<(), WriteError> { self.backend.erase() .map_err(WriteError::Backend)?; Ok(()) } }