use cfg_if::cfg_if;
use crate::error::Error;
use crate::tlv::{TLVTag, ToTLV};
use crate::utils::cell::RefCell;
use crate::utils::storage::WriteBuf;
use crate::utils::sync::blocking::Mutex;
#[cfg(feature = "std")]
pub use fileio::*;
cfg_if! {
if #[cfg(feature = "kv-blob-store-65536")] {
pub const KV_BUF_SIZE: usize = 65536;
} else if #[cfg(feature = "kv-blob-store-32768")] {
pub const KV_BUF_SIZE: usize = 32768;
} else if #[cfg(feature = "kv-blob-store-16384")] {
pub const KV_BUF_SIZE: usize = 16384;
} else if #[cfg(feature = "kv-blob-store-8192")] {
pub const KV_BUF_SIZE: usize = 8192;
} else if #[cfg(feature = "kv-blob-store-2048")] {
pub const KV_BUF_SIZE: usize = 2048;
} else if #[cfg(feature = "kv-blob-store-1024")] {
pub const KV_BUF_SIZE: usize = 1024;
} else { pub const KV_BUF_SIZE: usize = 4096;
}
}
pub const VENDOR_KEYS_START: u16 = 0x1000;
pub const FABRIC_KEYS_START: u16 = 0;
pub const BASIC_INFO_KEY: u16 = FABRIC_KEYS_START + 256;
pub const EVENT_EPOCH_KEY: u16 = BASIC_INFO_KEY + 1;
pub const NETWORKS_KEY: u16 = EVENT_EPOCH_KEY + 1;
pub const USER_LABELS_KEY: u16 = NETWORKS_KEY + 1;
pub const BINDINGS_KEY: u16 = USER_LABELS_KEY + 1;
pub const LKG_UTC_KEY: u16 = BINDINGS_KEY + 1;
pub const TRUSTED_TIME_SOURCE_KEY: u16 = LKG_UTC_KEY + 1;
pub const SCENES_KEY: u16 = TRUSTED_TIME_SOURCE_KEY + 1;
pub const OTA_PROVIDERS_KEY: u16 = SCENES_KEY + 1;
pub trait KvBlobStore {
fn load<'a>(&mut self, key: u16, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error>;
fn store(&mut self, key: u16, data: &[u8], buf: &mut [u8]) -> Result<(), Error>;
fn remove(&mut self, key: u16, buf: &mut [u8]) -> Result<(), Error>;
}
impl<T> KvBlobStore for &mut T
where
T: KvBlobStore,
{
fn load<'a>(&mut self, key: u16, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
T::load(self, key, buf)
}
fn store(&mut self, key: u16, data: &[u8], buf: &mut [u8]) -> Result<(), Error> {
T::store(self, key, data, buf)
}
fn remove(&mut self, key: u16, buf: &mut [u8]) -> Result<(), Error> {
T::remove(self, key, buf)
}
}
impl KvBlobStore for &mut dyn KvBlobStore {
fn load<'a>(&mut self, key: u16, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
(**self).load(key, buf)
}
fn store(&mut self, key: u16, data: &[u8], buf: &mut [u8]) -> Result<(), Error> {
(**self).store(key, data, buf)
}
fn remove(&mut self, key: u16, buf: &mut [u8]) -> Result<(), Error> {
(**self).remove(key, buf)
}
}
pub struct DummyKvBlobStore;
impl KvBlobStore for DummyKvBlobStore {
fn load<'a>(&mut self, _key: u16, _buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
Ok(None)
}
fn store(&mut self, _key: u16, _data: &[u8], _buf: &mut [u8]) -> Result<(), Error> {
Ok(())
}
fn remove(&mut self, _key: u16, _buf: &mut [u8]) -> Result<(), Error> {
Ok(())
}
}
pub trait KvBlobStoreAccess {
fn access<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut dyn KvBlobStore, &mut [u8]) -> R;
}
impl<T> KvBlobStoreAccess for &T
where
T: KvBlobStoreAccess,
{
fn access<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut dyn KvBlobStore, &mut [u8]) -> R,
{
T::access(self, f)
}
}
pub struct SharedKvBlobStore<'a, S, const KB: usize> {
store: Mutex<RefCell<S>>,
buf: &'a Mutex<RefCell<[u8; KB]>>,
}
impl<'a, S, const KB: usize> SharedKvBlobStore<'a, S, KB> {
pub const fn new(store: S, buf: &'a Mutex<RefCell<[u8; KB]>>) -> Self {
Self {
store: Mutex::new(RefCell::new(store)),
buf,
}
}
}
impl<S, const KB: usize> KvBlobStoreAccess for SharedKvBlobStore<'_, S, KB>
where
S: KvBlobStore,
{
fn access<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut dyn KvBlobStore, &mut [u8]) -> R,
{
self.buf.lock(|cell| {
let mut buf = cell.borrow_mut();
self.store
.lock(|store| f(&mut *store.borrow_mut(), &mut *buf))
})
}
}
pub struct Persist<S> {
kvb: S,
}
impl<S> Persist<S>
where
S: KvBlobStoreAccess,
{
pub const fn new(kvb: S) -> Self {
Self { kvb }
}
pub fn store<F: FnOnce(&mut [u8]) -> Result<Option<usize>, Error>>(
&mut self,
key: u16,
f: F,
) -> Result<(), Error> {
self.kvb.access(|kvb, buf| {
if !buf.is_empty() {
if let Some(len) = f(buf)? {
let (data, buf) = buf.split_at_mut(len);
kvb.store(key, data, buf)?;
}
}
Ok(())
})
}
pub fn store_tlv<T: ToTLV>(&mut self, key: u16, tlv: T) -> Result<(), Error> {
self.store(key, |buf| {
let mut wb = WriteBuf::new(buf);
tlv.to_tlv(&TLVTag::Anonymous, &mut wb)?;
Ok(Some(wb.get_tail()))
})
}
pub fn remove(&mut self, key: u16) -> Result<(), Error> {
self.kvb.access(|kvb, buf| {
if !buf.is_empty() {
kvb.remove(key, buf)?;
}
Ok(())
})
}
pub fn run(self) -> Result<(), Error> {
Ok(())
}
}
#[cfg(feature = "std")]
mod fileio {
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use crate::error::Error;
use super::KvBlobStore;
extern crate std;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DirKvBlobStore(
#[cfg_attr(feature = "defmt", defmt(Debug2Format))] std::path::PathBuf,
);
impl DirKvBlobStore {
pub fn new_default() -> Self {
Self(std::env::temp_dir().join("rs-matter"))
}
pub const fn new(path: std::path::PathBuf) -> Self {
Self(path)
}
pub fn load(&self, key: u16, buf: &mut [u8]) -> Result<Option<usize>, Error> {
let path = self.key_path(key);
match File::open(path) {
Ok(mut file) => {
let mut offset = 0;
loop {
if offset == buf.len() {
Err(crate::error::ErrorCode::NoSpace)?;
}
let len = file.read(&mut buf[offset..])?;
if len == 0 {
break;
}
offset += len;
}
let data = &buf[..offset];
debug!("Key {}: loaded {}B ({:?})", key, data.len(), data);
Ok(Some(data.len()))
}
Err(_) => Ok(None),
}
}
pub fn store(&self, key: u16, data: &[u8]) -> Result<(), Error> {
let path = self.key_path(key);
std::fs::create_dir_all(unwrap!(path.parent()))?;
let mut file = File::create(path)?;
file.write_all(data)?;
debug!("Key {}: stored {}B ({:?})", key, data.len(), data);
Ok(())
}
pub fn remove(&self, key: u16) -> Result<(), Error> {
let path = self.key_path(key);
if std::fs::remove_file(path).is_ok() {
debug!("Key {}: removed", key);
}
Ok(())
}
fn key_path(&self, key: u16) -> std::path::PathBuf {
self.0.join(format!("k_{key:04x}"))
}
}
impl Default for DirKvBlobStore {
fn default() -> Self {
Self::new_default()
}
}
impl KvBlobStore for DirKvBlobStore {
fn load<'a>(&mut self, key: u16, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
Ok(Self::load(self, key, buf)?.map(|len| &buf[..len]))
}
fn store(&mut self, key: u16, data: &[u8], _buf: &mut [u8]) -> Result<(), Error> {
Self::store(self, key, data)
}
fn remove(&mut self, key: u16, _buf: &mut [u8]) -> Result<(), Error> {
Self::remove(self, key)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FileKvBlobStore {
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
path: std::path::PathBuf,
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
blobs: Option<HashMap<u16, Vec<u8>>>,
}
impl FileKvBlobStore {
pub fn new_default() -> Self {
Self::new(PathBuf::from("/tmp/chip_kvs"))
}
pub const fn new(path: PathBuf) -> Self {
Self { path, blobs: None }
}
pub fn load(&mut self, key: u16, buf: &mut [u8]) -> Result<Option<usize>, Error> {
self.initialize()?;
let blobs = self.blobs.as_ref().unwrap();
if let Some(blob) = blobs.get(&key) {
if blob.len() > buf.len() {
Err(crate::error::ErrorCode::NoSpace)?;
}
buf[..blob.len()].copy_from_slice(blob);
Ok(Some(blob.len()))
} else {
Ok(None)
}
}
pub fn store(&mut self, key: u16, data: &[u8]) -> Result<(), Error> {
self.initialize()?;
let blobs = self.blobs.as_mut().unwrap();
blobs.insert(key, data.to_vec());
Self::save_all(&self.path, blobs)
}
pub fn remove(&mut self, key: u16) -> Result<(), Error> {
self.initialize()?;
let blobs = self.blobs.as_mut().unwrap();
blobs.remove(&key);
Self::save_all(&self.path, blobs)
}
fn initialize(&mut self) -> Result<(), Error> {
if self.blobs.is_none() {
let mut blobs = HashMap::new();
Self::load_all(&self.path, &mut blobs)?;
self.blobs = Some(blobs);
}
Ok(())
}
fn load_all(path: &Path, blobs: &mut HashMap<u16, Vec<u8>>) -> Result<(), Error> {
if let Ok(mut file) = File::open(path) {
loop {
let mut key_buf = [0; 2];
if file.read_exact(&mut key_buf).is_err() {
break;
}
let key = u16::from_le_bytes(key_buf);
let mut len_buf = [0; 4];
file.read_exact(&mut len_buf)?;
let len = u32::from_le_bytes(len_buf) as usize;
let mut data = vec![0; len];
file.read_exact(&mut data)?;
blobs.insert(key, data);
}
}
Ok(())
}
fn save_all(path: &Path, blobs: &HashMap<u16, Vec<u8>>) -> Result<(), Error> {
let mut file = File::create(path)?;
for (key, data) in blobs {
file.write_all(&key.to_le_bytes())?;
file.write_all(&(data.len() as u32).to_le_bytes())?;
file.write_all(data)?;
}
Ok(())
}
}
impl Default for FileKvBlobStore {
fn default() -> Self {
Self::new_default()
}
}
impl KvBlobStore for FileKvBlobStore {
fn load<'a>(&mut self, key: u16, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> {
Ok(Self::load(self, key, buf)?.map(|len| &buf[..len]))
}
fn store(&mut self, key: u16, data: &[u8], _buf: &mut [u8]) -> Result<(), Error> {
Self::store(self, key, data)
}
fn remove(&mut self, key: u16, _buf: &mut [u8]) -> Result<(), Error> {
Self::remove(self, key)
}
}
}