ockam_core/
compat.rs

1//! A facade around the various collections and primitives needed to
2//! support `std`, `no_std + alloc` or `no_std` targets.
3//!
4//! When importing from the standard library:
5//!
6//!   1. always prefer `core::<mod>` over `std::<mod>` where it's
7//!      available. (e.g. `std::fmt::Result` -> `core::fmt::Result`)
8//!   2. use `ockam_core::compat::<mod>` equivalents where
9//!      possible. (e.g. `std::sync::Arc` -> `ockam_core::compat::sync::Arc`)
10//!   3. if you need to add new items to compat, follow the originating
11//!      namespace. (e.g. `compat::vec::Vec` and not `compat::Vec`)
12
13/// Provides `std::borrow` for `alloc` targets.
14#[cfg(feature = "alloc")]
15pub use alloc::borrow;
16
17#[doc(hidden)]
18pub use futures_util::{join, try_join};
19
20/// Provides `std::boxed` for `alloc` targets.
21pub mod boxed {
22    #[cfg(feature = "alloc")]
23    pub use alloc::boxed::Box;
24}
25
26/// Provides `std::collections` and alternate `hashbrown` map and set
27/// implementations.
28pub mod collections {
29    #[cfg(feature = "alloc")]
30    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
31
32    pub use hashbrown::{HashMap, HashSet};
33
34    /// hash map
35    pub mod hash_map {
36        pub use hashbrown::hash_map::{Entry, EntryRef};
37        pub use hashbrown::Equivalent;
38    }
39
40    /// btree map
41    #[cfg(feature = "alloc")]
42    pub mod btree_map {
43        pub use alloc::collections::btree_map::Entry;
44    }
45}
46
47/// Provides a `std::error::Error` trait.
48pub mod error {
49    #[cfg(not(feature = "std"))]
50    /// A `no_std` compatible definition of the `std::error::Error` trait.
51    pub trait Error: core::fmt::Debug + core::fmt::Display {
52        /// The source of this error.
53        fn source(&self) -> Option<&(dyn Error + 'static)> {
54            None
55        }
56    }
57    #[cfg(feature = "std")]
58    pub use std::error::Error;
59}
60
61/// Provides `std::format` for `alloc` targets.
62#[cfg(feature = "alloc")]
63pub use alloc::format;
64
65/// Provides `std::io`.
66#[cfg(not(feature = "std"))]
67pub use core2::io;
68#[cfg(feature = "std")]
69pub use std::io;
70
71/// Provides `std::net`.
72#[cfg(feature = "std")]
73pub use std::net;
74
75/// Provides a `println!` wrapper around `tracing::info!` for `no_std` targets
76#[cfg(all(not(feature = "std"), feature = "alloc"))]
77pub mod println {
78    #[macro_export]
79    /// Implementation of println for `no_std` by wrapping the `tracing::info!` macro.
80    macro_rules! println {
81        ($($arg:tt)*) => {{
82            tracing::info!($($arg)*);
83        }};
84    }
85}
86
87/// Provides `rand`.
88pub mod rand {
89    pub use rand::distributions;
90    pub use rand::prelude;
91    pub use rand::CryptoRng;
92    pub use rand::Error;
93    pub use rand::Rng;
94    pub use rand::RngCore;
95
96    #[cfg(not(feature = "std"))]
97    pub use not_random::thread_rng;
98    #[cfg(feature = "std")]
99    pub use rand::thread_rng;
100
101    #[cfg(not(feature = "std"))]
102    pub use not_random::random;
103    #[cfg(feature = "std")]
104    pub use rand::random;
105
106    /// rngs
107    #[cfg(feature = "std")]
108    pub use rand::rngs;
109    #[cfg(not(feature = "std"))]
110    /// A placeholder implementation of the `rand::rngs` generators module.
111    ///
112    /// WARNING: This implementation does NOT generate true random
113    /// values, please do not try to use it in production.
114    pub mod rngs {
115        pub use super::not_random::OsRng;
116    }
117
118    /// Generates a random String of length 16.
119    #[cfg(feature = "std")]
120    pub fn random_string() -> String {
121        use rand::distributions::{Alphanumeric, DistString};
122        Alphanumeric.sample_string(&mut thread_rng(), 16)
123    }
124
125    /// Placeholders for various features from 'rand' that are not
126    /// supported on no_std targets.
127    ///
128    /// WARNING: This implementation does NOT generate true random
129    /// values, please do not try to use any of these in production.
130    #[cfg(not(feature = "std"))]
131    mod not_random {
132        use super::*;
133
134        #[derive(Clone)]
135        pub struct FakeRng(rand_pcg::Lcg64Xsh32);
136
137        impl CryptoRng for FakeRng {}
138
139        impl RngCore for FakeRng {
140            fn next_u32(&mut self) -> u32 {
141                self.0.gen()
142            }
143
144            fn next_u64(&mut self) -> u64 {
145                self.0.gen()
146            }
147
148            fn fill_bytes(&mut self, dest: &mut [u8]) {
149                if let Err(e) = self.0.try_fill_bytes(dest) {
150                    panic!("Error: {}", e);
151                }
152            }
153
154            fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
155                self.0.try_fill(dest)
156            }
157        }
158
159        /// An implementation of `rand::thread_rng()` not intended for
160        /// production use.
161        ///
162        /// WARNING: This implementation is neither random nor
163        /// thread-local.
164        #[allow(unsafe_code, static_mut_refs)]
165        pub fn thread_rng() -> FakeRng {
166            use rand::SeedableRng;
167            static mut RNG: Option<rand_pcg::Lcg64Xsh32> = None;
168            unsafe {
169                if RNG.is_none() {
170                    RNG = Some(rand_pcg::Pcg32::seed_from_u64(1234));
171                }
172            }
173            let lcg = unsafe { rand_pcg::Pcg32::seed_from_u64(RNG.as_mut().unwrap().gen()) };
174
175            FakeRng(lcg)
176        }
177
178        /// An implementation of `rand::random()` not intended for
179        /// production use.
180        pub fn random<T>() -> T
181        where
182            distributions::Standard: prelude::Distribution<T>,
183        {
184            let mut rng = thread_rng();
185            rng.gen()
186        }
187
188        /// `rand::OsRng`
189        pub struct OsRng;
190
191        impl CryptoRng for OsRng {}
192
193        impl RngCore for OsRng {
194            fn next_u32(&mut self) -> u32 {
195                let mut rng = thread_rng();
196                rng.gen()
197            }
198
199            fn next_u64(&mut self) -> u64 {
200                let mut rng = thread_rng();
201                rng.gen()
202            }
203
204            fn fill_bytes(&mut self, dest: &mut [u8]) {
205                if let Err(e) = self.try_fill_bytes(dest) {
206                    panic!("Error: {}", e);
207                }
208            }
209
210            fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
211                let mut rng = thread_rng();
212                rng.try_fill(dest)
213            }
214        }
215    }
216}
217
218/// Provides `std::string`.
219pub mod string {
220    #[cfg(feature = "alloc")]
221    pub use alloc::string::{String, ToString};
222    #[cfg(not(feature = "alloc"))]
223    use heapless::String as ByteString;
224}
225
226/// Provides `std::str`.
227pub mod str {
228    #[cfg(feature = "alloc")]
229    pub use alloc::str::from_utf8;
230    #[cfg(feature = "alloc")]
231    pub use alloc::str::FromStr;
232}
233
234/// Provides `std::sync` for `no_std` targets.
235#[cfg(not(feature = "std"))]
236pub mod sync {
237    use core::convert::Infallible;
238
239    pub use alloc::sync::{Arc, Weak};
240
241    /// Wrap `spin::RwLock` as it does not return LockResult<Guard> like `std::sync::Mutex`.
242    #[derive(Debug)]
243    pub struct RwLock<T>(spin::RwLock<T>);
244
245    /// Wrap `spin::RwLockWriteGuard`
246    pub type RwLockWriteGuard<'a, T> = spin::RwLockWriteGuard<'a, T>;
247
248    impl<T> RwLock<T> {
249        /// Creates a new spinlock wrapping the supplied data.
250        pub fn new(value: T) -> Self {
251            RwLock(spin::RwLock::new(value))
252        }
253        /// Locks this rwlock with shared read access, blocking the current thread
254        /// until it can be acquired.
255        pub fn read(&self) -> Result<spin::RwLockReadGuard<'_, T>, Infallible> {
256            Ok(self.0.read())
257        }
258        /// Lock this rwlock with exclusive write access, blocking the current
259        /// thread until it can be acquired.
260        pub fn write(&self) -> Result<spin::RwLockWriteGuard<'_, T>, Infallible> {
261            Ok(self.0.write())
262        }
263    }
264    impl<T: Default> Default for RwLock<T> {
265        fn default() -> Self {
266            Self::new(Default::default())
267        }
268    }
269    impl<T> From<T> for RwLock<T> {
270        fn from(t: T) -> Self {
271            Self::new(t)
272        }
273    }
274    impl<T> core::ops::Deref for RwLock<T> {
275        type Target = spin::RwLock<T>;
276        fn deref(&self) -> &spin::RwLock<T> {
277            &self.0
278        }
279    }
280    impl<T> core::ops::DerefMut for RwLock<T> {
281        fn deref_mut(&mut self) -> &mut spin::RwLock<T> {
282            &mut self.0
283        }
284    }
285
286    /// Wrap `spin::Mutex.lock()` as it does not return LockResult<Guard> like `std::sync::Mutex`.
287    pub struct Mutex<T>(spin::Mutex<T>);
288    impl<T> Mutex<T> {
289        /// Creates a new mutex in an unlocked state ready for use.
290        pub const fn new(value: T) -> Self {
291            Mutex(spin::Mutex::new(value))
292        }
293        /// Acquires a mutex, blocking the current thread until it is able to do so.
294        pub fn lock(&self) -> Result<spin::MutexGuard<'_, T>, Infallible> {
295            Ok(self.0.lock())
296        }
297    }
298    impl<T> core::ops::Deref for Mutex<T> {
299        type Target = spin::Mutex<T>;
300        fn deref(&self) -> &spin::Mutex<T> {
301            &self.0
302        }
303    }
304    impl<T> core::ops::DerefMut for Mutex<T> {
305        fn deref_mut(&mut self) -> &mut spin::Mutex<T> {
306            &mut self.0
307        }
308    }
309    impl<T> Default for Mutex<T>
310    where
311        T: Default,
312    {
313        fn default() -> Self {
314            Self::new(Default::default())
315        }
316    }
317    impl<T> From<T> for Mutex<T> {
318        fn from(t: T) -> Self {
319            Self::new(t)
320        }
321    }
322}
323/// Provides `std::sync` for `std` targets.
324#[cfg(feature = "std")]
325pub mod sync {
326    pub use std::sync::{Arc, Weak};
327    pub use std::sync::{Mutex, RwLock};
328}
329
330/// Provides `std::task` for `no_std` targets.
331#[cfg(not(feature = "std"))]
332pub mod task {
333    // Include both `alloc::task::*` and `core::task::*` for a better
334    // approximation of `std::task::*` (which contains both).
335    #[cfg(feature = "alloc")]
336    pub use alloc::task::*;
337    pub use core::task::*;
338}
339
340/// Provides `std::task` for `std` targets.
341#[cfg(feature = "std")]
342pub use std::task;
343
344/// Provides `std::vec`.
345pub mod vec {
346    #[cfg(feature = "alloc")]
347    pub use alloc::vec;
348    #[cfg(feature = "alloc")]
349    pub use alloc::vec::*;
350    #[cfg(not(feature = "alloc"))]
351    pub type Vec<T> = heapless::Vec<T, 64>;
352}
353
354/// Provides `std::time` for `std` targets.
355#[cfg(feature = "std")]
356pub mod time {
357    pub use std::time::*;
358
359    /// Create a new timestamp using the system time
360    pub fn now() -> crate::Result<u64> {
361        if let Ok(now) = SystemTime::now().duration_since(UNIX_EPOCH) {
362            Ok(now.as_secs())
363        } else {
364            Err(crate::Error::new(
365                crate::errcode::Origin::Core,
366                crate::errcode::Kind::Unsupported,
367                "Can't get time",
368            ))?
369        }
370    }
371}
372
373/// Provides `std::time` for no_std targets
374#[cfg(not(feature = "std"))]
375pub mod time {
376    pub use core::time::Duration;
377
378    /// Create a new timestamp using the system time
379    #[cfg(not(feature = "std"))]
380    pub fn now() -> crate::Result<u64> {
381        match utcnow::utcnow() {
382            Ok(time) => Ok(time.as_secs() as u64),
383            Err(_err) => Err(crate::Error::new(
384                crate::errcode::Origin::Core,
385                crate::errcode::Kind::Unsupported,
386                "Can't get time",
387            ))?,
388        }
389    }
390}
391
392/// Provides `core::fmt`
393pub mod fmt {
394    #[cfg(feature = "alloc")]
395    pub use alloc::fmt::*;
396    #[cfg(not(feature = "alloc"))]
397    pub use core::fmt::*;
398}
399
400/// Provides `future::poll_once`
401pub mod future {
402    use crate::{
403        errcode::{Kind, Origin},
404        Error, Result,
405    };
406    use futures_util::future::{Future, FutureExt};
407
408    /// Polls a future just once and returns the Result
409    ///
410    /// This is only used for some tests and it is hoped that we can
411    /// remove it if, at some point, this makes it into `core::future`
412    pub fn poll_once<'a, F, T>(future: F) -> Result<T>
413    where
414        F: Future<Output = Result<T>> + Send + 'a,
415    {
416        use core::task::{Context, Poll};
417        use core::task::{RawWaker, RawWakerVTable, Waker};
418
419        fn dummy_raw_waker() -> RawWaker {
420            fn no_op(_: *const ()) {}
421            fn clone(_: *const ()) -> RawWaker {
422                dummy_raw_waker()
423            }
424            let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op);
425            RawWaker::new(core::ptr::null(), vtable)
426        }
427
428        fn dummy_waker() -> Waker {
429            // The RawWaker's vtable only contains safe no-op
430            // functions which do not refer to the data field.
431            #[allow(unsafe_code)]
432            unsafe {
433                Waker::from_raw(dummy_raw_waker())
434            }
435        }
436
437        let waker = dummy_waker();
438        let mut context = Context::from_waker(&waker);
439        let result = future.boxed().poll_unpin(&mut context);
440        assert!(
441            result.is_ready(),
442            "poll_once() only accepts futures that resolve after being polled once"
443        );
444        match result {
445            Poll::Ready(value) => value,
446            Poll::Pending => Err(Error::new_without_cause(Origin::Core, Kind::Invalid)),
447        }
448    }
449}