-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add documentation about how to put a string in progmem #3
Comments
It is also unclear how to access data from #[cfg_attr(target_arch = "avr", link_section = ".progmem.data")]
static MESSAGE4: &[u8] = include_bytes!("parts1.txt"); seems like it creates a |
Thanks for bringing up these pain points. This is definitely something that should be mentioned in the documentation. Generally, you should not store any reference in progmem, because otherwise only the reference is in progmem instead of the actual data. Thus, you should rather use arrays directly, e.g.: #[cfg_attr(target_arch = "avr", link_section = ".progmem.data")]
static MESSAGE6: [u8; 9] = *b"dai kenja";
#[cfg_attr(target_arch = "avr", link_section = ".progmem.data")]
static MESSAGE4: [u8; 2031] = *include_bytes!("parts1.txt"); Non-ASCII byte literals, well, seem not to be supported by Rust, so either use the proposed manual UTF-8 encoding or maybe a char array: #[cfg_attr(target_arch = "avr", link_section = ".progmem.data")]
static MESSAGE5_1: [u8; 19] = *b"dai \xE5\xA4\xA7\xE8\xB3\xA2\xE8\x80\x85 kenja";
#[cfg_attr(target_arch = "avr", link_section = ".progmem.data")]
static MESSAGE5_2: [char; 13] = ['d', 'a', 'i', ' ', '大', '賢', '者', ' ', 'k', 'e', 'n', 'j', 'a']; Neither look particularly nice, but as far as I know, there is not much else you can do. Maybe one day something like this could work, but for now this is sadly not static MESSAGE5_3: [u8; 19] = "dai 大賢者 kenja".as_bytes().try_into().unwrap(); |
I have vague ideas for a procedural macro that lets you code a string literal and it turns into a wrapper around a progmem byte array. Maybe one day I'll make time to write it if no one beats me to it. Another complication is that ufmt's |
Your suggestion to do something with macros, got me intrigue. Admittedly, I mostly use progmem! {
static progmem TEXT: ByteString<19> = ByteString::new(
"dai 大賢者 kenja"
);
}
// usage:
let text_buffer = TEXT.load(); // The temporary DRAM buffer for `TEXT`
let text: &str = &text_buffer; // Just derefs to `str` Update: One can use that to offer convenient in-place string wrapping, e.g.: fn print(s: &str) {
// do something with `s`
}
// Put the literal as byte array into progmem and load it here as `&str`
print(progmem_str!("dai 大賢者 kenja")); Which would be just syntactic sugar for an ad-hoc progmem static and load. |
The best of all worlds would be if you can use ufmt to "print" a literal string that would not fit in RAM, but the least awkward way to do that would be to transfer the string byte-by-byte from progmem to the serial port or whatever, and ufmt does not have a byte interface at the moment. Workarounds would include doing 8(?)-byte slices at a time, probably using |
Hmm, reading strings byte-by-byte is an interesting thought. The In order to support partially reading of a Also, due to the variable length encoding of UTF-8, reading fixed blocks of bytes (such as 8 at a time) and then using However, at this point, I wounder whether this byte-wise reading & parsing is detrimental for performance, and whether, using a Do you have further thoughts on this? Update Actually, it's much harder to get a |
The usage style I implemented so far is: use avr_progmem::progmem;
// Imagine having something implementing `ufmt::uWrite`
let mut writer = /* snip */;
progmem! {
// declare a string with the additonal `string` pseudo-keyword.
// This will wrap the given `&str` in a `PmString`
static progmem string TEXT = "dai 大賢者 kenja";
}
// Either load at once (potential high RAM usage)
let buffer = TEXT.load();
// and use it as `&str`
writer.write_str(&*buffer).unwrap();
// Or load it lazily (one char at a time)
for c in TEXT.chars() {
writer.write_char(c).unwrap();
}
// Or use the `uDisplay` impl which is also lazy
ufmt::uformat!(&mut writer, "{}", TEXT).unwrap(); Additionally, for single-use strings, there are two macros for in-line strings: use avr_progmem::progmem_str as F;
use avr_progmem::progmem_display as D;
// Imagine having something implementing `ufmt::uWrite`
let mut writer = /* snip */;
// In-line progmem string accessible as temporary `&str`
// Needs enough RAM to store the entire string
writer.write_str(F!("dai 大賢者 kenja")).unwrap();
// In-line progmem string accessible as `impl Display + uDisplay`
// Needs only a minimal amount of RAM
ufmt::uwrite!(&mut writer, "{}", D!("dai 大賢者 kenja")).unwrap(); Would this cover your use-case? |
I think so. The funniest part is that I wrote some of this last week (the ability to turn You even made your own copy of The final transformation will be when the pieces of the format specification to the
would become
which probably isn't exactly how the macro works, but you get the idea. I suspect that most (if not all) of the work in this crate is done and it's just a question of tweaking ufmt (which, given that japaric/ufmt#32 has been sitting there since January, could take a while) |
Nice. I have few more things that I will implement before I will release a new
It'd be interesting whether your
That looks more complicated, probably needs a proc-macro. I suppose that could be its own crate. |
One thing I am fuzzy on, is "how do we check that a particular definition actually ends up in progmem instead of RAM?" I am also having difficulty writing code that uses this.
But I still get stuff like
I can see the code at line 511 of string.rs . |
The good news is that
works just fine, even though lovecraft.txt is an 11k file which has no chance of fitting in the RAM of an arduino UNO. |
OK, I think the After banging on Cargo.toml with increasingly large rocks, I was able to get it to compile and run with reasonable output. |
That is strange, this should work already. If I create a fresh crate it works for me: [package]
name = "test-avr-progmem-3"
version = "0.1.0"
edition = "2018"
[dependencies]
ufmt = "0.1"
[dependencies.avr-progmem]
git="https://github.com/Cryptjar/avr-progmem-rs.git"
branch="v0.2" #![feature(const_option)]
avr_progmem::progmem! {
static progmem string FOO = "Foobar";
}
pub fn x<D: ufmt::uDisplay>(blah: &D) {}
fn main() {
x(&FOO);
} That compiles just fine for me (you don't even need the
Maybe you need to do a |
Yes that looks like it, I mean [dependencies]
ufmt = {git = "https://github.com/mrk-its/ufmt.git", branch = "ptr_width_16_fix"} that would be incompatible, using this, I get the same error as you. If you instead patch it, like I did in [dependencies]
ufmt = "0.1"
[patch.crates-io]
# Fixes formatting of u32
ufmt = { git = "https://github.com/mrk-its/ufmt.git", branch = "ptr_width_16_fix" } |
after I harmonized the ufmt crate revisions, this is one of the tests I used:
I'm not sure if that would be a good example for the docstrings. But right now I think the specific issue this ticket was originally about is solved. The code works in my testing (ignoring the rev -vs- branch weirdness that will be irrelevant if ufmt ever merges that fix). The docstrings on the |
If have something to expand the docs, I am open to any PRs.
Good to hear. I'm going to close this issue, when I released the new version on crates.io. |
One last thing: the initializer for If you ever feel like going nuclear, you might experiment with my procedural macro:
its syntax is a little different than yours, but could be adjusted to match. |
That looks interesting. But I can't just include a proc-macro into the let bytes: [u8; 13] = byte_string!("dai 大賢者 kenja"); Having such a macro, would allow using the |
Until I get around to that... https://github.com/mutantbob/avr-progmem-string/blob/master/src/lib.rs |
I added a procedural macro |
Well, the nice thing about See: 156651d |
I just released |
I have spent the afternoon experimenting with how to put a string in progmem.
Using
only puts the pointer and count of the slice in progmem. The actual characters still end up in RAM.
It is possible to use something like
But if you try
you get an error:
The text was updated successfully, but these errors were encountered: