Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ This crate provides multiple semantic wrappers and utilities for byte size repre
* `#![no_std]`-capable, no heap allocation, and no runtime dependencies by default.
* `ByteSize<T>` wrappers over supported unsigned integer base types, with `BSize` as the `usize` alias and `BSize8`, `BSize16`, `BSize32`, and `BSize64` aliases for fixed-width base types.
* `FromStr` impl for `ByteSize`, allowing for parsing string size representations like "1.5 KiB" and "521 TB".
* `Display` impl for `ByteSize`, allowing for formatting byte sizes as human-readable strings in both binary (e.g., "1.5 MiB") and decimal (e.g., "1.5 MB") styles.
* Exact `Display` impl for `ByteSize`, rendering the underlying byte count in base bytes (e.g., "1572864 B").
* Configurable, approximate human-readable formatting in both binary (e.g., "1.5 MiB") and decimal (e.g., "1.6 MB") styles.
* Optional `serde` support for binary and human-readable format.
* Optional `nightly` support for a broader const-friendly API surface powered by nightly-only Rust features.

Expand Down
36 changes: 29 additions & 7 deletions bsize/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ use core::fmt::Write as _;
use crate::BaseByteSize;
use crate::ByteSize;

/// Create a [`Display`] instance for displaying a byte size.
/// Create a [`Display`] instance for approximate, human-readable formatting.
///
/// See [`Display`] for examples. Use [`Display::new`] when the byte count is already represented
/// as an `f64`.
/// The integer byte count is converted to `f64` before scaling and formatting. See [`Display`] for
/// the precision contract and examples. Use [`Display::new`] when the byte count is already
/// represented as an `f64`.
pub fn display(size: impl BaseByteSize) -> Display {
Display::new(size.to_f64())
}

impl<T: BaseByteSize> ByteSize<T> {
/// Returns a [`Display`] wrapper.
/// Returns a [`Display`] wrapper for approximate, human-readable formatting.
///
/// See [`Display`] for examples.
/// The underlying integer byte count is converted to `f64` before scaling and formatting. Use
/// this type's standard [`fmt::Display`] implementation when an exact base-byte representation
/// is required. See [`Display`] for the full precision contract and examples.
pub fn display(&self) -> Display {
Display::new(self.bytes().to_f64())
}
Expand All @@ -40,6 +43,22 @@ impl<T: BaseByteSize> ByteSize<T> {
/// You may create this wrapper with [`Display::new`], [`display`], or [`ByteSize::display`], then
/// pass custom [`DisplayOptions`] with [`Display::options`].
///
/// # Precision and round trips
///
/// This wrapper is intended for approximate presentation, not exact serialization. Values are
/// stored, scaled, and formatted as `f64`. Integer byte counts supplied through [`display`] or
/// [`ByteSize::display`] are converted to `f64` first, so sufficiently large integers may lose
/// precision. Automatic scale selection also observes the converted value and can therefore cross
/// a scale boundary when conversion rounds an integer upward.
///
/// Formatter precision controls only the number of displayed fractional digits; it cannot recover
/// precision lost during conversion to `f64`. Output from this wrapper is not guaranteed to parse
/// back to the original byte count. Use [`ByteSize`]'s standard [`fmt::Display`] implementation for
/// an exact, round-trippable base-byte representation.
///
/// Without an explicit formatter precision, values in the base scale use the default `f64`
/// representation and values with a unit prefix use one fractional digit.
///
/// # Examples
///
/// Display with the [`DisplayOptions::BINARY`] and [`DisplayOptions::DECIMAL`] presets.
Expand Down Expand Up @@ -312,10 +331,13 @@ impl Display {
self
}

/// Create a [`Display`] instance from a byte count.
/// Create a [`Display`] instance from an approximate byte count.
///
/// This constructor is useful when the byte count is already represented as an `f64`. For
/// supported integer byte counts, use [`display`] or [`ByteSize::display`].
/// supported integer byte counts, use [`display`] or [`ByteSize::display`]. The wrapper follows
/// `f64` precision semantics and is intended for presentation rather than exact serialization.
/// Positive infinity is accepted and uses the largest available unit prefix when the scale is
/// selected automatically.
///
/// # Examples
///
Expand Down
16 changes: 12 additions & 4 deletions bsize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
//! for fixed-width base types.
//! * `FromStr` impl for `ByteSize`, allowing for parsing string size representations like "1.5 KiB"
//! and "521 TB".
//! * [`Display`] impl for `ByteSize`, allowing for formatting byte sizes as human-readable strings
//! in both binary (e.g., "1.5 MiB") and decimal (e.g., "1.5 MB") styles.
//! * Exact [`core::fmt::Display`] impl for [`ByteSize`], rendering the underlying byte count in
//! base bytes (e.g., "1572864 B").
//! * Configurable, approximate human-readable formatting in both binary (e.g., "1.5 MiB") and
//! decimal (e.g., "1.6 MB") styles.
//! * Optional `serde` support for binary and human-readable format.
//! * Optional `nightly` support for a broader const-friendly API surface powered by nightly-only
//! Rust features.
Expand Down Expand Up @@ -66,14 +68,18 @@
//! assert_eq!(BSize64::mib(1).map(|bytes| bytes + 512 * 1024), size);
//! ```
//!
//! Display as human-readable string.
//! Format the exact byte count or an approximate human-readable string.
//!
//! ```
//! use bsize::BSize;
//! use bsize::DisplayBaseUnit;
//! use bsize::DisplayOptions;
//! use bsize::DisplayScale;
//!
//! let size = BSize::mib(1);
//! assert_eq!("1048576 B", size.to_string());
//! assert_eq!("1.0 MiB", size.display().to_string());
//!
//! assert_eq!("518.0 GiB", BSize::gib(518).display().binary().to_string());
//!
//! assert_eq!("556.2 GB", BSize::gib(518).display().decimal().to_string());
Expand Down Expand Up @@ -138,7 +144,9 @@ pub use self::traits::TeraByteSize;
/// Byte size representation.
///
/// Use [`ByteSize::b`] to construct a value from bytes and [`ByteSize::bytes`] to get
/// the exact underlying byte count.
/// the exact underlying byte count. Its standard [`core::fmt::Display`] implementation renders
/// that exact count in base bytes. Use [`ByteSize::display`] for configurable, approximate
/// human-readable formatting.
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ByteSize<T: BaseByteSize>(T);

Expand Down