Skip to content

Commit 307d2ba

Browse files
committed
Write some docs
1 parent fdac0d1 commit 307d2ba

File tree

5 files changed

+167
-12
lines changed

5 files changed

+167
-12
lines changed

README.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ compiler.
2020
## MSRV
2121

2222
This crate only works with a Rust `nightly-2021-01-07` compiler, which is,
23-
as of the time of writing (early 2022), still the latest version that
23+
as of the time of writing (early 2022), still the most recent version that
2424
supports AVR
2525
(see <https://github.com/rust-lang/compiler-builtins/issues/400>).
2626
So it is actually, the minimum and maximum supported version.
2727
All versions `0.2.x` will adhere to work with `nightly-2021-01-07`.
2828

29-
Future versions such as `0.3.x` might required a new different Rust compiler
29+
Future versions such as `0.3.x` might required a newer Rust compiler
3030
version.
3131

3232

@@ -76,7 +76,7 @@ data memory, causing **undefined behavior**!
7676
## Example
7777

7878
```rust
79-
use avr_progmem::read_byte;
79+
use avr_progmem::raw::read_byte;
8080

8181
// This `static` must never be directly dereferenced/accessed!
8282
// So a `let data: u8 = P_BYTE;` is **undefined behavior**!!!
@@ -136,6 +136,28 @@ assert_eq!(b'A', data);
136136
```
137137

138138

139+
# Strings
140+
141+
Using strings such as `&str` with [`ProgMem`] is rather difficult, and
142+
surprisingly hard if Unicode support is needed
143+
(see <https://github.com/Cryptjar/avr-progmem-rs/issues/3>).
144+
Thus, to make working with string convenient the
145+
[`PmString`](string::PmString) struct is provided on top of [`ProgMem`].
146+
147+
[`PmString`](string::PmString) stores any given `&str` as statically sized
148+
UTF-8 byte array (with full Unicode support).
149+
To make its content usable, it provides a `Display` & `uDisplay`
150+
implementation, a lazy `char` iterator, and `load` function similar to
151+
[`ProgMem`], that yields a [`LoadedString`](string::LoadedString),
152+
which in turn defers to `&str`.
153+
154+
TODO: how about the in-line macro
155+
156+
## Example
157+
158+
TODO
159+
160+
139161
# Other Architectures
140162

141163
As mentioned before, this crate is specifically designed to be use with

src/lib.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
//! ## MSRV
2828
//!
2929
//! This crate only works with a Rust `nightly-2021-01-07` compiler, which is,
30-
//! as of the time of writing (early 2022), still the latest version that
30+
//! as of the time of writing (early 2022), still the most recent version that
3131
//! supports AVR
3232
//! (see <https://github.com/rust-lang/compiler-builtins/issues/400>).
3333
//! So it is actually, the minimum and maximum supported version.
3434
//! All versions `0.2.x` will adhere to work with `nightly-2021-01-07`.
3535
//!
36-
//! Future versions such as `0.3.x` might required a new different Rust compiler
36+
//! Future versions such as `0.3.x` might required a newer Rust compiler
3737
//! version.
3838
//!
3939
//!
@@ -143,6 +143,28 @@
143143
//! ```
144144
//!
145145
//!
146+
//! # Strings
147+
//!
148+
//! Using strings such as `&str` with [`ProgMem`] is rather difficult, and
149+
//! surprisingly hard if Unicode support is needed
150+
//! (see <https://github.com/Cryptjar/avr-progmem-rs/issues/3>).
151+
//! Thus, to make working with string convenient the
152+
//! [`PmString`](string::PmString) struct is provided on top of [`ProgMem`].
153+
//!
154+
//! [`PmString`](string::PmString) stores any given `&str` as statically sized
155+
//! UTF-8 byte array (with full Unicode support).
156+
//! To make its content usable, it provides a `Display` & `uDisplay`
157+
//! implementation, a lazy `char` iterator, and `load` function similar to
158+
//! [`ProgMem`], that yields a [`LoadedString`](string::LoadedString),
159+
//! which in turn defers to `&str`.
160+
//!
161+
//! TODO: how about the in-line macro
162+
//!
163+
//! ## Example
164+
//!
165+
//! TODO
166+
//!
167+
//!
146168
//! # Other Architectures
147169
//!
148170
//! As mentioned before, this crate is specifically designed to be use with
@@ -194,6 +216,6 @@
194216

195217
pub mod raw;
196218
pub mod string;
197-
mod wrapper;
219+
pub mod wrapper;
198220

199221
pub use wrapper::ProgMem;

src/raw.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
//! Raw direct progmem access
2+
//!
3+
//! This module provides functions to directly access the progmem, such as
4+
//! [read_value].
5+
//!
6+
//! It is recommended to use best-effort wrappers in [wrapper](crate::wrapper)
7+
//! and [string](crate::string), which use these functions internally.
8+
//! This is in particular, because having a raw `static` that is stored in the
9+
//! progmem is very hazardous since Rust does not understand the difference
10+
//! between the normal data memory domain and the program memory domain, and
11+
//! allows safe code to directly access those raw progmem statics, which is
12+
//! **undefined behavior**.
13+
//! The wrapper types in [wrapper](crate::wrapper) and [string](crate::string),
14+
//! prevent safe code from directly accessing these statics and only offer
15+
//! dedicated accessor methods that first load the data into the normal data
16+
//! memory domain via the function of this module.
17+
18+
119
use core::mem::size_of;
220
use core::mem::MaybeUninit;
321

src/string.rs

+74-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1+
//! String utilities
2+
//!
3+
//! This module offers further utilities base on [`ProgMem`] to make working
4+
//! with strings in progmem more convenient.
5+
//!
6+
//! The difficulty with strings is that normally they are either heap allocated
7+
//! (as with the std Rust `String`) or dynamically sized (as with `str`).
8+
//! To store a string in progmem, one needs first of all a fixed-sized storage
9+
//! variant of a `str`.
10+
//! One option is to use byte string literals (e.g. `b"foobar"`), however,
11+
//! for some reason those only accept ASCII and no Unicode.
12+
//! At some day, one might be able to to convert arbitrary string literals to
13+
//! byte arrays like this:
14+
//!
15+
//! ```ignore
16+
//! # use std::convert::TryInto;
17+
//! // Dose not compile as of 1.51, because `try_into` is not a const fn
18+
//! static WIKIPEDIA: [u8; 12] = "维基百科".as_bytes().try_into().unwrap();
19+
//! ```
20+
//!
21+
//! As a convenient workaround, this module offers:
22+
//! * [`LoadedString`] a simple UTF-8 encoded sized byte array
23+
//! * [`PmString`] a UTF-8 encoded sized byte array in progmem similar to [`ProgMem`].
24+
//!
25+
//! Also the [`progmem`](crate::progmem) macro offers a special syntax for
26+
//! converting a normal string literal into a [`PmString`]:
27+
//!
28+
//! ```rust
29+
//! // TODO: code
30+
//! ```
31+
//!
32+
33+
134
use core::convert::TryFrom;
235
use core::fmt;
336
use core::ops::Deref;
@@ -175,10 +208,19 @@ impl<const N: usize> ufmt::uDisplay for LoadedString<N> {
175208
/// A byte string in progmem
176209
///
177210
/// Not to be confused with a [`LoadedString`].
178-
/// A `LoadedString` is just a wrapper around a byte array (`[u8;N]`) that can
179-
/// be put into a [`ProgMem`].
180-
/// A `PmString` on the other hand, is a wrapper around a
181-
/// `ProgMem<[u8;N]>`.
211+
/// A `LoadedString` is a simple wrapper around a byte array (`[u8;N]`) that
212+
/// derefs to `str`, and should be used in RAM.
213+
/// A `PmString` on the other hand, is a wrapper around a byte array in progmem
214+
/// aka around a `ProgMem<[u8;N]>`, and thus must always be progmem.
215+
/// Similar to `ProgMem`, `PmString` offers a [`load`](PmString::load) method to
216+
/// load its entire content into RAM.
217+
/// The loaded content will be a `LoadedString`, hence the name.
218+
///
219+
/// Besides loading the entire string at once into RAM, `PmString` also offers
220+
/// a lazy [`chars`](PmString::chars) iterator method, that will load just one
221+
/// char at a time.
222+
/// This allows `chars` to be used on very large strings that do not fit into
223+
/// the RAM as whole.
182224
///
183225
/// # Safety
184226
///
@@ -208,6 +250,8 @@ impl<const N: usize> PmString<N> {
208250
/// This function is only sound to call, if the value is
209251
/// stored in a static that is for instance attributed with
210252
/// `#[link_section = ".progmem.data"]`.
253+
///
254+
/// You are encouraged to use the [`progmem`] macro instead.
211255
pub const unsafe fn new(s: &str) -> Option<Self> {
212256
Self::from_bytes(s.as_bytes())
213257
}
@@ -267,6 +311,18 @@ impl<const N: usize> PmString<N> {
267311
}
268312

269313
/// Loads the entire string into RAM
314+
///
315+
/// # Panics
316+
///
317+
/// This method panics, if the size of the value (i.e. `N`) is beyond 255
318+
/// bytes.
319+
/// However, this is currently just a implementation limitation, which may
320+
/// be lifted in the future.
321+
///
322+
/// If you have a very large string, consider using the lazy
323+
/// [`chars`](Self::chars) iterator that accesses the string by one char at
324+
/// a time and thus does not have such a limitation.
325+
///
270326
pub fn load(&self) -> LoadedString<N> {
271327
let array = self.load_bytes();
272328

@@ -279,6 +335,17 @@ impl<const N: usize> PmString<N> {
279335
}
280336

281337
/// Loads the entire string as byte array into RAM
338+
///
339+
/// # Panics
340+
///
341+
/// This method panics, if the size of the value (i.e. `[u8; N]`) is beyond
342+
/// 255 bytes.
343+
/// However, this is currently just a implementation limitation, which may
344+
/// be lifted in the future.
345+
///
346+
/// If you have a very large string, consider using the lazy
347+
/// [`chars`](Self::chars) iterator or the respective byte iterator
348+
/// (via `as_bytes().iter()`).
282349
pub fn load_bytes(&self) -> [u8; N] {
283350
self.as_bytes().load()
284351
}
@@ -290,8 +357,9 @@ impl<const N: usize> PmString<N> {
290357

291358
/// Lazily iterate over the `char`s of the string.
292359
///
293-
/// This function is analog to [`ProgMem::iter`], except it is over the
294-
/// `char`s of this string.
360+
/// This function is analog to [`ProgMem::iter`], except it performs UTF-8
361+
/// parsing and returns the `char`s of this string, thus it is more similar
362+
/// to [`str::chars`].
295363
pub fn chars(&self) -> PmChars<N> {
296364
PmChars::new(self)
297365
}

src/wrapper.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
//! Best-effort safe wrapper for progmem.
2+
//!
3+
//! This module offers the [`ProgMem`] struct that wraps a value in progmem,
4+
//! and only gives access to this value via methods that first load the value
5+
//! into the normal data memory domain.
6+
//! This is also the reason why the value must be `Copy` and is always returned
7+
//! by-value instead of by-reference (since the value is not in the data memory
8+
//! where it could be referenced).
9+
//!
10+
//! Since the `ProgMem` struct loads the value using special instructions,
11+
//! it must actually be in progmem, otherwise it would be
12+
//! **undefined behavior**, therefore, its constructor is `unsafe` where the
13+
//! caller must guarantee that it is indeed stored in progmem.
14+
//!
15+
//! As further convenience, the [`progmem`] macro is offered that will create
16+
//! a `static` in progmem and wrap the given value in the [`ProgMem`] struct
17+
//! for you.
18+
19+
120
use core::convert::TryInto;
221

322
use crate::raw::read_value;
@@ -142,6 +161,9 @@ impl<T: Copy, const N: usize> ProgMem<[T; N]> {
142161
/// However, this is currently just a implementation limitation, which may
143162
/// be lifted in the future.
144163
///
164+
/// Notice, that here `T` is the type of the elements not the entire array
165+
/// as it would be with [`load`](Self::load).
166+
///
145167
pub fn load_at(&self, idx: usize) -> T {
146168
// Just take a reference to the selected element.
147169
// Notice that this will execute a bounds check.
@@ -213,6 +235,9 @@ impl<T: Copy, const N: usize> ProgMem<[T; N]> {
213235
/// However, this is currently just a implementation limitation, which may
214236
/// be lifted in the future.
215237
///
238+
/// Notice, that here `T` is the type of the elements not the entire array
239+
/// as it would be with [`load`](Self::load).
240+
///
216241
pub fn iter(&self) -> PmIter<T, N> {
217242
PmIter::new(self)
218243
}

0 commit comments

Comments
 (0)