@@ -2,40 +2,54 @@ use core::{fmt, write};
22
33use 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 ) ]
715pub ( crate ) enum Format {
816 Iec ,
917 IecShort ,
1018 Si ,
1119 SiShort ,
20+ IecBits ,
21+ SiBits ,
1222}
1323
1424impl 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
120154impl 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