use crate::error::Result;
use crate::time::Micros;
pub trait Clock {
fn now(&self) -> Micros;
}
pub trait Rng {
fn fill(&mut self, buf: &mut [u8]) -> Result<()>;
}
pub const MAX_KEY_LEN: usize = 15;
pub trait Kv {
fn get(&self, key: &str, out: &mut [u8]) -> Result<Option<usize>>;
fn put(&mut self, key: &str, value: &[u8]) -> Result<()>;
fn remove(&mut self, key: &str) -> Result<bool>;
}
pub fn check_key(key: &str) -> Result<()> {
let ok = !key.is_empty()
&& key.len() <= MAX_KEY_LEN
&& key
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'.' || b == b'-');
if ok {
Ok(())
} else {
Err(crate::Error::InvalidFormat)
}
}
#[cfg(feature = "std")]
pub mod host {
use super::{Clock, Kv, Rng, check_key};
use crate::error::{Error, Result};
use crate::time::Micros;
use std::collections::BTreeMap;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct SystemClock {
epoch: Instant,
}
impl SystemClock {
#[must_use]
pub fn new() -> Self {
SystemClock {
epoch: Instant::now(),
}
}
}
impl Default for SystemClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for SystemClock {
fn now(&self) -> Micros {
let d = self.epoch.elapsed();
Micros(u64::try_from(d.as_micros()).unwrap_or(u64::MAX))
}
}
#[derive(Debug, Default, Clone)]
pub struct MemoryKv {
map: BTreeMap<String, Vec<u8>>,
}
impl MemoryKv {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn len(&self) -> usize {
self.map.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}
impl Kv for MemoryKv {
fn get(&self, key: &str, out: &mut [u8]) -> Result<Option<usize>> {
check_key(key)?;
let Some(value) = self.map.get(key) else {
return Ok(None);
};
let Some(slot) = out.get_mut(..value.len()) else {
return Err(Error::BufferTooSmall {
needed: value.len(),
});
};
slot.copy_from_slice(value);
Ok(Some(value.len()))
}
fn put(&mut self, key: &str, value: &[u8]) -> Result<()> {
check_key(key)?;
self.map.insert(key.to_owned(), value.to_vec());
Ok(())
}
fn remove(&mut self, key: &str) -> Result<bool> {
check_key(key)?;
Ok(self.map.remove(key).is_some())
}
}
#[derive(Debug, Clone)]
pub struct InsecureTestRng(u64);
impl InsecureTestRng {
#[must_use]
pub fn seeded(seed: u64) -> Self {
InsecureTestRng(seed | 1)
}
}
impl Rng for InsecureTestRng {
fn fill(&mut self, buf: &mut [u8]) -> Result<()> {
for b in buf {
let mut x = self.0;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.0 = x;
*b = (x.wrapping_mul(0x2545_F491_4F6C_DD1D) >> 56) as u8;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_kv_round_trip() {
let mut kv = MemoryKv::new();
assert_eq!(kv.get("k", &mut [0; 4]).unwrap(), None);
kv.put("k", b"abc").unwrap();
let mut out = [0u8; 2];
assert_eq!(
kv.get("k", &mut out),
Err(Error::BufferTooSmall { needed: 3 })
);
let mut out = [0u8; 8];
assert_eq!(kv.get("k", &mut out).unwrap(), Some(3));
assert_eq!(&out[..3], b"abc");
assert!(kv.remove("k").unwrap());
assert!(!kv.remove("k").unwrap());
assert_eq!(
kv.put("this_key_is_far_too_long", b""),
Err(Error::InvalidFormat)
);
}
#[test]
fn clock_is_monotonic() {
let c = SystemClock::new();
let a = c.now();
let b = c.now();
assert!(b >= a);
}
#[test]
fn test_rng_is_deterministic_and_nonconstant() {
let mut a = InsecureTestRng::seeded(7);
let mut b = InsecureTestRng::seeded(7);
let (mut x, mut y) = ([0u8; 16], [0u8; 16]);
a.fill(&mut x).unwrap();
b.fill(&mut y).unwrap();
assert_eq!(x, y);
assert!(x.iter().any(|&v| v != x[0]));
}
}
}
#[cfg(test)]
mod tests {
use super::check_key;
#[test]
fn key_rules() {
assert!(check_key("device.key").is_ok());
assert!(check_key("").is_err());
assert!(check_key("has space").is_err());
assert!(check_key("0123456789abcde").is_ok());
assert!(check_key("0123456789abcdef").is_err());
}
}