2626//! assert_eq!("482 GiB".to_string(), ByteSize::gb(518).to_string(true));
2727//! assert_eq!("518 GB".to_string(), ByteSize::gb(518).to_string(false));
2828//! ```
29+ #![ cfg_attr( not( feature = "std" ) , no_std) ]
2930
3031mod parse;
3132
33+ #[ cfg( any( feature = "std" , not( all( not( feature = "std" ) , test) ) ) ) ]
34+ extern crate core;
35+ #[ cfg( all( not( feature = "std" ) , test) ) ]
36+ extern crate std;
37+
3238#[ cfg( feature = "serde" ) ]
3339extern crate serde;
3440#[ cfg( feature = "serde" ) ]
3541use serde:: { de, Deserialize , Deserializer , Serialize , Serializer } ;
3642#[ cfg( feature = "serde" ) ]
3743use std:: convert:: TryFrom ;
3844
39- use std :: fmt:: { self , Debug , Display , Formatter } ;
40- use std :: ops:: { Add , AddAssign , Mul , MulAssign } ;
45+ use core :: fmt:: { self , Debug , Display , Formatter } ;
46+ use core :: ops:: { Add , AddAssign , Mul , MulAssign } ;
4147
4248/// byte size for 1 byte
4349pub const B : u64 = 1 ;
@@ -173,13 +179,33 @@ impl ByteSize {
173179 self . 0
174180 }
175181
182+ #[ cfg( feature = "std" ) ]
176183 #[ inline( always) ]
177184 pub fn to_string_as ( & self , si_unit : bool ) -> String {
178185 to_string ( self . 0 , si_unit)
179186 }
180187}
181188
182- pub fn to_string ( bytes : u64 , si_prefix : bool ) -> String {
189+ // Used to implement `Display` in `no_std` environment
190+ struct BytePrinter ( u64 , bool ) ;
191+ impl Display for BytePrinter {
192+ fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
193+ to_string_fmt ( self . 0 , self . 1 , f)
194+ }
195+ }
196+
197+ fn to_string_fmt ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
198+ let unit = if si_prefix { KIB } else { KB } ;
199+
200+ if bytes < unit {
201+ write ! ( f, "{} B" , bytes)
202+ } else {
203+ to_string_decimal ( bytes, si_prefix, f)
204+ }
205+ }
206+
207+ #[ cfg( feature = "std" ) ]
208+ fn to_string_decimal ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
183209 let unit = if si_prefix { KIB } else { KB } ;
184210 let unit_base = if si_prefix { LN_KIB } else { LN_KB } ;
185211 let unit_prefix = if si_prefix {
@@ -189,27 +215,73 @@ pub fn to_string(bytes: u64, si_prefix: bool) -> String {
189215 } ;
190216 let unit_suffix = if si_prefix { "iB" } else { "B" } ;
191217
192- if bytes < unit {
193- format ! ( "{} B" , bytes)
218+ let size = bytes as f64 ;
219+ let exp = match ( size. ln ( ) / unit_base) as usize {
220+ e if e == 0 => 1 ,
221+ e => e,
222+ } ;
223+
224+ write ! (
225+ f,
226+ "{:.1} {}{}" ,
227+ ( size / unit. pow( exp as u32 ) as f64 ) ,
228+ unit_prefix[ exp - 1 ] as char ,
229+ unit_suffix
230+ )
231+ }
232+
233+ // Simplified algorithm because `no_std` does not have access to `f32::ln()`
234+ #[ cfg( not( feature = "std" ) ) ]
235+ fn to_string_decimal ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
236+ let unit_sizes = if si_prefix {
237+ [ KIB , MIB , GIB , TIB , PIB ]
194238 } else {
195- let size = bytes as f64 ;
196- let exp = match ( size. ln ( ) / unit_base) as usize {
197- e if e == 0 => 1 ,
198- e => e,
199- } ;
200-
201- format ! (
202- "{:.1} {}{}" ,
203- ( size / unit. pow( exp as u32 ) as f64 ) ,
204- unit_prefix[ exp - 1 ] as char ,
205- unit_suffix
206- )
239+ [ KB , MB , GB , TB , PB ]
240+ } ;
241+ let unit_prefix = if si_prefix {
242+ UNITS_SI . as_bytes ( )
243+ } else {
244+ UNITS . as_bytes ( )
245+ } ;
246+ let mut ideal_size = unit_sizes[ 0 ] ;
247+ let mut ideal_prefix = unit_prefix[ 0 ] ;
248+ for ( & size, & prefix) in unit_sizes. iter ( ) . zip ( unit_prefix. iter ( ) ) {
249+ ideal_size = size;
250+ ideal_prefix = prefix;
251+ if size <= bytes && bytes / 1_000 < size {
252+ break ;
253+ }
207254 }
255+
256+ let unit_suffix = if si_prefix { "iB" } else { "B" } ;
257+
258+ write ! (
259+ f,
260+ "{:.1} {}{}" ,
261+ bytes as f64 / ideal_size as f64 ,
262+ ideal_prefix as char ,
263+ unit_suffix
264+ )
208265}
209266
267+ #[ cfg( feature = "std" ) ]
268+ pub fn to_string ( bytes : u64 , si_prefix : bool ) -> String {
269+ BytePrinter ( bytes, si_prefix) . to_string ( )
270+ }
271+
272+ // `no_std` padding support would require writing to an intermediary buffer
273+ // as well as implementing said buffer.
274+ // So we just drop padding support in `no_std` environments
210275impl Display for ByteSize {
276+ #[ cfg( feature = "std" ) ]
211277 fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
212- f. pad ( & to_string ( self . 0 , false ) )
278+ f. pad ( & BytePrinter ( self . 0 , false ) . to_string ( ) )
279+ }
280+
281+ #[ cfg( not( feature = "std" ) ) ]
282+ fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
283+
284+ to_string_fmt ( self . 0 , false , f)
213285 }
214286}
215287
@@ -362,6 +434,7 @@ impl Serialize for ByteSize {
362434mod tests {
363435 use super :: * ;
364436
437+ use std:: format;
365438 #[ test]
366439 fn test_arithmetic_op ( ) {
367440 let mut x = ByteSize :: mb ( 1 ) ;
@@ -408,6 +481,7 @@ mod tests {
408481 }
409482
410483 fn assert_display ( expected : & str , b : ByteSize ) {
484+
411485 assert_eq ! ( expected, format!( "{}" , b) ) ;
412486 }
413487
@@ -422,6 +496,7 @@ mod tests {
422496 assert_display ( "609.0 PB" , ByteSize :: pb ( 609 ) ) ;
423497 }
424498
499+ #[ cfg( feature = "std" ) ]
425500 #[ test]
426501 fn test_display_alignment ( ) {
427502 assert_eq ! ( "|357 B |" , format!( "|{:10}|" , ByteSize ( 357 ) ) ) ;
@@ -434,10 +509,12 @@ mod tests {
434509 assert_eq ! ( "|--357 B---|" , format!( "|{:-^10}|" , ByteSize ( 357 ) ) ) ;
435510 }
436511
512+ #[ cfg( feature = "std" ) ]
437513 fn assert_to_string ( expected : & str , b : ByteSize , si : bool ) {
438514 assert_eq ! ( expected. to_string( ) , b. to_string_as( si) ) ;
439515 }
440516
517+ #[ cfg( feature = "std" ) ]
441518 #[ test]
442519 fn test_to_string_as ( ) {
443520 assert_to_string ( "215 B" , ByteSize :: b ( 215 ) , true ) ;
@@ -474,6 +551,7 @@ mod tests {
474551 assert_eq ! ( ByteSize :: b( 0 ) , ByteSize :: default ( ) ) ;
475552 }
476553
554+ #[ cfg( feature = "std" ) ]
477555 #[ test]
478556 fn test_to_string ( ) {
479557 assert_to_string ( "609.0 PB" , ByteSize :: pb ( 609 ) , false ) ;
0 commit comments