prettier-bytes 0.2.1

A blazingly fast and safe, zero-allocation, `no_std`-compatible byte formatter.
Documentation
  • Coverage
  • 100%
    16 out of 16 items documented3 out of 12 items with examples
  • Size
  • Source code size: 30.53 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.89 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 38s Average build duration of successful builds.
  • all releases: 43s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • xangelix/prettier-bytes
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • xangelix

prettier-bytes

Crates.io Documentation License no_std

A blazingly fast and safe, zero-allocation, no_std-compatible byte formatter.

prettier-bytes provides the API to format raw byte sizes into human-readable strings (e.g., "1.50 MB", "2.34 GiB") without using heap allocations, floating-point math, or dynamic CPU division. It is designed for maximum throughput in microcontrollers, OS kernels, and high-performance CLIs.

✨ Features

  • Zero Allocation: Operates entirely on a 16-byte stack array. It does not use the alloc crate.
  • Mathematically Panic-Free: Built under a paranoid #![forbid(...)] linting regime. Zero slice indexing, zero unwrap(), zero expect(). The compiler is mathematically guaranteed to never insert bounds-check panic branches.
  • Hardware Optimized: Bypasses incredibly slow 64-bit dynamic hardware division. It relies purely on 1-cycle bitwise shifts for Binary parsing, and compile-time reciprocal multiplication for SI parsing.
  • 100% Safe Rust: Forbids unsafe code entirely.
  • no_std Native: Works out of the box in bare-metal environments.

🚀 Quick Start

use prettier_bytes::{ByteFormatter, Standard, Unit};

fn main() {
    // Configure the formatter once
    let formatter = ByteFormatter::new()
        .standard(Standard::Binary)
        .unit(Unit::Bytes)
        .space(true);

    // Format your value!
    let size = formatter.format(1048576);

    // Implements `fmt::Display` for zero-friction printing
    println!("File size: {size}"); // Outputs: "1.00 MiB"
}

🛠 Usage & API

The ByteFormatter uses a builder pattern to configure your output. Once configured, you can call .format(value) to calculate the size and store it inside a tiny [u8; 16] buffer. You have full control over the standard, units, and spacing.

Standards (SI vs Binary)

use prettier_bytes::{ByteFormatter, Standard};

let val = 2_500_000;

// Base 1000 (SI Standard) -> kB, MB, GB...
let si = ByteFormatter::new()
    .standard(Standard::SI)
    .format(val);

assert_eq!(si.as_str().unwrap(), "2.50 MB");

// Base 1024 (IEC/Binary Standard) -> KiB, MiB, GiB...
let bin = ByteFormatter::new()
    .standard(Standard::Binary)
    .format(val);

assert_eq!(bin.as_str().unwrap(), "2.38 MiB");

Units (Bytes vs Bits)

use prettier_bytes::{ByteFormatter, Standard, Unit};

let val = 2_500_000;

// Capital 'B' for Bytes
let bytes = ByteFormatter::new()
    .standard(Standard::SI)
    .unit(Unit::Bytes)
    .format(val);

assert_eq!(bytes.as_str().unwrap(), "2.50 MB");

// Lowercase 'b' for Bits
let bits = ByteFormatter::new()
    .standard(Standard::SI)
    .unit(Unit::Bits)
    .format(val);

assert_eq!(bits.as_str().unwrap(), "2.50 Mb");

Zero-Cost Retrievals

If you are writing to a fast I/O stream, you don't even need to convert it to a string. You can extract the raw, formatted byte slice directly:

use prettier_bytes::{ByteFormatter, Standard, Unit};

let formatted = ByteFormatter::new()
    .standard(Standard::SI)
    .space(false)
    .format(1500);

// Instantly returns &[u8] for fast std::io::Write or embedded UART transmission
let raw_bytes = formatted.as_bytes();

assert_eq!(raw_bytes, b"1.50kB");

🏎 Under the Hood: Why is it so fast?

Most "human readable byte" formatting libraries follow this pattern:

  1. Figure out the magnitude (Kilo, Mega, Giga).
  2. Look up the divisor in an array.
  3. Divide the value by the divisor dynamically at runtime (val / divisor).
  4. Append to a String.

This is disastrous for performance. Array lookups risk bounds panics. Heap allocation (String) is slow. And worst of all, dynamic 64-bit division is one of the slowest operations a CPU can do, taking 15 to 40 clock cycles.

prettier-bytes flips this on its head:

  1. It uses purely static match statements to figure out the magnitude.
  2. For Binary (base 1024), it replaces division entirely with bitwise shifts (>>) and masking (&), executing in a single clock cycle.
  3. For SI (base 1000), it feeds hardcoded literals into a macro, allowing the LLVM compiler to replace division with lightning-fast "reciprocal multiplication".
  4. It iterates safely over a stack-allocated [u8; 16] to assemble the string.

The result is a completely flat, allocation-free assembly profile.

⚖️ License

This project is licensed under the MIT License.