use littlefs2_core::{path, PathBuf};
use rand_chacha::ChaCha8Rng;
use trussed_core::{
types::{Bytes, CounterId, Location},
Error, Result,
};
use crate::store::{self, Store};
pub struct ClientCounterstore<S>
where
S: Store,
{
client_id: PathBuf,
rng: ChaCha8Rng,
store: S,
}
pub type Counter = u128;
impl<S: Store> ClientCounterstore<S> {
pub fn new(client_id: PathBuf, rng: ChaCha8Rng, store: S) -> Self {
Self {
client_id,
rng,
store,
}
}
fn counter_path(&self, id: CounterId) -> PathBuf {
let mut path = PathBuf::new();
path.push(&self.client_id);
path.push(path!("ctr"));
path.push(&id.legacy_hex_path());
path
}
fn read_counter(&mut self, location: Location, id: CounterId) -> Result<Counter> {
let path = self.counter_path(id);
let mut bytes: Bytes<16> = store::read(&self.store, location, &path)?;
bytes.resize_zero(16).ok();
Ok(u128::from_le_bytes(bytes.as_slice().try_into().unwrap()))
}
fn write_counter(&mut self, location: Location, id: CounterId, value: u128) -> Result<()> {
let path = self.counter_path(id);
store::store(&self.store, location, &path, &value.to_le_bytes())
}
fn increment_location(&mut self, location: Location, id: CounterId) -> Result<Counter> {
let counter: u128 = self.read_counter(location, id)?;
let next_counter = counter + 1;
self.write_counter(location, id, next_counter)?;
Ok(counter)
}
}
pub trait Counterstore {
const DEFAULT_START_AT: u128 = 0;
fn create_starting_at(
&mut self,
location: Location,
starting_at: impl Into<Counter>,
) -> Result<CounterId>;
fn create(&mut self, location: Location) -> Result<CounterId> {
self.create_starting_at(location, Self::DEFAULT_START_AT)
}
fn increment(&mut self, id: CounterId) -> Result<u128>;
}
impl<S: Store> Counterstore for ClientCounterstore<S> {
fn create_starting_at(
&mut self,
location: Location,
starting_at: impl Into<Counter>,
) -> Result<CounterId> {
let id = CounterId::new(&mut self.rng);
self.write_counter(location, id, starting_at.into())?;
Ok(id)
}
fn increment(&mut self, id: CounterId) -> Result<u128> {
let locations = [Location::Internal, Location::External, Location::Volatile];
locations
.iter()
.filter_map(|&location| self.increment_location(location, id).ok())
.next()
.ok_or(Error::NoSuchKey)
}
}