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
DebugandDisplay-like traitscore::write!-like macro- A generic
Formatter<'_, impl uWrite>instead of a singlecore::Formatter; theuWritetrait has an associated error type so each writer can choose its error type. For example, the implementation forstd::StringusesInfallibleas 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
- Derive macros
Macros§
- uwrite
- Write formatted data into a buffer
- uwriteln
- Write formatted data into a buffer, with a newline appended
Structs§
- Debug
List - A struct to help with
uDebugimplementations. - Debug
Map - A struct to help with
uDebugimplementations. - Debug
Struct - A struct to help with
uDebugimplementations. - Debug
Tuple - A struct to help with
uDebugimplementations. - Formatter
- Configuration for formatting