Skip to content

Commit 35d6b6d

Browse files
committed
feat: display in bits
1 parent ac756bb commit 35d6b6d

File tree

1 file changed

+61
-11
lines changed

1 file changed

+61
-11
lines changed

src/display.rs

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,54 @@ use core::{fmt, write};
22

33
use crate::ByteSize;
44

5+
const KIB_BITS: u64 = crate::KIB * 8;
6+
const KB_BITS: u64 = crate::KB * 8;
7+
8+
/// `ln(8196) ~= 6.931`
9+
const LN_KIB_BITS: f64 = 9.010_913_347_279_289;
10+
/// `ln(8000) ~= 6.931`
11+
const LN_KB_BITS: f64 = 8.987_196_820_661_972;
12+
513
/// Format / style to use when displaying a [`ByteSize`].
614
#[derive(Debug, Clone, Copy)]
715
pub(crate) enum Format {
816
Iec,
917
IecShort,
1018
Si,
1119
SiShort,
20+
IecBits,
21+
SiBits,
1222
}
1323

1424
impl Format {
1525
fn unit(self) -> u64 {
1626
match self {
1727
Format::Iec | Format::IecShort => crate::KIB,
1828
Format::Si | Format::SiShort => crate::KB,
29+
Format::IecBits => KIB_BITS,
30+
Format::SiBits => KB_BITS,
1931
}
2032
}
2133

2234
fn unit_base(self) -> f64 {
2335
match self {
2436
Format::Iec | Format::IecShort => crate::LN_KIB,
2537
Format::Si | Format::SiShort => crate::LN_KB,
38+
Format::IecBits => LN_KIB_BITS,
39+
Format::SiBits => LN_KB_BITS,
2640
}
2741
}
2842

2943
fn unit_prefixes(self) -> &'static [u8] {
3044
match self {
31-
Format::Iec | Format::IecShort => crate::UNITS_IEC.as_bytes(),
32-
Format::Si | Format::SiShort => crate::UNITS_SI.as_bytes(),
45+
Format::Iec | Format::IecShort | Format::IecBits => crate::UNITS_IEC.as_bytes(),
46+
Format::Si | Format::SiShort | Self::SiBits => crate::UNITS_SI.as_bytes(),
3347
}
3448
}
3549

3650
fn unit_separator(self) -> &'static str {
3751
match self {
38-
Format::Iec | Format::Si => " ",
52+
Format::Iec | Format::Si | Format::IecBits | Format::SiBits => " ",
3953
Format::IecShort | Format::SiShort => "",
4054
}
4155
}
@@ -45,6 +59,8 @@ impl Format {
4559
Format::Iec => "iB",
4660
Format::Si => "B",
4761
Format::IecShort | Format::SiShort => "",
62+
Format::IecBits => "ib",
63+
Format::SiBits => "b",
4864
}
4965
}
5066
}
@@ -115,12 +131,33 @@ impl Display {
115131
self.format = Format::SiShort;
116132
self
117133
}
134+
135+
/// Format as equivalent number of bits using IEC (binary) units.
136+
///
137+
/// E.g., `12.3 Mib`.
138+
#[must_use]
139+
pub fn iec_bits(mut self) -> Self {
140+
self.format = Format::IecBits;
141+
self
142+
}
143+
144+
/// Format as equivalent number of bits using SI (decimal) units.
145+
///
146+
/// E.g., `12.3 Mb`.
147+
#[must_use]
148+
pub fn si_bits(mut self) -> Self {
149+
self.format = Format::SiBits;
150+
self
151+
}
118152
}
119153

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

158+
let is_bits = matches!(self.format, Format::IecBits | Format::SiBits);
159+
let bits_or_bytes = bytes * (is_bits as u64 * 8);
160+
124161
let unit = self.format.unit();
125162
#[allow(unused_variables)] // used in std contexts
126163
let unit_base = self.format.unit_base();
@@ -130,10 +167,14 @@ impl fmt::Display for Display {
130167
let unit_suffix = self.format.unit_suffix();
131168
let precision = f.precision().unwrap_or(1);
132169

133-
if bytes < unit {
134-
write!(f, "{bytes}{unit_separator}B")?;
170+
if bits_or_bytes < unit {
171+
if is_bits {
172+
write!(f, "{bits_or_bytes}{unit_separator}b")?;
173+
} else {
174+
write!(f, "{bits_or_bytes}{unit_separator}B")?;
175+
}
135176
} else {
136-
let size = bytes as f64;
177+
let size = bits_or_bytes as f64;
137178

138179
#[cfg(feature = "std")]
139180
let exp = ideal_unit_std(size, unit_base);
@@ -215,6 +256,11 @@ mod tests {
215256
}
216257
}
217258

259+
#[track_caller]
260+
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
261+
assert_eq!(expected, Display { byte_size, format }.to_string());
262+
}
263+
218264
#[test]
219265
fn to_string_iec() {
220266
let display = Display {
@@ -260,11 +306,6 @@ mod tests {
260306
assert_eq!("953.7M", display.to_string());
261307
}
262308

263-
#[track_caller]
264-
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
265-
assert_eq!(expected, Display { byte_size, format }.to_string());
266-
}
267-
268309
#[test]
269310
fn test_to_string_as() {
270311
assert_to_string("215 B", ByteSize::b(215), Format::Iec);
@@ -295,6 +336,15 @@ mod tests {
295336
assert_to_string("609.0 PB", ByteSize::pb(609), Format::Si);
296337
}
297338

339+
#[test]
340+
fn as_bits() {
341+
assert_to_string("8 b", ByteSize(1), Format::IecBits);
342+
assert_to_string("8 b", ByteSize(1), Format::SiBits);
343+
344+
assert_to_string("8.4 Kib", ByteSize(8555), Format::IecBits);
345+
assert_to_string("8.6 kb", ByteSize(8555), Format::SiBits);
346+
}
347+
298348
#[test]
299349
fn precision() {
300350
let size = ByteSize::mib(1908);

0 commit comments

Comments
 (0)