Module avr_progmem::string
source · Expand description
String utilities
This module offers further utilities base on ProgMem
to make working
with strings in progmem more convenient.
The difficulty with strings is that normally they are either heap allocated
(as with the std Rust String
) or dynamically sized (as with str
).
To store a string in progmem, one needs first of all a fixed-sized storage
variant of a str
.
One option is to use byte string literals (e.g. b"foobar"
), however,
for some reason those only accept ASCII and no Unicode.
At some day, one might be able to to convert arbitrary string literals to
byte arrays like this:
// Dose not compile as of 1.51, because `try_into` is not a const fn, yet
static WIKIPEDIA: [u8; 12] = "维基百科".as_bytes().try_into().unwrap();
However, for the time being, this module offers as a convenient workaround:
LoadedString
a simple UTF-8 encoded sized byte arrayPmString
a UTF-8 encoded sized byte array in progmem similar toProgMem
.
Working with Strings
To work with strings in progmem, this crate offers two APIs and two modes of operation, each with their own little tradeoff.
Operation Modes
When you want to use strings from progmem you have two options:
- you can load them as whole from progmem into RAM and work with them
essentially like stack-allocated
&str
s - or, you choose to load the strings
char
afterchar
from progmem and work with them using achar
-iterator.
The first mode of operation obviously allows you to use them everywhere,
where you can use a &str
, giving you high compatibility with other APIs.
On the other hand, this comes at the cost of high RAM usage.
So you must leave enough free RAM to fit all your string, thus the bigger,
your biggest string is, the less RAM you must use statically.
So, you might have to split your strings somehow to make them manageable.
The alternative is to only load just one char
at a time.
This obviously limits the amount of RAM that you need, independently of
how big your strings are, allowing you to work with really huge strings.
However, you no longer get a &str
, any you have make do with a char
iterator.
However, if you only need your strings to be printed in some way,
the Display
and ufmt::uDisplay
traits implementations
(the latter only if the ufmt
crate feature is enabled) of PmString
,
might become very handy.
These trait implementations only need the char
-iterator so they are very
economic with respect to RAM usage.
APIs
API-wise you can either:
- define progmem
static
s via theprogmem
macro and use them all over your program, - or, you create single-use progmem strings via the
progmem_str
andprogmem_display
macro
The single-use macros are the most concise option, but also a rather
special-case solution.
progmem_str
gives you are very temporary &str
to an ad-hoc loaded
progmem string, so you can only pass it to a function call and you need
enough RAM to store it.
On the other hand, progmem_display
gives you
just something that is impl Display + uDisplay
, so you can just print it,
but it has minimal RAM usage.
If need anything more flexible or fancy, you are probably best served
creating a static
via progmem
macro.
Examples
Using PmString
directly via the progmem
macro:
use avr_progmem::progmem;
use avr_progmem::string::LoadedString;
progmem! {
// A simple Unicode string in progmem, internally stored as fix-sized
// byte array, i.e. a `PmString<18>`.
static progmem string TEXT = "Hello 大賢者";
}
// You can load it all at once (like a `ProgMem`)
let buffer: LoadedString<15> = TEXT.load();
// and use that as `&str`
assert_eq!("Hello 大賢者", &*buffer);
// Or you load it one char at a time (limits RAM usage)
let chars_iter = TEXT.chars(); // impl Iterator<Item=char>
let exp = ['H', 'e', 'l', 'l', 'o', ' ', '大', '賢', '者'];
assert_eq!(&exp, &*Vec::from_iter(chars_iter));
// Or you use the `Display`/`uDisplay` impl on `PmString`
fn foo<W: ufmt::uWrite>(writer: &mut W) {
#[cfg(feature = "ufmt")] // requires the `ufmt` crate feature
ufmt::uwrite!(writer, "{}", TEXT);
}
Using the special literal in-line string macros progmem_str
(yielding a &str
) and progmem_display
(yielding some impl Display + uDisplay
):
use avr_progmem::progmem_str as F;
use avr_progmem::progmem_display as D;
fn foo<W: ufmt::uWrite>(writer: &mut W) {
// In-line string as temporary `&str`
writer.write_str(F!("Hello 大賢者"));
// In-line string as some `impl Display + uDisplay`
#[cfg(feature = "ufmt")] // requires the `ufmt` crate feature
ufmt::uwrite!(writer, "{}", D!("Hello 大賢者"));
}
You can also use arbitrary &str
-yielding expression, including loading
huge strings from files, just don’t use PmString::load
nor progmem_str
with huge strings (because if it is bigger than 255 bytes, it will panic).
use avr_progmem::progmem;
use avr_progmem::progmem_display as D;
progmem! {
// Text too large to fit in the RAM of a Arduino Uno
static progmem string HUGE_TEXT = include_str!("../examples/test_text.txt");
}
println!("{}", HUGE_TEXT);
// In-line a huge string from a file
println!("{}", D!(include_str!("../examples/test_text.txt")));
Structs
- Indicates that static type size does not match the dynamic str length.
- A string stored as byte array.
- An iterator over a
PmString
- A byte string in progmem