From 156651d04a80810da63a87f50bb1b8203928c59d Mon Sep 17 00:00:00 2001 From: Cryptjar Date: Tue, 12 Apr 2022 18:42:11 +0200 Subject: [PATCH] Add a special rule for string literals that omits the unwrap call. Using `avr_progmem_string::string_as_bytes` macro, string literals can be converted into fixed-sized byte arrays allowing to use the `PmString::from_array` constructor, thus removing the `unwrap`, and consequently the necessity for the `const_option` Compiler feature. --- Cargo.toml | 6 ++++++ examples/uno-arrays.rs | 5 ----- src/string.rs | 38 ++++++++++++++++++++++++++++++++------ src/wrapper.rs | 39 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4df3de3..9793113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,12 @@ cfg-if = "1.0" version = "0.1.0" optional = true +[dependencies.avr-progmem-string] +# TODO: upgrade to a crates.io version +git = "https://github.com/mutantbob/avr-progmem-string.git" +rev = "2dc98b809a99098f70d38a8d1653d9cc6664e16e" + + [dev-dependencies] panic-halt = "0.2.0" diff --git a/examples/uno-arrays.rs b/examples/uno-arrays.rs index 4dd64cd..5f83f1d 100644 --- a/examples/uno-arrays.rs +++ b/examples/uno-arrays.rs @@ -7,11 +7,6 @@ // Define no_std only for AVR #![cfg_attr(target_arch = "avr", no_std)] #![cfg_attr(target_arch = "avr", no_main)] -// -// To unwrap the Option in const context -#![feature(const_option)] -// -#![feature(extended_key_value_attributes)] use avr_progmem::progmem; // The macro diff --git a/src/string.rs b/src/string.rs index 1236a73..db70490 100644 --- a/src/string.rs +++ b/src/string.rs @@ -85,8 +85,6 @@ //! Using [`PmString`] directly via the [`progmem`] macro: //! //! ```rust -//! #![feature(const_option)] -//! //! # use std::iter::FromIterator; //! use avr_progmem::progmem; //! use avr_progmem::string::LoadedString; @@ -95,8 +93,6 @@ //! // A simple Unicode string in progmem, internally stored as fix-sized //! // byte array, i.e. a `PmString<18>`. //! static progmem string TEXT = "Hello 大賢者"; -//! // text too large to fit in the RAM of a microcontroller -//! static progmem string LOVECRAFT = include_str!("../examples/test_text.txt"); //! } //! //! // You can load it all at once (like a `ProgMem`) @@ -132,8 +128,6 @@ //! [`progmem_display`]: //! //! ```rust -//! #![feature(const_option)] -//! //! use avr_progmem::progmem_str as F; //! use avr_progmem::progmem_display as D; //! @@ -159,6 +153,38 @@ //! ufmt::uwrite!(&mut writer, "{}", D!("Hello 大賢者")); //! ``` //! +//! You can also use arbitrary `&str`-yielding expression, however, +//! then you need Rust's `const_option` feature, because the string size +//! can no longer be determined statically. +//! +//! ```rust +//! #![feature(const_option)] +//! +//! use avr_progmem::progmem; +//! use avr_progmem::progmem_display as D; +//! # +//! # use ufmt::uWrite; +//! # struct MyWriter; +//! # impl uWrite for MyWriter { +//! # type Error = (); +//! # fn write_str(&mut self, _s: &str) -> Result<(),()> { +//! # Ok(()) // ignore input +//! # } +//! # } +//! # let mut writer = +//! # MyWriter +//! # /* SNIP */; +//! +//! progmem! { +//! // Text too large to fit in the RAM of a microcontroller +//! static progmem string LOVECRAFT = include_str!("../examples/test_text.txt"); +//! } +//! +//! // In-line a huge string from a file +//! #[cfg(feature = "ufmt")] // requires the `ufmt` crate feature +//! ufmt::uwrite!(&mut writer, "{}", D!(include_str!("../examples/test_text.txt"))); +//! ``` +//! use core::convert::TryFrom; diff --git a/src/wrapper.rs b/src/wrapper.rs index 5d5cd39..bf89b86 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -19,6 +19,10 @@ use core::convert::TryInto; +// Used in the `progmem` macro +#[doc(hidden)] +pub use avr_progmem_string::string_as_bytes; + use crate::raw::read_value; @@ -530,7 +534,40 @@ macro_rules! progmem { #[doc(hidden)] #[macro_export] macro_rules! progmem_internal { - // The string rule creating the progmem string static via `PmString` + // A string rule creating the progmem string static from a string literal + // using `string_as_bytes`, so no `unwrap` is required + { + $( #[ $attr:meta ] )* + $vis:vis static progmem string $name:ident = $value:literal ; + } => { + + // PmString must be stored in the progmem or text section! + // The link_section lets us define it: + #[cfg_attr(target_arch = "avr", link_section = ".progmem.data")] + + // User attributes + $(#[$attr])* + // The actual static definition + $vis static $name : $crate::string::PmString<{ + // This bit runs at compile-time + let s: &str = $value; + s.len() + }> = + unsafe { + // SAFETY: This call is sound, be cause we ensure with the above + // link_section attribute that this value is indeed in the + // progmem section. + $crate::string::PmString::from_array( + // Using `avr_progmem_string::string_as_bytes` macro to get + // a fixed size array, so there is no need for an `unwrap` + // anymore. + $crate::wrapper::string_as_bytes!( $value ) + ) + }; + }; + + // The string rule creating the progmem string static from an arbitrary + // string expression, thus a unwrap is needed. { $( #[ $attr:meta ] )* $vis:vis static progmem string $name:ident = $value:expr ;