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//! - Hexadecimal formatting (`{:x}`) of integer primitives (e.g. `i32`) -- currently cannot be extended to other types
23//!
24//! [`Debug`]: trait.uDebug.html
25//! [`Display`]: trait.uDisplay.html
26//! [uwrite]: index.html#reexports
27//! [formatter]: struct.Formatter.html
28//! [`uWrite`]: trait.uWrite.html
29//! [`Infallible`]: https://doc.rust-lang.org/core/convert/enum.Infallible.html
30//! [debug_struct]: struct.Formatter.html#method.debug_struct
31//! [derive]: derive/index.html
32//!
33//! # Non-features
34//!
35//! These are out of scope
36//!
37//! - Padding, alignment and other formatting options
38//! - Formatting floating point numbers
39//!
40//! # Examples
41//!
42//! - `uwrite!` / `uwriteln!`
43//!
44//! ```
45//! use ufmt::{derive::uDebug, uwrite};
46//!
47//! #[derive(uDebug)]
48//! struct Pair { x: u32, y: u32 }
49//!
50//! let mut s = String::new();
51//! let pair = Pair { x: 1, y: 2 };
52//! uwrite!(s, "{:?}", pair).unwrap();
53//! assert_eq!(s, "Pair { x: 1, y: 2 }");
54//! ```
55//!
56//! - Hexadecimal formatting
57//!
58//! Lowercase (`{:x}`), uppercase (`{:X}`), `0x`-prefix (`{:#x}`) and padding (`{:02x}`) are
59//! supported on primitive integer types.
60//!
61//! ```
62//! use ufmt::uwrite;
63//!
64//! let mut s = String::new();
65//! uwrite!(s, "{:#06x}", 0x42);
66//! assert_eq!(s, "0x0042");
67//! ```
68//!
69//! - implementing `uWrite`
70//!
71//! When implementing the `uWrite` trait you should prefer the `ufmt_write::uWrite` crate over the
72//! `ufmt::uWrite` crate for better forward compatibility.
73//!
74//! ```
75//! use core::convert::Infallible;
76//!
77//! use ufmt_write::uWrite;
78//!
79//! struct MyWriter;
80//!
81//! impl uWrite for MyWriter {
82//!     type Error = Infallible;
83//!
84//!     fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
85//!         // ..
86//!         Ok(())
87//!     }
88//! }
89//! ```
90//!
91//! - writing a `macro_rules!` macro that uses `uwrite!` (or `uwriteln!`).
92//!
93//! ```
94//! // like `std::format!` it returns a `std::String` but uses `uwrite!` instead of `write!`
95//! macro_rules! uformat {
96//!     // IMPORTANT use `tt` fragments instead of `expr` fragments (i.e. `$($exprs:expr),*`)
97//!     ($($tt:tt)*) => {{
98//!         let mut s = String::new();
99//!         match ufmt::uwrite!(&mut s, $($tt)*) {
100//!             Ok(_) => Ok(s),
101//!             Err(e) => Err(e),
102//!         }
103//!     }}
104//! }
105//! ```
106//!
107//! # Benchmarks
108//!
109//! The benchmarks ran on a ARM Cortex-M3 chip (`thumbv7m-none-eabi`).
110//!
111//! The benchmarks were compiled with `nightly-2019-05-01`, `-C opt-level=3`, `lto = true`,
112//! `codegen-units = 1`.
113//!
114//! In all benchmarks `x = i32::MIN` and `y = i32::MIN` plus some obfuscation was applied to
115//! prevent const-propagation of the `*write!` arguments.
116//!
117//! The unit of time is one core clock cycle: 125 ns (8 MHz)
118//!
119//! The `.text` and `.rodata` columns indicate the delta (in bytes) when commenting out the
120//! `*write!` statement.
121//!
122//! |Code                                      |Time|%        |`.text`|%        |`.rodata`|%       |
123//! |------------------------------------------|----|---------|-------|---------|---------|--------|
124//! |`write!("Hello, world!")`                 |154 |~        |1906   |~        |248      |~       |
125//! |`uwrite!("Hello, world!")`                |20  |**13.0%**|34     |**1.8%** |16       |**6.5%**|
126//! |`write!(w, "{}", 0i32)`                   |256 |~        |1958   |~        |232      |~       |
127//! |`uwrite!(w, "{}", 0i32)`                  |37  |**14.5%**|288    |**14.7%**|0        |**0%**  |
128//! |`write!(w, "{}", x)`                      |381 |~        |
129//! |`uwrite!(w, "{}", x)`                     |295 |77.4%    |
130//! |`write!(w, "{:?}", Pair { x: 0, y: 0 })`  |996 |~        |4704   |~        |312      |~       |
131//! |`uwrite!(w, "{:?}", Pair { x: 0, y: 0 })` |254 |**25.5%**|752    |**16.0%**|24       |**7.7%**|
132//! |`write!(w, "{:?}", Pair { x, y })`        |1264|~        |
133//! |`uwrite!(w, "{:?}", Pair { x, y })`       |776 |61.4%    |
134//! |`write!(w, "{:#?}", Pair { x: 0, y: 0 })` |2853|~        |4710   |~        |348      |~       |
135//! |`uwrite!(w, "{:#?}", Pair { x: 0, y: 0 })`|301 |**10.6%**|754    |**16.0%**|24       |**6.9%**|
136//! |`write!(w, "{:#?}", Pair { x, y })`       |3693|~        |
137//! |`uwrite!(w, "{:#?}", Pair { x, y })`      |823 |**22.3%**|
138//!
139//!
140//! Benchmark program:
141//!
142//! ``` ignore
143//! static X: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
144//! static Y: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
145//!
146//! #[exception]
147//! fn PendSV() {
148//!    // read DWT.CYCCNT here
149//!
150//!    let x = X.load(Ordering::Relaxed);
151//!    let y = Y.load(Ordering::Relaxed);
152//!
153//!    let p = Pair { x, y };
154//!
155//!    uwrite!(&mut W, "{:#?}", p).ok();
156//!
157//!    // write!(&mut W, "{:#?}", p).ok();
158//!
159//!    asm::bkpt(); // read DWT.CYCCNT here
160//! }
161//! ```
162//!
163//! Writer used in the benchmarks:
164//!
165//! ```
166//! use core::{convert::Infallible, fmt, ptr};
167//!
168//! use ufmt::uWrite;
169//!
170//! struct W;
171//!
172//! impl uWrite for W {
173//!     type Error = Infallible;
174//!
175//!     fn write_str(&mut self, s: &str) -> Result<(), Infallible> {
176//!         s.as_bytes()
177//!             .iter()
178//!             .for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
179//!
180//!         Ok(())
181//!     }
182//! }
183//!
184//! impl fmt::Write for W {
185//!     fn write_str(&mut self, s: &str) -> fmt::Result {
186//!         s.as_bytes()
187//!             .iter()
188//!             .for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
189//!
190//!         Ok(())
191//!     }
192//! }
193//! ```
194//!
195//! # Minimum Supported Rust Version (MSRV)
196//!
197//! This crate does *not* have a Minimum Supported Rust Version (MSRV) and may make use of language
198//! features and API in the standard library available in the latest stable Rust version.
199//!
200//! In other words, changes in the Rust version requirement of this crate are not considered semver
201//! breaking change and may occur in patch version release.
202
203#![cfg_attr(not(feature = "std"), no_std)]
204#![deny(missing_docs)]
205#![deny(warnings)]
206
207// this lets us use `uwrite!` in the test suite
208#[allow(unused_extern_crates)]
209#[cfg(test)]
210extern crate self as ufmt;
211
212use core::str;
213
214pub use ufmt_write::uWrite;
215
216/// Write formatted data into a buffer
217///
218/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be
219/// formatted according to the specified format string and the result will be passed to the writer.
220/// The writer must have type `[&mut] impl uWrite` or `[&mut] ufmt::Formatter<'_, impl uWrite>`. The
221/// macro returns the associated `Error` type of the `uWrite`-r.
222///
223/// The syntax is similar to [`core::write!`] but only a handful of argument types are accepted:
224///
225/// [`core::write!`]: https://doc.rust-lang.org/core/macro.write.html
226///
227/// - `{}` - `uDisplay`
228/// - `{:?}` - `uDebug`
229/// - `{:#?}` - "pretty" `uDebug`
230///
231/// Named parameters and "specified" positional parameters (`{0}`) are not supported.
232///
233/// `{{` and `}}` can be used to escape braces.
234pub use ufmt_macros::uwrite;
235
236/// Write formatted data into a buffer, with a newline appended
237///
238/// See [`uwrite!`](macro.uwrite.html) for more details
239pub use ufmt_macros::uwriteln;
240
241pub use crate::helpers::{DebugList, DebugMap, DebugStruct, DebugTuple};
242
243mod helpers;
244mod impls;
245/// Derive macros
246pub mod derive {
247    pub use ufmt_macros::uDebug;
248}
249
250/// Just like `core::fmt::Debug`
251#[allow(non_camel_case_types)]
252pub trait uDebug {
253    /// Formats the value using the given formatter
254    fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
255    where
256        W: uWrite + ?Sized;
257}
258
259/// Just like `core::fmt::Display`
260#[allow(non_camel_case_types)]
261pub trait uDisplay {
262    /// Formats the value using the given formatter
263    fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
264    where
265        W: uWrite + ?Sized;
266}
267
268/// HEADS UP this is currently an implementation detail and not subject to semver guarantees.
269/// do NOT use this outside the `ufmt` crate
270// options for formatting hexadecimal numbers
271#[doc(hidden)]
272pub struct HexOptions {
273    /// when we need to use digits a-f, should they be upper case instead?
274    pub upper_case: bool,
275    /// when we are padding to a target length, what character should we pad using?
276    pub pad_char: u8,
277    /// when we are padding to a target length, how long should our string be?
278    pub pad_length: usize,
279    /// should we include a 0x prefix? (also controlled by upper_case)
280    pub ox_prefix: bool,
281}
282
283impl HexOptions {
284    /// applies the various padding/prefix options while writing the `payload` string
285    pub fn with_stuff<W: uWrite + ?Sized>(
286        &self,
287        fmt: &mut Formatter<'_, W>,
288        payload: &str,
289    ) -> Result<(), <W as uWrite>::Error> {
290        let pad_before = self.ox_prefix && self.pad_char == b' ';
291
292        let pad = self.pad_length as isize
293            - (if self.ox_prefix { 2 } else { 0 } + payload.len()) as isize;
294
295        let do_pad = |fmt: &mut Formatter<'_, W>, pad: isize| -> Result<(), <W as uWrite>::Error> {
296            if pad > 0 {
297                for _ in 0..pad {
298                    // miri considers the `write_char` defined in `ufmt-write` v0.1.0 unsound
299                    // to workaround the issue we use `write_str` instead of `write_char`
300                    fmt.write_str(unsafe { str::from_utf8_unchecked(&[self.pad_char]) })?;
301                }
302            }
303            Ok(())
304        };
305
306        let do_prefix = |fmt: &mut Formatter<'_, W>,
307                         go: bool,
308                         upper_case: bool|
309         -> Result<(), <W as uWrite>::Error> {
310            if go {
311                fmt.write_str(if upper_case { "0X" } else { "0x" })
312            } else {
313                Ok(())
314            }
315        };
316        if pad_before {
317            do_pad(fmt, pad)?;
318            do_prefix(fmt, self.ox_prefix, self.upper_case)?;
319        } else {
320            do_prefix(fmt, self.ox_prefix, self.upper_case)?;
321            do_pad(fmt, pad)?;
322        }
323
324        fmt.write_str(payload)
325    }
326}
327
328/// HEADS UP this is currently an implementation detail and not subject to semver guarantees.
329/// do NOT use this outside the `ufmt` crate
330// just like std::fmt::LowerHex
331#[doc(hidden)]
332#[allow(non_camel_case_types)]
333pub trait uDisplayHex {
334    /// Formats the value using the given formatter
335    fn fmt_hex<W>(&self, _: &mut Formatter<'_, W>, options: HexOptions) -> Result<(), W::Error>
336    where
337        W: uWrite + ?Sized;
338}
339
340/// Configuration for formatting
341#[allow(non_camel_case_types)]
342pub struct Formatter<'w, W>
343where
344    W: uWrite + ?Sized,
345{
346    indentation: u8,
347    pretty: bool,
348    writer: &'w mut W,
349}
350
351impl<'w, W> Formatter<'w, W>
352where
353    W: uWrite + ?Sized,
354{
355    /// Creates a formatter from the given writer
356    pub fn new(writer: &'w mut W) -> Self {
357        Self {
358            indentation: 0,
359            pretty: false,
360            writer,
361        }
362    }
363
364    /// Execute the closure with pretty-printing enabled
365    pub fn pretty(
366        &mut self,
367        f: impl FnOnce(&mut Self) -> Result<(), W::Error>,
368    ) -> Result<(), W::Error> {
369        let pretty = self.pretty;
370        self.pretty = true;
371        f(self)?;
372        self.pretty = pretty;
373        Ok(())
374    }
375
376    /// Writes a character to the underlying buffer contained within this formatter.
377    pub fn write_char(&mut self, c: char) -> Result<(), W::Error> {
378        self.writer.write_char(c)
379    }
380
381    /// Writes a string slice to the underlying buffer contained within this formatter.
382    pub fn write_str(&mut self, s: &str) -> Result<(), W::Error> {
383        self.writer.write_str(s)
384    }
385
386    /// Write whitespace according to the current `self.indentation`
387    fn indent(&mut self) -> Result<(), W::Error> {
388        for _ in 0..self.indentation {
389            self.write_str("    ")?;
390        }
391
392        Ok(())
393    }
394}
395
396// Implementation detail of the `uwrite*!` macros
397#[doc(hidden)]
398pub trait UnstableDoAsFormatter {
399    type Writer: uWrite + ?Sized;
400
401    fn do_as_formatter(
402        &mut self,
403        f: impl FnOnce(&mut Formatter<'_, Self::Writer>) -> Result<(), <Self::Writer as uWrite>::Error>,
404    ) -> Result<(), <Self::Writer as uWrite>::Error>;
405}
406
407impl<W> UnstableDoAsFormatter for W
408where
409    W: uWrite + ?Sized,
410{
411    type Writer = W;
412
413    fn do_as_formatter(
414        &mut self,
415        f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>,
416    ) -> Result<(), W::Error> {
417        f(&mut Formatter::new(self))
418    }
419}
420
421impl<W> UnstableDoAsFormatter for Formatter<'_, W>
422where
423    W: uWrite + ?Sized,
424{
425    type Writer = W;
426
427    fn do_as_formatter(
428        &mut self,
429        f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>,
430    ) -> Result<(), W::Error> {
431        f(self)
432    }
433}