[][src]Crate ufmt

μ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

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 }");
  • 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 uses uwrite! (or uwriteln!).

Both ufmt macros are implemented using proc-macro-hack; care is needed to avoid running into the compiler bug #43081. See also dtolnay/proc-macro-hack#46.

// 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.

CodeTime%.text%.rodata%
write!("Hello, world!")154~1906~248~
uwrite!("Hello, world!")2013.0%341.8%166.5%
write!(w, "{}", 0i32)256~1958~232~
uwrite!(w, "{}", 0i32)3714.5%28814.7%00%
write!(w, "{}", x)381~
uwrite!(w, "{}", x)29577.4%
write!(w, "{:?}", Pair { x: 0, y: 0 })996~4704~312~
uwrite!(w, "{:?}", Pair { x: 0, y: 0 })25425.5%75216.0%247.7%
write!(w, "{:?}", Pair { x, y })1264~
uwrite!(w, "{:?}", Pair { x, y })77661.4%
write!(w, "{:#?}", Pair { x: 0, y: 0 })2853~4710~348~
uwrite!(w, "{:#?}", Pair { x: 0, y: 0 })30110.6%75416.0%246.9%
write!(w, "{:#?}", Pair { x, y })3693~
uwrite!(w, "{:#?}", Pair { x, y })82322.3%

Benchmark program:

This example is not tested
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 is guaranteed to compile on stable Rust 1.34 and up. It might compile on older versions but that may change in any new patch 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

DebugList

A struct to help with uDebug implementations.

DebugMap

A struct to help with uDebug implementations.

DebugStruct

A struct to help with uDebug implementations.

DebugTuple

A struct to help with uDebug implementations.

Formatter

Configuration for formatting

Traits

uDebug

Just like core::fmt::Debug

uDisplay

Just like core::fmt::Display

uWrite

A collection of methods that are required / used to format a message into a stream.