Skip to content
Open
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
72 changes: 61 additions & 11 deletions src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,54 @@ use core::{fmt, write};

use crate::ByteSize;

const KIB_BITS: u64 = crate::KIB * 8;
const KB_BITS: u64 = crate::KB * 8;

/// `ln(8196) ~= 6.931`
const LN_KIB_BITS: f64 = 9.010_913_347_279_289;
/// `ln(8000) ~= 6.931`
const LN_KB_BITS: f64 = 8.987_196_820_661_972;

/// Format / style to use when displaying a [`ByteSize`].
#[derive(Debug, Clone, Copy)]
pub(crate) enum Format {
Iec,
IecShort,
Si,
SiShort,
IecBits,
SiBits,
}

impl Format {
fn unit(self) -> u64 {
match self {
Format::Iec | Format::IecShort => crate::KIB,
Format::Si | Format::SiShort => crate::KB,
Format::IecBits => KIB_BITS,
Format::SiBits => KB_BITS,
}
}

fn unit_base(self) -> f64 {
match self {
Format::Iec | Format::IecShort => crate::LN_KIB,
Format::Si | Format::SiShort => crate::LN_KB,
Format::IecBits => LN_KIB_BITS,
Format::SiBits => LN_KB_BITS,
}
}

fn unit_prefixes(self) -> &'static [u8] {
match self {
Format::Iec | Format::IecShort => crate::UNITS_IEC.as_bytes(),
Format::Si | Format::SiShort => crate::UNITS_SI.as_bytes(),
Format::Iec | Format::IecShort | Format::IecBits => crate::UNITS_IEC.as_bytes(),
Format::Si | Format::SiShort | Self::SiBits => crate::UNITS_SI.as_bytes(),
}
}

fn unit_separator(self) -> &'static str {
match self {
Format::Iec | Format::Si => " ",
Format::Iec | Format::Si | Format::IecBits | Format::SiBits => " ",
Format::IecShort | Format::SiShort => "",
}
}
Expand All @@ -45,6 +59,8 @@ impl Format {
Format::Iec => "iB",
Format::Si => "B",
Format::IecShort | Format::SiShort => "",
Format::IecBits => "ib",
Format::SiBits => "b",
}
}
}
Expand Down Expand Up @@ -115,12 +131,33 @@ impl Display {
self.format = Format::SiShort;
self
}

/// Format as equivalent number of bits using IEC (binary) units.
///
/// E.g., `12.3 Mib`.
#[must_use]
pub fn iec_bits(mut self) -> Self {
self.format = Format::IecBits;
self
}

/// Format as equivalent number of bits using SI (decimal) units.
///
/// E.g., `12.3 Mb`.
#[must_use]
pub fn si_bits(mut self) -> Self {
self.format = Format::SiBits;
self
}
}

impl fmt::Display for Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = self.byte_size.as_u64();

let is_bits = matches!(self.format, Format::IecBits | Format::SiBits);
let bits_or_bytes = bytes * (is_bits as u64 * 8);

let unit = self.format.unit();
#[allow(unused_variables)] // used in std contexts
let unit_base = self.format.unit_base();
Expand All @@ -130,10 +167,14 @@ impl fmt::Display for Display {
let unit_suffix = self.format.unit_suffix();
let precision = f.precision().unwrap_or(1);

if bytes < unit {
write!(f, "{bytes}{unit_separator}B")?;
if bits_or_bytes < unit {
if is_bits {
write!(f, "{bits_or_bytes}{unit_separator}b")?;
} else {
write!(f, "{bits_or_bytes}{unit_separator}B")?;
}
} else {
let size = bytes as f64;
let size = bits_or_bytes as f64;

#[cfg(feature = "std")]
let exp = ideal_unit_std(size, unit_base);
Expand Down Expand Up @@ -215,6 +256,11 @@ mod tests {
}
}

#[track_caller]
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
assert_eq!(expected, Display { byte_size, format }.to_string());
}

#[test]
fn to_string_iec() {
let display = Display {
Expand Down Expand Up @@ -260,11 +306,6 @@ mod tests {
assert_eq!("953.7M", display.to_string());
}

#[track_caller]
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
assert_eq!(expected, Display { byte_size, format }.to_string());
}

#[test]
fn test_to_string_as() {
assert_to_string("215 B", ByteSize::b(215), Format::Iec);
Expand Down Expand Up @@ -295,6 +336,15 @@ mod tests {
assert_to_string("609.0 PB", ByteSize::pb(609), Format::Si);
}

#[test]
fn as_bits() {
assert_to_string("8 b", ByteSize(1), Format::IecBits);
assert_to_string("8 b", ByteSize(1), Format::SiBits);

assert_to_string("8.4 Kib", ByteSize(8555), Format::IecBits);
assert_to_string("8.6 kb", ByteSize(8555), Format::SiBits);
}

#[test]
fn precision() {
let size = ByteSize::mib(1908);
Expand Down
Loading