aya_ufmt/lib.rs
1//! `μfmt`, a (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt`
2//!
3//! # Design goals
4//!
5//! From highest priority to lowest priority
6//!
7//! - Optimized for binary size and speed (rather than for compilation time)
8//! - No dynamic dispatch in generated code
9//! - No panicking branches in generated code, when optimized
10//! - No recursion where possible
11//!
12//! # Features
13//!
14//! - [`Debug`] and [`Display`]-like traits
15//! - [`core::write!`][uwrite]-like macro
16//! - A generic [`Formatter<'_, impl uWrite>`][formatter] instead of a single `core::Formatter`; the
17//! [`uWrite`] trait has an associated error type so each writer can choose its error type. For
18//! example, the implementation for `std::String` uses [`Infallible`] as its error type.
19//! - [`core::fmt::Formatter::debug_struct`][debug_struct]-like API
20//! - [`#[derive(uDebug)]`][derive]
21//! - Pretty formatting (`{:#?}`) for `uDebug`
22//!
23//! [`Debug`]: trait.uDebug.html
24//! [`Display`]: trait.uDisplay.html
25//! [uwrite]: index.html#reexports
26//! [formatter]: struct.Formatter.html
27//! [`uWrite`]: trait.uWrite.html
28//! [`Infallible`]: https://doc.rust-lang.org/core/convert/enum.Infallible.html
29//! [debug_struct]: struct.Formatter.html#method.debug_struct
30//! [derive]: derive/index.html
31//!
32//! # Non-features
33//!
34//! These are out of scope
35//!
36//! - Padding, alignment and other formatting options
37//! - Formatting floating point numbers
38//!
39//! # Examples
40//!
41//! - `uwrite!` / `uwriteln!`
42//!
43//! ```
44//! use ufmt::{derive::uDebug, uwrite};
45//!
46//! #[derive(uDebug)]
47//! struct Pair { x: u32, y: u32 }
48//!
49//! let mut s = String::new();
50//! let pair = Pair { x: 1, y: 2 };
51//! uwrite!(s, "{:?}", pair).unwrap();
52//! assert_eq!(s, "Pair { x: 1, y: 2 }");
53//! ```
54//!
55//! - implementing `uWrite`
56//!
57//! When implementing the `uWrite` trait you should prefer the `ufmt_write::uWrite` crate over the
58//! `ufmt::uWrite` crate for better forward compatibility.
59//!
60//! ```
61//! use core::convert::Infallible;
62//!
63//! use ufmt_write::uWrite;
64//!
65//! struct MyWriter;
66//!
67//! impl uWrite for MyWriter {
68//! type Error = Infallible;
69//!
70//! fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
71//! // ..
72//! Ok(())
73//! }
74//! }
75//! ```
76//!
77//! - writing a `macro_rules!` macro that uses `uwrite!` (or `uwriteln!`).
78//!
79//! Both `ufmt` macros are implemented using [`proc-macro-hack`]; care is needed to avoid running
80//! into the compiler bug [#43081](https://github.com/rust-lang/rust/issues/43081). See also
81//! [dtolnay/proc-macro-hack#46][pmh-46].
82//!
83//! [`proc-macro-hack`]: https://github.com/dtolnay/proc-macro-hack
84//! [pmh-46]: https://github.com/dtolnay/proc-macro-hack/issues/46
85//!
86//! ```
87//! // like `std::format!` it returns a `std::String` but uses `uwrite!` instead of `write!`
88//! macro_rules! uformat {
89//! // IMPORTANT use `tt` fragments instead of `expr` fragments (i.e. `$($exprs:expr),*`)
90//! ($($tt:tt)*) => {{
91//! let mut s = String::new();
92//! match ufmt::uwrite!(&mut s, $($tt)*) {
93//! Ok(_) => Ok(s),
94//! Err(e) => Err(e),
95//! }
96//! }}
97//! }
98//! ```
99//!
100//! # Benchmarks
101//!
102//! The benchmarks ran on a ARM Cortex-M3 chip (`thumbv7m-none-eabi`).
103//!
104//! The benchmarks were compiled with `nightly-2019-05-01`, `-C opt-level=3`, `lto = true`,
105//! `codegen-units = 1`.
106//!
107//! In all benchmarks `x = i32::MIN` and `y = i32::MIN` plus some obfuscation was applied to
108//! prevent const-propagation of the `*write!` arguments.
109//!
110//! The unit of time is one core clock cycle: 125 ns (8 MHz)
111//!
112//! The `.text` and `.rodata` columns indicate the delta (in bytes) when commenting out the
113//! `*write!` statement.
114//!
115//! |Code |Time|% |`.text`|% |`.rodata`|% |
116//! |------------------------------------------|----|---------|-------|---------|---------|--------|
117//! |`write!("Hello, world!")` |154 |~ |1906 |~ |248 |~ |
118//! |`uwrite!("Hello, world!")` |20 |**13.0%**|34 |**1.8%** |16 |**6.5%**|
119//! |`write!(w, "{}", 0i32)` |256 |~ |1958 |~ |232 |~ |
120//! |`uwrite!(w, "{}", 0i32)` |37 |**14.5%**|288 |**14.7%**|0 |**0%** |
121//! |`write!(w, "{}", x)` |381 |~ |
122//! |`uwrite!(w, "{}", x)` |295 |77.4% |
123//! |`write!(w, "{:?}", Pair { x: 0, y: 0 })` |996 |~ |4704 |~ |312 |~ |
124//! |`uwrite!(w, "{:?}", Pair { x: 0, y: 0 })` |254 |**25.5%**|752 |**16.0%**|24 |**7.7%**|
125//! |`write!(w, "{:?}", Pair { x, y })` |1264|~ |
126//! |`uwrite!(w, "{:?}", Pair { x, y })` |776 |61.4% |
127//! |`write!(w, "{:#?}", Pair { x: 0, y: 0 })` |2853|~ |4710 |~ |348 |~ |
128//! |`uwrite!(w, "{:#?}", Pair { x: 0, y: 0 })`|301 |**10.6%**|754 |**16.0%**|24 |**6.9%**|
129//! |`write!(w, "{:#?}", Pair { x, y })` |3693|~ |
130//! |`uwrite!(w, "{:#?}", Pair { x, y })` |823 |**22.3%**|
131//!
132//!
133//! Benchmark program:
134//!
135//! ``` ignore
136//! static X: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
137//! static Y: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
138//!
139//! #[exception]
140//! fn PendSV() {
141//! // read DWT.CYCCNT here
142//!
143//! let x = X.load(Ordering::Relaxed);
144//! let y = Y.load(Ordering::Relaxed);
145//!
146//! let p = Pair { x, y };
147//!
148//! uwrite!(&mut W, "{:#?}", p).ok();
149//!
150//! // write!(&mut W, "{:#?}", p).ok();
151//!
152//! asm::bkpt(); // read DWT.CYCCNT here
153//! }
154//! ```
155//!
156//! Writer used in the benchmarks:
157//!
158//! ```
159//! use core::{convert::Infallible, fmt, ptr};
160//!
161//! use ufmt::uWrite;
162//!
163//! struct W;
164//!
165//! impl uWrite for W {
166//! type Error = Infallible;
167//!
168//! fn write_str(&mut self, s: &str) -> Result<(), Infallible> {
169//! s.as_bytes()
170//! .iter()
171//! .for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
172//!
173//! Ok(())
174//! }
175//! }
176//!
177//! impl fmt::Write for W {
178//! fn write_str(&mut self, s: &str) -> fmt::Result {
179//! s.as_bytes()
180//! .iter()
181//! .for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
182//!
183//! Ok(())
184//! }
185//! }
186//! ```
187//!
188//! # Minimum Supported Rust Version (MSRV)
189//!
190//! This crate is guaranteed to compile on stable Rust 1.34 and up. It *might* compile on older
191//! versions but that may change in any new patch release.
192
193#![cfg_attr(not(feature = "std"), no_std)]
194#![deny(missing_docs)]
195#![deny(rust_2018_compatibility)]
196#![deny(rust_2018_idioms)]
197
198// this lets us use `uwrite!` in the test suite
199#[allow(unused_extern_crates)]
200#[cfg(test)]
201extern crate self as ufmt;
202
203use core::str;
204
205use proc_macro_hack::proc_macro_hack;
206pub use ufmt_write::uWrite;
207
208/// Write formatted data into a buffer
209///
210/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be
211/// formatted according to the specified format string and the result will be passed to the writer.
212/// The writer must have type `[&mut] impl uWrite` or `[&mut] ufmt::Formatter<'_, impl uWrite>`. The
213/// macro returns the associated `Error` type of the `uWrite`-r.
214///
215/// The syntax is similar to [`core::write!`] but only a handful of argument types are accepted:
216///
217/// [`core::write!`]: https://doc.rust-lang.org/core/macro.write.html
218///
219/// - `{}` - `uDisplay`
220/// - `{:?}` - `uDebug`
221/// - `{:#?}` - "pretty" `uDebug`
222///
223/// Named parameters and "specified" positional parameters (`{0}`) are not supported.
224///
225/// `{{` and `}}` can be used to escape braces.
226#[proc_macro_hack]
227pub use ufmt_macros::uwrite;
228
229/// Write formatted data into a buffer, with a newline appended
230///
231/// See [`uwrite!`](macro.uwrite.html) for more details
232#[proc_macro_hack]
233pub use ufmt_macros::uwriteln;
234
235pub use crate::helpers::{DebugList, DebugMap, DebugStruct, DebugTuple};
236
237#[macro_use]
238mod macros;
239
240mod helpers;
241mod impls;
242/// Derive macros
243pub mod derive {
244 pub use ufmt_macros::uDebug;
245}
246
247#[allow(deprecated)]
248unsafe fn uninitialized<T>() -> T {
249 core::mem::uninitialized()
250}
251
252/// Just like `core::fmt::Debug`
253#[allow(non_camel_case_types)]
254pub trait uDebug {
255 /// Formats the value using the given formatter
256 fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
257 where
258 W: uWrite + ?Sized;
259}
260
261/// Just like `core::fmt::Display`
262#[allow(non_camel_case_types)]
263pub trait uDisplay {
264 /// Formats the value using the given formatter
265 fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
266 where
267 W: uWrite + ?Sized;
268}
269
270/// Configuration for formatting
271#[allow(non_camel_case_types)]
272pub struct Formatter<'w, W>
273where
274 W: uWrite + ?Sized,
275{
276 indentation: u8,
277 pretty: bool,
278 writer: &'w mut W,
279}
280
281impl<'w, W> Formatter<'w, W>
282where
283 W: uWrite + ?Sized,
284{
285 /// Creates a formatter from the given writer
286 pub fn new(writer: &'w mut W) -> Self {
287 Self {
288 indentation: 0,
289 pretty: false,
290 writer,
291 }
292 }
293
294 /// Execute the closure with pretty-printing enabled
295 pub fn pretty(
296 &mut self,
297 f: impl FnOnce(&mut Self) -> Result<(), W::Error>,
298 ) -> Result<(), W::Error> {
299 let pretty = self.pretty;
300 self.pretty = true;
301 f(self)?;
302 self.pretty = pretty;
303 Ok(())
304 }
305
306 /// Writes a character to the underlying buffer contained within this formatter.
307 pub fn write_char(&mut self, c: char) -> Result<(), W::Error> {
308 self.writer.write_char(c)
309 }
310
311 /// Writes a string slice to the underlying buffer contained within this formatter.
312 pub fn write_str(&mut self, s: &str) -> Result<(), W::Error> {
313 self.writer.write_str(s)
314 }
315
316 /// Write whitespace according to the current `self.indentation`
317 fn indent(&mut self) -> Result<(), W::Error> {
318 for _ in 0..self.indentation {
319 self.write_str(" ")?;
320 }
321
322 Ok(())
323 }
324}
325
326// Implementation detail of the `uwrite*!` macros
327#[doc(hidden)]
328pub trait UnstableDoAsFormatter {
329 type Writer: uWrite + ?Sized;
330
331 fn do_as_formatter(
332 &mut self,
333 f: impl FnOnce(&mut Formatter<'_, Self::Writer>) -> Result<(), <Self::Writer as uWrite>::Error>,
334 ) -> Result<(), <Self::Writer as uWrite>::Error>;
335}
336
337impl<W> UnstableDoAsFormatter for W
338where
339 W: uWrite + ?Sized,
340{
341 type Writer = W;
342
343 fn do_as_formatter(
344 &mut self,
345 f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>,
346 ) -> Result<(), W::Error> {
347 f(&mut Formatter::new(self))
348 }
349}
350
351impl<W> UnstableDoAsFormatter for Formatter<'_, W>
352where
353 W: uWrite + ?Sized,
354{
355 type Writer = W;
356
357 fn do_as_formatter(
358 &mut self,
359 f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>,
360 ) -> Result<(), W::Error> {
361 f(self)
362 }
363}