umsh/
embassy_support.rs

1//! Embassy-friendly runtime adapters and fixed-capacity in-memory stores.
2
3use core::{cell::RefCell, marker::PhantomData};
4
5use embedded_hal_async::delay::DelayNs;
6use heapless::Vec;
7use rand::{CryptoRng, TryCryptoRng, TryRng};
8use umsh_hal::{CounterStore, KeyValueStore};
9
10/// Monotonic clock backed by `embassy-time`.
11///
12/// Re-exported from `umsh-hal` so host and embedded targets share one
13/// implementation, including an efficient `poll_delay_until` that registers a
14/// real embassy timer rather than busy-polling.
15pub use umsh_hal::EmbassyClock;
16
17#[cfg(feature = "software-crypto")]
18use crate::{
19    Platform,
20    crypto::software::{SoftwareAes, SoftwareIdentity, SoftwareSha256},
21};
22
23/// [`DelayNs`] adapter backed by `embassy_time`.
24#[derive(Clone, Copy, Debug, Default)]
25pub struct EmbassyDelay;
26
27impl DelayNs for EmbassyDelay {
28    async fn delay_ns(&mut self, ns: u32) {
29        embassy_time::Timer::after_nanos(u64::from(ns)).await;
30    }
31}
32
33/// Adapter that preserves a concrete RNG type while forwarding `rand` traits.
34pub struct RngCoreAdapter<R>(pub R);
35
36impl<R: TryRng> TryRng for RngCoreAdapter<R> {
37    type Error = R::Error;
38
39    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
40        self.0.try_next_u32()
41    }
42
43    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
44        self.0.try_next_u64()
45    }
46
47    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
48        self.0.try_fill_bytes(dest)
49    }
50}
51
52impl<R: TryCryptoRng> TryCryptoRng for RngCoreAdapter<R> {}
53
54/// Errors returned by the fixed-capacity memory stores.
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum MemoryStoreError {
57    Capacity,
58    KeyTooLarge,
59    ValueTooLarge,
60    BufferTooSmall,
61}
62
63/// Fixed-capacity in-memory counter store.
64pub struct MemoryCounterStore<const ENTRIES: usize, const KEY_LEN: usize> {
65    entries: RefCell<Vec<(Vec<u8, KEY_LEN>, u32), ENTRIES>>,
66}
67
68impl<const ENTRIES: usize, const KEY_LEN: usize> Default for MemoryCounterStore<ENTRIES, KEY_LEN> {
69    fn default() -> Self {
70        Self {
71            entries: RefCell::new(Vec::new()),
72        }
73    }
74}
75
76impl<const ENTRIES: usize, const KEY_LEN: usize> CounterStore
77    for MemoryCounterStore<ENTRIES, KEY_LEN>
78{
79    type Error = MemoryStoreError;
80
81    async fn load(&self, context: &[u8]) -> Result<u32, Self::Error> {
82        Ok(self
83            .entries
84            .borrow()
85            .iter()
86            .find(|(key, _)| key.as_slice() == context)
87            .map(|(_, value)| *value)
88            .unwrap_or(0))
89    }
90
91    async fn store(&self, context: &[u8], value: u32) -> Result<(), Self::Error> {
92        let mut entries = self.entries.borrow_mut();
93        if let Some((_, stored)) = entries
94            .iter_mut()
95            .find(|(key, _)| key.as_slice() == context)
96        {
97            *stored = value;
98            return Ok(());
99        }
100        let key = to_heapless_vec::<KEY_LEN>(context).map_err(|_| MemoryStoreError::KeyTooLarge)?;
101        entries
102            .push((key, value))
103            .map_err(|_| MemoryStoreError::Capacity)
104    }
105
106    async fn flush(&self) -> Result<(), Self::Error> {
107        Ok(())
108    }
109}
110
111/// Fixed-capacity in-memory key-value store.
112pub struct MemoryKeyValueStore<const ENTRIES: usize, const KEY_LEN: usize, const VALUE_LEN: usize> {
113    entries: RefCell<Vec<(Vec<u8, KEY_LEN>, Vec<u8, VALUE_LEN>), ENTRIES>>,
114}
115
116impl<const ENTRIES: usize, const KEY_LEN: usize, const VALUE_LEN: usize> Default
117    for MemoryKeyValueStore<ENTRIES, KEY_LEN, VALUE_LEN>
118{
119    fn default() -> Self {
120        Self {
121            entries: RefCell::new(Vec::new()),
122        }
123    }
124}
125
126impl<const ENTRIES: usize, const KEY_LEN: usize, const VALUE_LEN: usize> KeyValueStore
127    for MemoryKeyValueStore<ENTRIES, KEY_LEN, VALUE_LEN>
128{
129    type Error = MemoryStoreError;
130
131    async fn load(&self, key: &[u8], buf: &mut [u8]) -> Result<Option<usize>, Self::Error> {
132        let entries = self.entries.borrow();
133        let Some((_, value)) = entries
134            .iter()
135            .find(|(stored_key, _)| stored_key.as_slice() == key)
136        else {
137            return Ok(None);
138        };
139        if value.len() > buf.len() {
140            return Err(MemoryStoreError::BufferTooSmall);
141        }
142        buf[..value.len()].copy_from_slice(value.as_slice());
143        Ok(Some(value.len()))
144    }
145
146    async fn store(&self, key: &[u8], value: &[u8]) -> Result<(), Self::Error> {
147        let mut entries = self.entries.borrow_mut();
148        let value_vec =
149            to_heapless_vec::<VALUE_LEN>(value).map_err(|_| MemoryStoreError::ValueTooLarge)?;
150        if let Some((_, stored_value)) = entries
151            .iter_mut()
152            .find(|(stored_key, _)| stored_key.as_slice() == key)
153        {
154            *stored_value = value_vec;
155            return Ok(());
156        }
157        let key_vec = to_heapless_vec::<KEY_LEN>(key).map_err(|_| MemoryStoreError::KeyTooLarge)?;
158        entries
159            .push((key_vec, value_vec))
160            .map_err(|_| MemoryStoreError::Capacity)
161    }
162
163    async fn delete(&self, key: &[u8]) -> Result<(), Self::Error> {
164        let mut entries = self.entries.borrow_mut();
165        if let Some(index) = entries
166            .iter()
167            .position(|(stored_key, _)| stored_key.as_slice() == key)
168        {
169            entries.swap_remove(index);
170        }
171        Ok(())
172    }
173}
174
175/// Convenience [`crate::Platform`] implementation for Embassy-style targets.
176#[cfg(feature = "software-crypto")]
177pub struct EmbassyPlatform<R, G, CS, KV>(PhantomData<(R, G, CS, KV)>);
178
179#[cfg(feature = "software-crypto")]
180impl<R, G, CS, KV> Platform for EmbassyPlatform<R, G, CS, KV>
181where
182    R: umsh_hal::Radio,
183    G: CryptoRng,
184    CS: CounterStore,
185    KV: KeyValueStore,
186{
187    type Identity = SoftwareIdentity;
188    type Aes = SoftwareAes;
189    type Sha = SoftwareSha256;
190    type Radio = R;
191    type Delay = EmbassyDelay;
192    type Clock = EmbassyClock;
193    type Rng = G;
194    type CounterStore = CS;
195    type KeyValueStore = KV;
196}
197
198fn to_heapless_vec<const N: usize>(bytes: &[u8]) -> Result<Vec<u8, N>, ()> {
199    let mut out = Vec::new();
200    out.extend_from_slice(bytes).map_err(|_| ())?;
201    Ok(out)
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207    use core::{
208        future::Future,
209        pin::pin,
210        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
211    };
212
213    fn block_on_ready<F: Future>(future: F) -> F::Output {
214        fn raw_waker() -> RawWaker {
215            fn clone(_: *const ()) -> RawWaker {
216                raw_waker()
217            }
218            fn wake(_: *const ()) {}
219            fn wake_by_ref(_: *const ()) {}
220            fn drop(_: *const ()) {}
221
222            static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
223            RawWaker::new(core::ptr::null(), &VTABLE)
224        }
225
226        let waker = unsafe { Waker::from_raw(raw_waker()) };
227        let mut future = pin!(future);
228        let mut context = Context::from_waker(&waker);
229        match future.as_mut().poll(&mut context) {
230            Poll::Ready(value) => value,
231            Poll::Pending => panic!("test future unexpectedly pending"),
232        }
233    }
234
235    #[test]
236    fn memory_counter_store_round_trips() {
237        let store = MemoryCounterStore::<4, 16>::default();
238        assert_eq!(block_on_ready(store.load(b"peer")).unwrap(), 0);
239        block_on_ready(store.store(b"peer", 9)).unwrap();
240        assert_eq!(block_on_ready(store.load(b"peer")).unwrap(), 9);
241    }
242
243    #[test]
244    fn memory_key_value_store_round_trips() {
245        let store = MemoryKeyValueStore::<4, 16, 32>::default();
246        block_on_ready(store.store(b"peer", b"value")).unwrap();
247        let mut buf = [0u8; 32];
248        let len = block_on_ready(store.load(b"peer", &mut buf))
249            .unwrap()
250            .unwrap();
251        assert_eq!(&buf[..len], b"value");
252        block_on_ready(store.delete(b"peer")).unwrap();
253        assert_eq!(block_on_ready(store.load(b"peer", &mut buf)).unwrap(), None);
254    }
255}