Expand description
μfmt
, a (6-40x) smaller, (2-9x) faster and panic-free alternative to core::fmt
§Design goals
From highest priority to lowest priority
- Optimized for binary size and speed (rather than for compilation time)
- No dynamic dispatch in generated code
- No panicking branches in generated code, when optimized
- No recursion where possible
§Features
Debug
andDisplay
-like traitscore::write!
-like macro- A generic
Formatter<'_, impl uWrite>
instead of a singlecore::Formatter
; theuWrite
trait has an associated error type so each writer can choose its error type. For example, the implementation forstd::String
usesInfallible
as its error type. core::fmt::Formatter::debug_struct
-like API#[derive(uDebug)]
- Pretty formatting (
{:#?}
) foruDebug
- Hexadecimal formatting (
{:x}
) of integer primitives (e.g.i32
) – currently cannot be extended to other types
§Non-features
These are out of scope
- Padding, alignment and other formatting options
- Formatting floating point numbers
§Examples
uwrite!
/uwriteln!
use ufmt::{derive::uDebug, uwrite};
#[derive(uDebug)]
struct Pair { x: u32, y: u32 }
let mut s = String::new();
let pair = Pair { x: 1, y: 2 };
uwrite!(s, "{:?}", pair).unwrap();
assert_eq!(s, "Pair { x: 1, y: 2 }");
- Hexadecimal formatting
Lowercase ({:x}
), uppercase ({:X}
), 0x
-prefix ({:#x}
) and padding ({:02x}
) are
supported on primitive integer types.
use ufmt::uwrite;
let mut s = String::new();
uwrite!(s, "{:#06x}", 0x42);
assert_eq!(s, "0x0042");
- implementing
uWrite
When implementing the uWrite
trait you should prefer the ufmt_write::uWrite
crate over the
ufmt::uWrite
crate for better forward compatibility.
use core::convert::Infallible;
use ufmt_write::uWrite;
struct MyWriter;
impl uWrite for MyWriter {
type Error = Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
// ..
Ok(())
}
}
- writing a
macro_rules!
macro that usesuwrite!
(oruwriteln!
).
// like `std::format!` it returns a `std::String` but uses `uwrite!` instead of `write!`
macro_rules! uformat {
// IMPORTANT use `tt` fragments instead of `expr` fragments (i.e. `$($exprs:expr),*`)
($($tt:tt)*) => {{
let mut s = String::new();
match ufmt::uwrite!(&mut s, $($tt)*) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}}
}
§Benchmarks
The benchmarks ran on a ARM Cortex-M3 chip (thumbv7m-none-eabi
).
The benchmarks were compiled with nightly-2019-05-01
, -C opt-level=3
, lto = true
,
codegen-units = 1
.
In all benchmarks x = i32::MIN
and y = i32::MIN
plus some obfuscation was applied to
prevent const-propagation of the *write!
arguments.
The unit of time is one core clock cycle: 125 ns (8 MHz)
The .text
and .rodata
columns indicate the delta (in bytes) when commenting out the
*write!
statement.
Code | Time | % | .text | % | .rodata | % |
---|---|---|---|---|---|---|
write!("Hello, world!") | 154 | ~ | 1906 | ~ | 248 | ~ |
uwrite!("Hello, world!") | 20 | 13.0% | 34 | 1.8% | 16 | 6.5% |
write!(w, "{}", 0i32) | 256 | ~ | 1958 | ~ | 232 | ~ |
uwrite!(w, "{}", 0i32) | 37 | 14.5% | 288 | 14.7% | 0 | 0% |
write!(w, "{}", x) | 381 | ~ | ||||
uwrite!(w, "{}", x) | 295 | 77.4% | ||||
write!(w, "{:?}", Pair { x: 0, y: 0 }) | 996 | ~ | 4704 | ~ | 312 | ~ |
uwrite!(w, "{:?}", Pair { x: 0, y: 0 }) | 254 | 25.5% | 752 | 16.0% | 24 | 7.7% |
write!(w, "{:?}", Pair { x, y }) | 1264 | ~ | ||||
uwrite!(w, "{:?}", Pair { x, y }) | 776 | 61.4% | ||||
write!(w, "{:#?}", Pair { x: 0, y: 0 }) | 2853 | ~ | 4710 | ~ | 348 | ~ |
uwrite!(w, "{:#?}", Pair { x: 0, y: 0 }) | 301 | 10.6% | 754 | 16.0% | 24 | 6.9% |
write!(w, "{:#?}", Pair { x, y }) | 3693 | ~ | ||||
uwrite!(w, "{:#?}", Pair { x, y }) | 823 | 22.3% |
Benchmark program:
static X: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
static Y: AtomicI32 = AtomicI32::new(i32::MIN); // or `0`
#[exception]
fn PendSV() {
// read DWT.CYCCNT here
let x = X.load(Ordering::Relaxed);
let y = Y.load(Ordering::Relaxed);
let p = Pair { x, y };
uwrite!(&mut W, "{:#?}", p).ok();
// write!(&mut W, "{:#?}", p).ok();
asm::bkpt(); // read DWT.CYCCNT here
}
Writer used in the benchmarks:
use core::{convert::Infallible, fmt, ptr};
use ufmt::uWrite;
struct W;
impl uWrite for W {
type Error = Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Infallible> {
s.as_bytes()
.iter()
.for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
Ok(())
}
}
impl fmt::Write for W {
fn write_str(&mut self, s: &str) -> fmt::Result {
s.as_bytes()
.iter()
.for_each(|b| unsafe { drop(ptr::read_volatile(b)) });
Ok(())
}
}
§Minimum Supported Rust Version (MSRV)
This crate does not have a Minimum Supported Rust Version (MSRV) and may make use of language features and API in the standard library available in the latest stable Rust version.
In other words, changes in the Rust version requirement of this crate are not considered semver breaking change and may occur in patch version release.
Modules§
- Derive macros
Macros§
- Write formatted data into a buffer
- Write formatted data into a buffer, with a newline appended
Structs§
- A struct to help with
uDebug
implementations. - A struct to help with
uDebug
implementations. - A struct to help with
uDebug
implementations. - A struct to help with
uDebug
implementations. - Configuration for formatting
Traits§
- Just like
core::fmt::Debug
- Just like
core::fmt::Display
- A collection of methods that are required / used to format a message into a stream.