[][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 trait objects
  • No panicking branches when optimized
  • No recursion (if / where possible)

Features

Non-features

These are out of scope

  • Padding, alignment and other formatting options
  • Formatting floating point numbers

Examples

  • on nightly: uwrite! / uwriteln!
#![feature(proc_macro_hygiene)]

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 }");
  • on stable: Formatter

The uwrite! macro requires nightly. On stable you can directly use the Formatter API.

use ufmt::{derive::uDebug, uDebug, Formatter, uwrite};

#[derive(uDebug)]
struct Pair { x: u32, y: u32 }

let mut s = String::new();
let pair = Pair { x: 1, y: 2 };

// equivalent to `uwrite!("{:#?}", pair).unwrap()`
{
    let mut f = Formatter::new(&mut s);

    f.pretty(|f| uDebug::fmt(&pair, f))
}.unwrap();

let pretty = "\
Pair {
    x: 1,
    y: 2,
}";

assert_eq!(s, pretty);
  • on stable: 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(())
    }
}

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:

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)

Rust 1.34 for everything but the uwrite! macro which requires the unstable proc_macro_hygiene feature at call site and thus nightly. However, it's possible to use the stable Formatter API instead of uwrite!.

Re-exports

pub use ufmt_macros::uwrite;
pub use ufmt_macros::uwriteln;

Modules

derive

Derive macros

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.