2828//! assert_eq!("482.4 GiB", ByteSize::gb(518).to_string_as(true));
2929//! assert_eq!("518.0 GB", ByteSize::gb(518).to_string_as(false));
3030//! ```
31+ #![ cfg_attr( not( feature = "std" ) , no_std) ]
3132
3233mod parse;
3334
3435#[ cfg( feature = "arbitrary" ) ]
3536extern crate arbitrary;
37+
38+ #[ cfg( any( feature = "std" , not( all( not( feature = "std" ) , test) ) ) ) ]
39+ extern crate core;
40+ #[ cfg( all( not( feature = "std" ) , test) ) ]
41+ extern crate std;
42+
3643#[ cfg( feature = "serde" ) ]
3744extern crate serde;
3845#[ cfg( feature = "serde" ) ]
3946use serde:: { de, Deserialize , Deserializer , Serialize , Serializer } ;
4047#[ cfg( feature = "serde" ) ]
4148use std:: convert:: TryFrom ;
4249
43- use std :: fmt:: { self , Debug , Display , Formatter } ;
44- use std :: ops:: { Add , AddAssign , Mul , MulAssign } ;
50+ use core :: fmt:: { self , Debug , Display , Formatter } ;
51+ use core :: ops:: { Add , AddAssign , Mul , MulAssign } ;
4552
4653/// byte size for 1 byte
4754pub const B : u64 = 1 ;
@@ -178,13 +185,33 @@ impl ByteSize {
178185 self . 0
179186 }
180187
188+ #[ cfg( feature = "std" ) ]
181189 #[ inline( always) ]
182190 pub fn to_string_as ( & self , si_unit : bool ) -> String {
183191 to_string ( self . 0 , si_unit)
184192 }
185193}
186194
187- pub fn to_string ( bytes : u64 , si_prefix : bool ) -> String {
195+ // Used to implement `Display` in `no_std` environment
196+ struct BytePrinter ( u64 , bool ) ;
197+ impl Display for BytePrinter {
198+ fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
199+ to_string_fmt ( self . 0 , self . 1 , f)
200+ }
201+ }
202+
203+ fn to_string_fmt ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
204+ let unit = if si_prefix { KIB } else { KB } ;
205+
206+ if bytes < unit {
207+ write ! ( f, "{} B" , bytes)
208+ } else {
209+ to_string_decimal ( bytes, si_prefix, f)
210+ }
211+ }
212+
213+ #[ cfg( feature = "std" ) ]
214+ fn to_string_decimal ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
188215 let unit = if si_prefix { KIB } else { KB } ;
189216 let unit_base = if si_prefix { LN_KIB } else { LN_KB } ;
190217 let unit_prefix = if si_prefix {
@@ -194,27 +221,73 @@ pub fn to_string(bytes: u64, si_prefix: bool) -> String {
194221 } ;
195222 let unit_suffix = if si_prefix { "iB" } else { "B" } ;
196223
197- if bytes < unit {
198- format ! ( "{} B" , bytes)
224+ let size = bytes as f64 ;
225+ let exp = match ( size. ln ( ) / unit_base) as usize {
226+ e if e == 0 => 1 ,
227+ e => e,
228+ } ;
229+
230+ write ! (
231+ f,
232+ "{:.1} {}{}" ,
233+ ( size / unit. pow( exp as u32 ) as f64 ) ,
234+ unit_prefix[ exp - 1 ] as char ,
235+ unit_suffix
236+ )
237+ }
238+
239+ // Simplified algorithm because `no_std` does not have access to `f32::ln()`
240+ #[ cfg( not( feature = "std" ) ) ]
241+ fn to_string_decimal ( bytes : u64 , si_prefix : bool , f : & mut fmt:: Formatter ) -> fmt:: Result {
242+ let unit_sizes = if si_prefix {
243+ [ KIB , MIB , GIB , TIB , PIB ]
199244 } else {
200- let size = bytes as f64 ;
201- let exp = match ( size. ln ( ) / unit_base) as usize {
202- e if e == 0 => 1 ,
203- e => e,
204- } ;
205-
206- format ! (
207- "{:.1} {}{}" ,
208- ( size / unit. pow( exp as u32 ) as f64 ) ,
209- unit_prefix[ exp - 1 ] as char ,
210- unit_suffix
211- )
245+ [ KB , MB , GB , TB , PB ]
246+ } ;
247+ let unit_prefix = if si_prefix {
248+ UNITS_SI . as_bytes ( )
249+ } else {
250+ UNITS . as_bytes ( )
251+ } ;
252+ let mut ideal_size = unit_sizes[ 0 ] ;
253+ let mut ideal_prefix = unit_prefix[ 0 ] ;
254+ for ( & size, & prefix) in unit_sizes. iter ( ) . zip ( unit_prefix. iter ( ) ) {
255+ ideal_size = size;
256+ ideal_prefix = prefix;
257+ if size <= bytes && bytes / 1_000 < size {
258+ break ;
259+ }
212260 }
261+
262+ let unit_suffix = if si_prefix { "iB" } else { "B" } ;
263+
264+ write ! (
265+ f,
266+ "{:.1} {}{}" ,
267+ bytes as f64 / ideal_size as f64 ,
268+ ideal_prefix as char ,
269+ unit_suffix
270+ )
213271}
214272
273+ #[ cfg( feature = "std" ) ]
274+ pub fn to_string ( bytes : u64 , si_prefix : bool ) -> String {
275+ BytePrinter ( bytes, si_prefix) . to_string ( )
276+ }
277+
278+ // `no_std` padding support would require writing to an intermediary buffer
279+ // as well as implementing said buffer.
280+ // So we just drop padding support in `no_std` environments
215281impl Display for ByteSize {
216- fn fmt ( & self , f : & mut Formatter ) -> fmt:: Result {
217- f. pad ( & to_string ( self . 0 , false ) )
282+ #[ cfg( feature = "std" ) ]
283+ fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
284+ f. pad ( & BytePrinter ( self . 0 , false ) . to_string ( ) )
285+ }
286+
287+ #[ cfg( not( feature = "std" ) ) ]
288+ fn fmt ( & self , f : & mut Formatter ) ->fmt:: Result {
289+
290+ to_string_fmt ( self . 0 , false , f)
218291 }
219292}
220293
@@ -375,6 +448,7 @@ impl Serialize for ByteSize {
375448mod tests {
376449 use super :: * ;
377450
451+ use std:: format;
378452 #[ test]
379453 fn test_arithmetic_op ( ) {
380454 let mut x = ByteSize :: mb ( 1 ) ;
@@ -421,6 +495,7 @@ mod tests {
421495 }
422496
423497 fn assert_display ( expected : & str , b : ByteSize ) {
498+
424499 assert_eq ! ( expected, format!( "{}" , b) ) ;
425500 }
426501
@@ -435,6 +510,7 @@ mod tests {
435510 assert_display ( "609.0 PB" , ByteSize :: pb ( 609 ) ) ;
436511 }
437512
513+ #[ cfg( feature = "std" ) ]
438514 #[ test]
439515 fn test_display_alignment ( ) {
440516 assert_eq ! ( "|357 B |" , format!( "|{:10}|" , ByteSize ( 357 ) ) ) ;
@@ -447,10 +523,12 @@ mod tests {
447523 assert_eq ! ( "|--357 B---|" , format!( "|{:-^10}|" , ByteSize ( 357 ) ) ) ;
448524 }
449525
526+ #[ cfg( feature = "std" ) ]
450527 fn assert_to_string ( expected : & str , b : ByteSize , si : bool ) {
451528 assert_eq ! ( expected. to_string( ) , b. to_string_as( si) ) ;
452529 }
453530
531+ #[ cfg( feature = "std" ) ]
454532 #[ test]
455533 fn test_to_string_as ( ) {
456534 assert_to_string ( "215 B" , ByteSize :: b ( 215 ) , true ) ;
@@ -487,6 +565,7 @@ mod tests {
487565 assert_eq ! ( ByteSize :: b( 0 ) , ByteSize :: default ( ) ) ;
488566 }
489567
568+ #[ cfg( feature = "std" ) ]
490569 #[ test]
491570 fn test_to_string ( ) {
492571 assert_to_string ( "609.0 PB" , ByteSize :: pb ( 609 ) , false ) ;
0 commit comments