# `prettier-bytes`
[](https://crates.io/crates/prettier-bytes)
[](https://docs.rs/prettier-bytes)
[](https://spdx.org/licenses/MIT)
[](#)
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
```rust
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)
```rust
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)
```rust
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:
```rust
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](https://spdx.org/licenses/MIT).