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}