Skip to content

Commit b255980

Browse files
authored
Merge pull request #11 from Cryptjar/impl-unsized
Impl unsized
2 parents 3ab0093 + 97c7344 commit b255980

File tree

5 files changed

+406
-44
lines changed

5 files changed

+406
-44
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ default = ["lpm-asm-loop", "ufmt"]
3535
lpm-asm-loop = []
3636
# Enables some tweak to ease debugging, should not be use in production
3737
dev = []
38+
# Enables unsize utilities, such as wrapper coercing.
39+
# However, this requires additional nightly Rust features, which might be unstable.
40+
unsize = []
3841

3942
[dependencies]
4043
cfg-if = "1.0"
4144

45+
[dependencies.derivative]
46+
version = "2.2.0"
47+
features = [ "use_core" ]
48+
4249
[dependencies.ufmt]
4350
version = "0.1.0"
4451
optional = true

examples/uno-slices.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//
2+
// This file provides a example on how to work with slice in progmem (sort of)
3+
// on an Arduino Uno.
4+
//
5+
6+
7+
// Define no_std only for AVR
8+
#![cfg_attr(target_arch = "avr", no_std)]
9+
#![cfg_attr(target_arch = "avr", no_main)]
10+
11+
12+
use avr_progmem::progmem; // The macro
13+
use avr_progmem::wrapper::ProgMem;
14+
use cfg_if::cfg_if;
15+
#[cfg(target_arch = "avr")]
16+
use panic_halt as _; // halting panic implementation for AVR
17+
18+
// First, there are no slices in progmem. All data must be statically size,
19+
// such as an array:
20+
progmem! {
21+
// Just one array of size 3
22+
static progmem<const ARRAY_A_LEN: usize> ARRAY_A: [u8; ARRAY_A_LEN] = [1,2,3];
23+
24+
// Another array of size 5
25+
static progmem<const ARRAY_B_LEN: usize> ARRAY_B: [u8; ARRAY_B_LEN] = [1,2,3,4,5];
26+
27+
// Yet another array
28+
static progmem<const ARRAY_C_LEN: usize> ARRAY_C: [u8; ARRAY_C_LEN] = [42];
29+
}
30+
31+
// Include a fancy printer supporting Arduino Uno's USB-Serial output as well
32+
// as stdout on non-AVR targets.
33+
mod printer;
34+
use printer::Printer;
35+
36+
#[cfg_attr(target_arch = "avr", arduino_hal::entry)]
37+
fn main() -> ! {
38+
// Setup the output
39+
let mut printer = setup();
40+
41+
//
42+
// Working with slices.
43+
//
44+
// So, we actually only have statically sized arrays in progmem. However,
45+
// sometimes slices are nicer to work with, for instance if you want to put
46+
// them into a list or something, e.g. to iterate over them.
47+
//
48+
// There are basically to way to accomplish this, first just load those
49+
// arrays into RAM and coerce the standard Rust arrays into standard Rust
50+
// slices as usual. The drawback is the potential high RAM usage.
51+
//
52+
// In order to have the benefits of slices while the data is still in
53+
// progmem, we can also just coerce the ProgMems of arrays into ProgMems of
54+
// slices:
55+
56+
// Once we have slice wrappers, we can store them together e.g. in a array
57+
let list_of_slices: [ProgMem<[u8]>; 3] = {
58+
cfg_if! {
59+
if #[cfg(feature = "unsize")] {
60+
// Option 1: just coerce them to a slice wrapper
61+
// but this requires the crate feature "unsize"
62+
63+
[
64+
ARRAY_A,
65+
ARRAY_B,
66+
ARRAY_C,
67+
]
68+
} else {
69+
// Option 2: convert them explicitly via the "as_slice" method
70+
71+
[
72+
ARRAY_A.as_slice(),
73+
ARRAY_B.as_slice(),
74+
ARRAY_C.as_slice(),
75+
]
76+
}
77+
}
78+
};
79+
80+
// And for instance iterate through that list.
81+
for (i, slice) in list_of_slices.iter().enumerate() {
82+
// Here `slice` is a `ProgMem<[u8]>`, which has (among others) a `len` and a
83+
// `load_at` method.
84+
85+
ufmt::uwriteln!(
86+
&mut printer,
87+
"Element #{}, size: {}, first element: {}\r",
88+
i,
89+
slice.len(),
90+
slice.load_at(0)
91+
)
92+
.unwrap();
93+
94+
// We can also use a progmem-iterator that gives a ProgMem wrapper for
95+
// each element of that slice (without yet loading any of them).
96+
for elem_wrapper in slice.wrapper_iter() {
97+
// Fun fact, if that element happened to be another array, we would
98+
// also iterate its elements without loading it yet.
99+
// That allows to iterate multi-dimensional arrays and only ever
100+
// loading a single element into RAM.
101+
102+
// Only load a single element at a time
103+
let e = elem_wrapper.load();
104+
ufmt::uwrite!(&mut printer, "{}, ", e).unwrap();
105+
}
106+
printer.println("");
107+
}
108+
109+
// "end" the program
110+
finish(printer)
111+
}
112+
113+
//
114+
// Following are just some auxiliary functions to setup and finish up the Arduino
115+
//
116+
117+
118+
// Setup the serial UART as output at 9600 baud and print a "Hello from Arduino"
119+
fn setup() -> Printer {
120+
let mut printer = {
121+
#[cfg(target_arch = "avr")]
122+
{
123+
// Initialize the USB-Serial output on the Arduino Uno
124+
125+
let dp = arduino_hal::Peripherals::take().unwrap();
126+
let pins = arduino_hal::pins!(dp);
127+
let serial = arduino_hal::default_serial!(dp, pins, 9600);
128+
129+
Printer(serial)
130+
}
131+
#[cfg(not(target_arch = "avr"))]
132+
{
133+
// Just use stdout for non-AVR targets
134+
Printer
135+
}
136+
};
137+
138+
// Print some introduction text
139+
printer.println("Hello from Arduino!");
140+
printer.println("");
141+
printer.println("--------------------------");
142+
printer.println("");
143+
144+
// return the printer
145+
printer
146+
}
147+
148+
// Print a "DONE" and exit (or go into an infinite loop).
149+
fn finish(mut printer: Printer) -> ! {
150+
// Print some final lines
151+
printer.println("");
152+
printer.println("--------------------------");
153+
printer.println("");
154+
printer.println("DONE");
155+
156+
// It is very convenient to just exit on non-AVR platforms, otherwise users
157+
// might get the impression that the program hangs, whereas it already
158+
// succeeded.
159+
#[cfg(not(target_arch = "avr"))]
160+
std::process::exit(0);
161+
162+
// Otherwise, that is on AVR, just go into an infinite loop, because on AVR
163+
// we just can't exit!
164+
loop {
165+
// Done, just do nothing
166+
}
167+
}

src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
// As of now (mid 2022), inline assembly for AVR is still unstable.
66
#![feature(asm_experimental_arch)]
77
//
8+
// We to access the length of a slice pointer (for unsized `ProgMem`s)
9+
#![feature(slice_ptr_len)]
10+
//
11+
// Allow to implement `CoerceUnsized` on `ProgMem`
12+
#![cfg_attr(feature = "unsize", feature(coerce_unsized))]
13+
//
14+
// Needed for implementing `CoerceUnsized` on `ProgMem`
15+
#![cfg_attr(feature = "unsize", feature(unsize))]
16+
//
817
// Allows to document required crate features on items
918
#![cfg_attr(doc, feature(doc_auto_cfg))]
1019
//
@@ -95,7 +104,7 @@
95104
//! use core::ptr::addr_of;
96105
//!
97106
//! // This `static` must never be directly dereferenced/accessed!
98-
//! // So a `let data: u8 = P_BYTE;` is **undefined behavior**!!!
107+
//! // So a `let data: u8 = P_BYTE;` ⚠️ is **undefined behavior**!!!
99108
//! /// Static byte stored in progmem!
100109
//! #[link_section = ".progmem.data"]
101110
//! static P_BYTE: u8 = b'A';

src/string.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,16 @@ impl<const N: usize> ufmt::uDisplay for LoadedString<N> {
375375
/// assert_eq!("dai 大賢者 kenja", &*loaded)
376376
/// ```
377377
///
378-
#[non_exhaustive] // SAFETY: this struct must not be publicly constructible
378+
//
379+
//
380+
// SAFETY: this struct must not be publicly constructible
381+
#[non_exhaustive]
382+
//
383+
// Its just a pointer type, thus copy, clone & debug are fine (none of them
384+
// will access the progmem, that's what `Display` is for).
385+
#[derive(Copy, Clone, Debug)]
386+
// Also impl `uDebug` if enabled.
387+
#[cfg_attr(feature = "ufmt", derive(ufmt::derive::uDebug))]
379388
pub struct PmString<const N: usize> {
380389
/// The inner UTF-8 string as byte array in progmem.
381390
///

0 commit comments

Comments
 (0)