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}