|
| 1 | +use core::ops::Deref; |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +/// Our own `core::array::TryFromSliceError` |
| 6 | +/// |
| 7 | +/// Used in [`array_ref_try_from_slice`]. |
| 8 | +// We need a local copy of this type, because we need to instantiate it, but it |
| 9 | +// has a private field. |
| 10 | +struct TryFromSliceError(()); |
| 11 | + |
| 12 | +/// Const version of `<&[T; N]>::try_from(&[T])` |
| 13 | +/// |
| 14 | +/// Original Source: |
| 15 | +/// https://github.com/rust-lang/rust/blob/eb82facb1626166188d49599a3313fc95201f556/library/core/src/array/mod.rs#L203-L215 |
| 16 | +const fn array_ref_try_from_slice<'a, T, const N: usize>( |
| 17 | + slice: &[T], |
| 18 | +) -> Result<&[T; N], TryFromSliceError> { |
| 19 | + if slice.len() == N { |
| 20 | + let ptr = slice.as_ptr() as *const [T; N]; |
| 21 | + // SAFETY: ok because we just checked that the length fits |
| 22 | + unsafe { Ok(&*ptr) } |
| 23 | + } else { |
| 24 | + Err(TryFromSliceError(())) |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +/// A string stored as byte array. |
| 29 | +/// |
| 30 | +/// This type is a simple wrapper around a byte array `[u8;N]` and therefore, |
| 31 | +/// is stored as such. |
| 32 | +/// However, this type primarily is created from `&str` and derefs to `&str`, |
| 33 | +/// thus it can be used similar to `String` except that it is not mutable. |
| 34 | +/// |
| 35 | +/// This type is particularly useful to store string literals in progmem. |
| 36 | +/// |
| 37 | +/// # Example |
| 38 | +/// |
| 39 | +/// ```rust |
| 40 | +/// #![feature(const_option)] |
| 41 | +/// |
| 42 | +/// use avr_progmem::progmem; |
| 43 | +/// use avr_progmem::string::ByteString; |
| 44 | +/// |
| 45 | +/// progmem! { |
| 46 | +/// // Stores a string as a byte array, i.e. `[u8;19]`, but makes it usable |
| 47 | +/// // as `&str` (via `Deref`) |
| 48 | +/// static progmem TEXT: ByteString<19> = ByteString::new( |
| 49 | +/// "dai 大賢者 kenja" |
| 50 | +/// ).unwrap(); |
| 51 | +/// } |
| 52 | +/// |
| 53 | +/// // usage: |
| 54 | +/// let text_buffer = TEXT.load(); // The temporary DRAM buffer for `TEXT` |
| 55 | +/// let text: &str = &text_buffer; // Just derefs to `str` |
| 56 | +/// assert_eq!(text, "dai 大賢者 kenja") |
| 57 | +/// ``` |
| 58 | +
|
| 59 | +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 60 | +pub struct ByteString<const N: usize>(pub [u8; N]); |
| 61 | + |
| 62 | +impl<const N: usize> ByteString<N> { |
| 63 | + /// Creates a new byte array from the given string |
| 64 | + pub const fn new(s: &str) -> Option<Self> { |
| 65 | + Self::from_bytes(s.as_bytes()) |
| 66 | + } |
| 67 | + |
| 68 | + /// Wraps the given byte slice |
| 69 | + pub const fn from_bytes(bytes: &[u8]) -> Option<Self> { |
| 70 | + let res = array_ref_try_from_slice(bytes); |
| 71 | + |
| 72 | + match res { |
| 73 | + Ok(array) => Some(Self(*array)), |
| 74 | + Err(_e) => None, |
| 75 | + } |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +impl<const N: usize> Deref for ByteString<N> { |
| 80 | + type Target = str; |
| 81 | + |
| 82 | + fn deref(&self) -> &str { |
| 83 | + core::str::from_utf8(&self.0).unwrap() |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +/// Define a string in progmem |
| 88 | +/// |
| 89 | +/// This is a short-cut macro to create an ad-hoc static storing the given |
| 90 | +/// string literal as by [`ByteString`] and load it here from progmem into a |
| 91 | +/// temporary and return it as `&str`. |
| 92 | +/// |
| 93 | +/// This macro allows to conveniently put literal string into progmem exactly, |
| 94 | +/// where they are used. However, since they are directly loaded into a |
| 95 | +/// temporary you don't get a `&'static str` back, and must use the `&str` |
| 96 | +/// immediately (i.e. pass it as a function parameter). |
| 97 | +/// You can't even store the returned `&str` in a local `let` assignment. |
| 98 | +/// |
| 99 | +/// # Example |
| 100 | +/// |
| 101 | +/// ```rust |
| 102 | +/// #![feature(const_option)] |
| 103 | +/// |
| 104 | +/// use avr_progmem::progmem_str as S; |
| 105 | +/// |
| 106 | +/// fn print(s: &str) { |
| 107 | +/// // -- snip -- |
| 108 | +/// # assert_eq!(s, "dai 大賢者 kenja") |
| 109 | +/// } |
| 110 | +/// |
| 111 | +/// // Put the literal as byte array into progmem and load it here as `&str` |
| 112 | +/// print(S!("dai 大賢者 kenja")); |
| 113 | +/// ``` |
| 114 | +#[macro_export] |
| 115 | +macro_rules! progmem_str { |
| 116 | + ($text:literal) => {{ |
| 117 | + const TEXT_LEN: usize = <str>::as_bytes($text).len(); |
| 118 | + $crate::progmem! { |
| 119 | + static progmem TEXT: $crate::string::ByteString<TEXT_LEN> = $crate::string::ByteString::new( |
| 120 | + $text |
| 121 | + ).unwrap(); |
| 122 | + } |
| 123 | + &*TEXT.load() |
| 124 | + }}; |
| 125 | +} |
0 commit comments