1use core::convert::Infallible;
9use std::{
10 cell::{Cell, RefCell},
11 collections::VecDeque,
12 rc::Rc,
13 vec::Vec,
14};
15
16use core::task::{Context, Poll};
17use embedded_hal_async::delay::DelayNs;
18use rand::{Rng, TryCryptoRng, TryRng};
19use umsh_core::PublicKey;
20use umsh_crypto::{
21 AesCipher, AesProvider, CryptoEngine, NodeIdentity, Sha256Provider, SharedSecret,
22};
23use umsh_hal::{
24 Clock, CounterStore, KeyValueStore, Radio, RxInfo, RxOrigin, Snr, TxError, TxOptions,
25};
26
27use crate::{
28 DEFAULT_ACKS, DEFAULT_CHANNELS, DEFAULT_DUP, DEFAULT_FRAME, DEFAULT_IDENTITIES, DEFAULT_PEERS,
29 DEFAULT_TX, Mac, OperatingPolicy, Platform, RepeaterConfig,
30};
31
32const DEFAULT_RSSI: i16 = -40;
33const DEFAULT_SNR: Snr = Snr::from_decibels(10);
34
35pub type TestMac<
37 const IDENTITIES: usize = DEFAULT_IDENTITIES,
38 const PEERS: usize = DEFAULT_PEERS,
39 const CHANNELS: usize = DEFAULT_CHANNELS,
40 const ACKS: usize = DEFAULT_ACKS,
41 const TX: usize = DEFAULT_TX,
42 const FRAME: usize = DEFAULT_FRAME,
43 const DUP: usize = DEFAULT_DUP,
44> = Mac<TestPlatform, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP>;
45
46pub type ModeledTestMac<
48 const IDENTITIES: usize = DEFAULT_IDENTITIES,
49 const PEERS: usize = DEFAULT_PEERS,
50 const CHANNELS: usize = DEFAULT_CHANNELS,
51 const ACKS: usize = DEFAULT_ACKS,
52 const TX: usize = DEFAULT_TX,
53 const FRAME: usize = DEFAULT_FRAME,
54 const DUP: usize = DEFAULT_DUP,
55> = Mac<ModeledTestPlatform, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP>;
56
57pub struct TestPlatform;
59
60impl Platform for TestPlatform {
61 type Identity = DummyIdentity;
62 type Aes = DummyAes;
63 type Sha = DummySha;
64 type Radio = SimulatedRadio;
65 type Delay = DummyDelay;
66 type Clock = DummyClock;
67 type Rng = DummyRng;
68 type CounterStore = DummyCounterStore;
69 type KeyValueStore = DummyKeyValueStore;
70}
71
72pub struct ModeledTestPlatform;
74
75impl Platform for ModeledTestPlatform {
76 type Identity = DummyIdentity;
77 type Aes = DummyAes;
78 type Sha = DummySha;
79 type Radio = ModeledRadio;
80 type Delay = DummyDelay;
81 type Clock = DummyClock;
82 type Rng = DummyRng;
83 type CounterStore = DummyCounterStore;
84 type KeyValueStore = DummyKeyValueStore;
85}
86
87pub fn make_test_mac<
89 const IDENTITIES: usize,
90 const PEERS: usize,
91 const CHANNELS: usize,
92 const ACKS: usize,
93 const TX: usize,
94 const FRAME: usize,
95 const DUP: usize,
96>(
97 radio: SimulatedRadio,
98 clock: DummyClock,
99) -> TestMac<IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP> {
100 Mac::new(
101 radio,
102 CryptoEngine::new(DummyAes, DummySha),
103 clock,
104 DummyRng::default(),
105 DummyCounterStore,
106 RepeaterConfig::default(),
107 OperatingPolicy::default(),
108 )
109}
110
111pub fn make_modeled_test_mac<
113 const IDENTITIES: usize,
114 const PEERS: usize,
115 const CHANNELS: usize,
116 const ACKS: usize,
117 const TX: usize,
118 const FRAME: usize,
119 const DUP: usize,
120>(
121 radio: ModeledRadio,
122 clock: DummyClock,
123) -> ModeledTestMac<IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP> {
124 Mac::new(
125 radio,
126 CryptoEngine::new(DummyAes, DummySha),
127 clock,
128 DummyRng::default(),
129 DummyCounterStore,
130 RepeaterConfig::default(),
131 OperatingPolicy::default(),
132 )
133}
134
135#[derive(Clone)]
137pub struct SimulatedNetwork {
138 inner: Rc<RefCell<NetworkState>>,
139}
140
141struct NetworkState {
142 inboxes: Vec<VecDeque<QueuedFrame>>,
143 links: Vec<Vec<LinkProfile>>,
144}
145
146struct QueuedFrame {
147 data: Vec<u8>,
148 rssi: i16,
149 snr: Snr,
150}
151
152#[derive(Clone, Copy)]
153struct LinkProfile {
154 connected: bool,
155 rssi: i16,
156 snr: Snr,
157}
158
159impl Default for LinkProfile {
160 fn default() -> Self {
161 Self {
162 connected: false,
163 rssi: DEFAULT_RSSI,
164 snr: DEFAULT_SNR,
165 }
166 }
167}
168
169impl Default for SimulatedNetwork {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl SimulatedNetwork {
176 pub fn new() -> Self {
178 Self {
179 inner: Rc::new(RefCell::new(NetworkState {
180 inboxes: Vec::new(),
181 links: Vec::new(),
182 })),
183 }
184 }
185
186 pub fn add_radio(&self) -> SimulatedRadio {
188 self.add_radio_with_config(256, 10)
189 }
190
191 pub fn add_radio_with_config(&self, max_frame_size: usize, t_frame_ms: u32) -> SimulatedRadio {
193 let mut state = self.inner.borrow_mut();
194 let id = state.inboxes.len();
195 for row in &mut state.links {
196 row.push(LinkProfile::default());
197 }
198 state.inboxes.push(VecDeque::new());
199 state.links.push(vec![LinkProfile::default(); id + 1]);
200 SimulatedRadio {
201 network: self.clone(),
202 id,
203 max_frame_size,
204 t_frame_ms,
205 }
206 }
207
208 pub fn connect(&self, from: usize, to: usize) {
210 self.set_link(from, to, true, DEFAULT_RSSI, DEFAULT_SNR);
211 }
212
213 pub fn connect_bidirectional(&self, a: usize, b: usize) {
215 self.connect(a, b);
216 self.connect(b, a);
217 }
218
219 pub fn disconnect(&self, from: usize, to: usize) {
221 self.set_link(from, to, false, DEFAULT_RSSI, DEFAULT_SNR);
222 }
223
224 pub fn set_link(&self, from: usize, to: usize, connected: bool, rssi: i16, snr: Snr) {
226 let mut state = self.inner.borrow_mut();
227 let Some(row) = state.links.get_mut(from) else {
228 panic!("unknown simulated radio id {from}");
229 };
230 let Some(link) = row.get_mut(to) else {
231 panic!("unknown simulated radio id {to}");
232 };
233 *link = LinkProfile {
234 connected,
235 rssi,
236 snr,
237 };
238 }
239
240 pub fn inject_frame(&self, to: usize, frame: &[u8]) {
242 self.inject_frame_with_info(to, frame, DEFAULT_RSSI, DEFAULT_SNR);
243 }
244
245 pub fn inject_frame_with_info(&self, to: usize, frame: &[u8], rssi: i16, snr: Snr) {
247 let mut state = self.inner.borrow_mut();
248 let Some(queue) = state.inboxes.get_mut(to) else {
249 panic!("unknown simulated radio id {to}");
250 };
251 queue.push_back(QueuedFrame {
252 data: frame.to_vec(),
253 rssi,
254 snr,
255 });
256 }
257
258 fn transmit(&self, from: usize, frame: &[u8]) {
259 let mut state = self.inner.borrow_mut();
260 let Some(row) = state.links.get(from) else {
261 panic!("unknown simulated radio id {from}");
262 };
263 let deliveries: Vec<(usize, i16, Snr)> = row
264 .iter()
265 .enumerate()
266 .filter_map(|(to, link)| link.connected.then_some((to, link.rssi, link.snr)))
267 .collect();
268 for (to, rssi, snr) in deliveries {
269 state.inboxes[to].push_back(QueuedFrame {
270 data: frame.to_vec(),
271 rssi,
272 snr,
273 });
274 }
275 }
276
277 fn receive(&self, id: usize, buf: &mut [u8]) -> RxInfo {
278 let mut state = self.inner.borrow_mut();
279 let Some(queue) = state.inboxes.get_mut(id) else {
280 panic!("unknown simulated radio id {id}");
281 };
282 let Some(frame) = queue.pop_front() else {
283 return RxInfo {
284 len: 0,
285 rssi: 0,
286 snr: Snr::from_decibels(0),
287 lqi: None,
288 origin: RxOrigin::Air,
289 };
290 };
291 let len = frame.data.len().min(buf.len());
292 buf[..len].copy_from_slice(&frame.data[..len]);
293 RxInfo {
294 len,
295 rssi: frame.rssi,
296 snr: frame.snr,
297 lqi: None,
298 origin: RxOrigin::Air,
299 }
300 }
301}
302
303#[derive(Clone)]
305pub struct SimulatedRadio {
306 network: SimulatedNetwork,
307 id: usize,
308 max_frame_size: usize,
309 t_frame_ms: u32,
310}
311
312impl SimulatedRadio {
313 pub fn id(&self) -> usize {
315 self.id
316 }
317}
318
319impl Radio for SimulatedRadio {
320 type Error = ();
321
322 async fn transmit(
323 &mut self,
324 data: &[u8],
325 _options: TxOptions,
326 ) -> Result<(), TxError<Self::Error>> {
327 self.network.transmit(self.id, data);
328 Ok(())
329 }
330
331 fn poll_receive(
332 &mut self,
333 _cx: &mut Context<'_>,
334 buf: &mut [u8],
335 ) -> Poll<Result<RxInfo, Self::Error>> {
336 let rx = self.network.receive(self.id, buf);
337 if rx.len == 0 {
338 Poll::Pending
339 } else {
340 Poll::Ready(Ok(rx))
341 }
342 }
343
344 fn max_frame_size(&self) -> usize {
345 self.max_frame_size
346 }
347
348 fn t_frame_ms(&self) -> u32 {
349 self.t_frame_ms
350 }
351}
352
353#[derive(Clone, Copy, Debug, PartialEq, Eq)]
355pub struct ModeledLinkProfile {
356 pub connected: bool,
357 pub base_rssi: i16,
358 pub base_snr: Snr,
359 pub rssi_jitter_dbm: i16,
360 pub snr_jitter_centibels: i16,
361 pub propagation_delay_ms: u32,
362 pub drop_per_thousand: u16,
363}
364
365impl Default for ModeledLinkProfile {
366 fn default() -> Self {
367 Self {
368 connected: false,
369 base_rssi: DEFAULT_RSSI,
370 base_snr: DEFAULT_SNR,
371 rssi_jitter_dbm: 2,
372 snr_jitter_centibels: 10,
373 propagation_delay_ms: 0,
374 drop_per_thousand: 0,
375 }
376 }
377}
378
379impl ModeledLinkProfile {
380 pub fn connected() -> Self {
382 Self {
383 connected: true,
384 ..Self::default()
385 }
386 }
387}
388
389#[derive(Clone)]
391pub struct ModeledNetwork {
392 inner: Rc<RefCell<ModeledNetworkState>>,
393 clock: DummyClock,
394}
395
396struct ModeledNetworkState {
397 inboxes: Vec<VecDeque<QueuedFrame>>,
398 links: Vec<Vec<ModeledLinkProfile>>,
399 in_flight: Vec<InFlightTransmission>,
400 scheduled: Vec<ScheduledDelivery>,
401 rng: ModeledRng,
402}
403
404struct InFlightTransmission {
405 from: usize,
406 start_ms: u64,
407 end_ms: u64,
408}
409
410struct ScheduledDelivery {
411 to: usize,
412 available_at_ms: u64,
413 start_ms: u64,
414 end_ms: u64,
415 data: Vec<u8>,
416 rssi: i16,
417 snr: Snr,
418 collided: bool,
419}
420
421impl ModeledNetwork {
422 pub fn new() -> Self {
424 Self::with_clock(DummyClock::new(0))
425 }
426
427 pub fn with_clock(clock: DummyClock) -> Self {
429 Self {
430 inner: Rc::new(RefCell::new(ModeledNetworkState {
431 inboxes: Vec::new(),
432 links: Vec::new(),
433 in_flight: Vec::new(),
434 scheduled: Vec::new(),
435 rng: ModeledRng::new(0x554d_5348),
436 })),
437 clock,
438 }
439 }
440
441 pub fn clock(&self) -> DummyClock {
443 self.clock.clone()
444 }
445
446 pub fn advance_ms(&self, delta_ms: u64) {
448 self.clock.advance_ms(delta_ms);
449 self.promote_due_frames();
450 }
451
452 pub fn reseed(&self, seed: u64) {
454 self.inner.borrow_mut().rng = ModeledRng::new(seed);
455 }
456
457 pub fn add_radio(&self) -> ModeledRadio {
459 self.add_radio_with_config(256, 100)
460 }
461
462 pub fn add_radio_with_config(&self, max_frame_size: usize, t_frame_ms: u32) -> ModeledRadio {
464 let mut state = self.inner.borrow_mut();
465 let id = state.inboxes.len();
466 for row in &mut state.links {
467 row.push(ModeledLinkProfile::default());
468 }
469 state.inboxes.push(VecDeque::new());
470 state
471 .links
472 .push(vec![ModeledLinkProfile::default(); id + 1]);
473 ModeledRadio {
474 network: self.clone(),
475 id,
476 max_frame_size,
477 t_frame_ms,
478 }
479 }
480
481 pub fn connect(&self, from: usize, to: usize) {
483 self.set_link_profile(from, to, ModeledLinkProfile::connected());
484 }
485
486 pub fn connect_bidirectional(&self, a: usize, b: usize) {
488 self.connect(a, b);
489 self.connect(b, a);
490 }
491
492 pub fn disconnect(&self, from: usize, to: usize) {
494 self.set_link_profile(from, to, ModeledLinkProfile::default());
495 }
496
497 pub fn set_link_profile(&self, from: usize, to: usize, profile: ModeledLinkProfile) {
499 let mut state = self.inner.borrow_mut();
500 let Some(row) = state.links.get_mut(from) else {
501 panic!("unknown modeled radio id {from}");
502 };
503 let Some(link) = row.get_mut(to) else {
504 panic!("unknown modeled radio id {to}");
505 };
506 *link = profile;
507 }
508
509 pub fn has_pending_deliveries(&self) -> bool {
511 !self.inner.borrow().scheduled.is_empty()
512 }
513
514 fn promote_due_frames(&self) {
515 let now_ms = self.clock.now_ms();
516 let mut state = self.inner.borrow_mut();
517 state.in_flight.retain(|tx| tx.end_ms > now_ms);
518 let mut index = 0usize;
519 while index < state.scheduled.len() {
520 if state.scheduled[index].available_at_ms > now_ms {
521 index += 1;
522 continue;
523 }
524 let delivery = state.scheduled.swap_remove(index);
525 if delivery.collided {
526 continue;
527 }
528 state.inboxes[delivery.to].push_back(QueuedFrame {
529 data: delivery.data,
530 rssi: delivery.rssi,
531 snr: delivery.snr,
532 });
533 }
534 }
535
536 fn channel_busy(&self, from: usize, now_ms: u64) -> bool {
537 let state = self.inner.borrow();
538 state.in_flight.iter().any(|tx| {
539 tx.from != from
540 && tx.start_ms <= now_ms
541 && now_ms < tx.end_ms
542 && state
543 .links
544 .get(tx.from)
545 .and_then(|row| row.get(from))
546 .map(|profile| profile.connected)
547 .unwrap_or(false)
548 })
549 }
550
551 fn transmit(
552 &self,
553 from: usize,
554 frame: &[u8],
555 t_frame_ms: u32,
556 options: TxOptions,
557 ) -> Result<(), TxError<()>> {
558 self.promote_due_frames();
559 let now_ms = self.clock.now_ms();
560 if !matches!(options.cad, umsh_hal::CadPolicy::Skip) && self.channel_busy(from, now_ms) {
561 return Err(TxError::CadTimeout);
562 }
563
564 let mut state = self.inner.borrow_mut();
565 let Some(row) = state.links.get(from) else {
566 panic!("unknown modeled radio id {from}");
567 };
568 let busy_until_ms = state
575 .in_flight
576 .iter()
577 .filter(|tx| tx.from == from)
578 .map(|tx| tx.end_ms)
579 .max()
580 .unwrap_or(0);
581 let start_ms = now_ms.max(busy_until_ms);
582 let end_ms = start_ms.saturating_add(u64::from(t_frame_ms));
583 let deliveries: Vec<(usize, ModeledLinkProfile)> = row
584 .iter()
585 .enumerate()
586 .filter_map(|(to, link)| link.connected.then_some((to, *link)))
587 .collect();
588
589 for (to, profile) in deliveries {
590 if profile.drop_per_thousand > 0
591 && state.rng.random_u16(1000) < profile.drop_per_thousand
592 {
593 continue;
594 }
595
596 let rssi_jitter = if profile.rssi_jitter_dbm > 0 {
597 state
598 .rng
599 .random_i16_inclusive(-profile.rssi_jitter_dbm, profile.rssi_jitter_dbm)
600 } else {
601 0
602 };
603 let snr_jitter = if profile.snr_jitter_centibels > 0 {
604 state.rng.random_i16_inclusive(
605 -profile.snr_jitter_centibels,
606 profile.snr_jitter_centibels,
607 )
608 } else {
609 0
610 };
611 let propagation_delay_ms = u64::from(profile.propagation_delay_ms);
612 let available_at_ms = end_ms.saturating_add(propagation_delay_ms);
613 let mut delivery = ScheduledDelivery {
614 to,
615 available_at_ms,
616 start_ms,
617 end_ms,
618 data: frame.to_vec(),
619 rssi: profile.base_rssi.saturating_add(rssi_jitter),
620 snr: Snr::from_centibels(
621 profile.base_snr.as_centibels().saturating_add(snr_jitter),
622 ),
623 collided: false,
624 };
625
626 for existing in &mut state.scheduled {
627 if existing.to != to {
628 continue;
629 }
630 if existing.start_ms < end_ms && start_ms < existing.end_ms {
631 existing.collided = true;
632 delivery.collided = true;
633 }
634 }
635
636 state.scheduled.push(delivery);
637 }
638
639 state.in_flight.push(InFlightTransmission {
640 from,
641 start_ms,
642 end_ms,
643 });
644 Ok(())
645 }
646
647 fn receive(&self, id: usize, buf: &mut [u8]) -> RxInfo {
648 self.promote_due_frames();
649 let mut state = self.inner.borrow_mut();
650 let Some(queue) = state.inboxes.get_mut(id) else {
651 panic!("unknown modeled radio id {id}");
652 };
653 let Some(frame) = queue.pop_front() else {
654 return RxInfo {
655 len: 0,
656 rssi: 0,
657 snr: Snr::from_decibels(0),
658 lqi: None,
659 origin: RxOrigin::Air,
660 };
661 };
662 let len = frame.data.len().min(buf.len());
663 buf[..len].copy_from_slice(&frame.data[..len]);
664 RxInfo {
665 len,
666 rssi: frame.rssi,
667 snr: frame.snr,
668 lqi: None,
669 origin: RxOrigin::Air,
670 }
671 }
672}
673
674impl Default for ModeledNetwork {
675 fn default() -> Self {
676 Self::new()
677 }
678}
679
680#[derive(Clone)]
682pub struct ModeledRadio {
683 network: ModeledNetwork,
684 id: usize,
685 max_frame_size: usize,
686 t_frame_ms: u32,
687}
688
689#[derive(Clone, Copy)]
690struct ModeledRng(u64);
691
692impl ModeledRng {
693 fn new(seed: u64) -> Self {
694 Self(seed.max(1))
695 }
696
697 fn next_u32(&mut self) -> u32 {
698 let mut x = self.0;
699 x ^= x << 13;
700 x ^= x >> 7;
701 x ^= x << 17;
702 self.0 = x.max(1);
703 x as u32
704 }
705
706 fn random_u16(&mut self, upper_exclusive: u16) -> u16 {
707 if upper_exclusive == 0 {
708 0
709 } else {
710 (self.next_u32() % u32::from(upper_exclusive)) as u16
711 }
712 }
713
714 fn random_i16_inclusive(&mut self, min: i16, max: i16) -> i16 {
715 if min >= max {
716 min
717 } else {
718 let span = (i32::from(max) - i32::from(min) + 1) as u32;
719 (i32::from(min) + (self.next_u32() % span) as i32) as i16
720 }
721 }
722}
723
724impl ModeledRadio {
725 pub fn id(&self) -> usize {
727 self.id
728 }
729}
730
731impl Radio for ModeledRadio {
732 type Error = ();
733
734 async fn transmit(
735 &mut self,
736 data: &[u8],
737 options: TxOptions,
738 ) -> Result<(), TxError<Self::Error>> {
739 self.network
740 .transmit(self.id, data, self.t_frame_ms, options)
741 }
742
743 fn poll_receive(
744 &mut self,
745 _cx: &mut Context<'_>,
746 buf: &mut [u8],
747 ) -> Poll<Result<RxInfo, Self::Error>> {
748 let rx = self.network.receive(self.id, buf);
749 if rx.len == 0 {
750 Poll::Pending
751 } else {
752 Poll::Ready(Ok(rx))
753 }
754 }
755
756 fn max_frame_size(&self) -> usize {
757 self.max_frame_size
758 }
759
760 fn t_frame_ms(&self) -> u32 {
761 self.t_frame_ms
762 }
763}
764
765#[derive(Clone)]
769pub struct DummyIdentity {
770 public_key: PublicKey,
771}
772
773impl DummyIdentity {
774 pub fn new(bytes: [u8; 32]) -> Self {
787 #[cfg(feature = "software-crypto")]
788 let public_key = {
789 use umsh_crypto::NodeIdentity;
790 *umsh_crypto::software::SoftwareIdentity::from_secret_bytes(&bytes).public_key()
791 };
792 #[cfg(not(feature = "software-crypto"))]
793 let public_key = PublicKey(bytes);
794 Self { public_key }
795 }
796
797 pub fn from_public_key(public_key: PublicKey) -> Self {
802 Self { public_key }
803 }
804}
805
806impl NodeIdentity for DummyIdentity {
807 type Error = ();
808
809 fn public_key(&self) -> &PublicKey {
810 &self.public_key
811 }
812
813 async fn sign(&self, _message: &[u8]) -> Result<[u8; 64], Self::Error> {
814 Ok([0u8; 64])
815 }
816
817 async fn agree(&self, peer: &PublicKey) -> Result<SharedSecret, Self::Error> {
818 let mut out = [0u8; 32];
819 for (index, byte) in out.iter_mut().enumerate() {
820 *byte = self.public_key.0[index] ^ peer.0[index];
821 }
822 Ok(SharedSecret(out))
823 }
824}
825
826pub struct DummyCipher {
830 key: [u8; 32],
831}
832
833impl AesCipher for DummyCipher {
834 fn encrypt_block(&self, block: &mut [u8; 16]) {
835 for (byte, key) in block.iter_mut().zip(self.key.iter()) {
836 *byte ^= *key;
837 }
838 }
839
840 fn decrypt_block(&self, block: &mut [u8; 16]) {
841 self.encrypt_block(block);
842 }
843}
844
845#[derive(Clone, Copy)]
849pub struct DummyAes;
850
851impl AesProvider for DummyAes {
852 type Cipher = DummyCipher;
853
854 fn new_cipher(&self, key: &[u8; 32]) -> Self::Cipher {
855 DummyCipher { key: *key }
856 }
857}
858
859#[derive(Clone, Copy)]
863pub struct DummySha;
864
865impl Sha256Provider for DummySha {
866 fn hash(&self, data: &[&[u8]]) -> [u8; 32] {
867 let mut out = [0u8; 32];
868 for chunk in data {
869 for (index, byte) in chunk.iter().enumerate() {
870 out[index % 32] ^= *byte;
871 }
872 }
873 out
874 }
875
876 fn hmac(&self, key: &[u8], data: &[&[u8]]) -> [u8; 32] {
877 let mut out = [0u8; 32];
878 for (index, byte) in key.iter().enumerate() {
879 out[index % 32] ^= *byte;
880 }
881 for chunk in data {
882 for (index, byte) in chunk.iter().enumerate() {
883 out[index % 32] ^= *byte;
884 }
885 }
886 out
887 }
888}
889
890#[derive(Clone, Default)]
892pub struct DummyClock {
893 now_ms: Rc<Cell<u64>>,
894}
895
896impl DummyClock {
897 pub fn new(now_ms: u64) -> Self {
899 Self {
900 now_ms: Rc::new(Cell::new(now_ms)),
901 }
902 }
903
904 pub fn advance_ms(&self, delta_ms: u64) {
906 self.now_ms.set(self.now_ms.get().saturating_add(delta_ms));
907 }
908
909 pub fn set_ms(&self, now_ms: u64) {
911 self.now_ms.set(now_ms);
912 }
913}
914
915impl Clock for DummyClock {
916 fn now_ms(&self) -> u64 {
917 self.now_ms.get()
918 }
919}
920
921#[derive(Clone, Copy, Default)]
923pub struct DummyDelay;
924
925impl DelayNs for DummyDelay {
926 async fn delay_ns(&mut self, _ns: u32) {}
927}
928
929#[derive(Clone, Default)]
933pub struct DummyRng(pub u8);
934
935impl TryRng for DummyRng {
936 type Error = Infallible;
937
938 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
939 let mut bytes = [0u8; 4];
940 self.fill_bytes(&mut bytes);
941 Ok(u32::from_le_bytes(bytes))
942 }
943
944 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
945 let mut bytes = [0u8; 8];
946 self.fill_bytes(&mut bytes);
947 Ok(u64::from_le_bytes(bytes))
948 }
949
950 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
951 for byte in dest.iter_mut() {
952 *byte = self.0;
953 self.0 = self.0.wrapping_add(1);
954 }
955 Ok(())
956 }
957}
958
959impl TryCryptoRng for DummyRng {}
960
961#[derive(Clone, Copy, Default)]
963pub struct DummyCounterStore;
964
965impl CounterStore for DummyCounterStore {
966 type Error = ();
967
968 async fn load(&self, _context: &[u8]) -> Result<u32, Self::Error> {
969 Ok(0)
970 }
971
972 async fn store(&self, _context: &[u8], _value: u32) -> Result<(), Self::Error> {
973 Ok(())
974 }
975
976 async fn flush(&self) -> Result<(), Self::Error> {
977 Ok(())
978 }
979}
980
981#[derive(Clone, Copy, Default)]
983pub struct DummyKeyValueStore;
984
985impl KeyValueStore for DummyKeyValueStore {
986 type Error = ();
987
988 async fn load(&self, _key: &[u8], _buf: &mut [u8]) -> Result<Option<usize>, Self::Error> {
989 Ok(None)
990 }
991
992 async fn store(&self, _key: &[u8], _value: &[u8]) -> Result<(), Self::Error> {
993 Ok(())
994 }
995
996 async fn delete(&self, _key: &[u8]) -> Result<(), Self::Error> {
997 Ok(())
998 }
999}