1
1
use std:: convert:: Infallible ;
2
2
use std:: fmt;
3
+ use std:: mem:: MaybeUninit ;
4
+ use std:: str:: from_utf8_unchecked;
3
5
4
- use humansize :: { DECIMAL , ISizeFormatter , ToF64 } ;
6
+ use super :: FastWritable ;
5
7
6
8
/// Returns adequate string representation (in KB, ..) of number of bytes
7
9
///
@@ -21,24 +23,95 @@ use humansize::{DECIMAL, ISizeFormatter, ToF64};
21
23
/// assert_eq!(tmpl.to_string(), "Filesize: 1.23 MB.");
22
24
/// ```
23
25
#[ inline]
24
- pub fn filesizeformat ( b : & impl ToF64 ) -> Result < FilesizeFormatFilter , Infallible > {
25
- Ok ( FilesizeFormatFilter ( b. to_f64 ( ) ) )
26
+ pub fn filesizeformat ( b : f32 ) -> Result < FilesizeFormatFilter , Infallible > {
27
+ Ok ( FilesizeFormatFilter ( b) )
26
28
}
27
29
28
30
#[ derive( Debug , Clone , Copy ) ]
29
- pub struct FilesizeFormatFilter ( f64 ) ;
31
+ pub struct FilesizeFormatFilter ( f32 ) ;
30
32
31
33
impl fmt:: Display for FilesizeFormatFilter {
34
+ #[ inline]
32
35
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
33
- f . write_fmt ( format_args ! ( "{}" , ISizeFormatter :: new ( self . 0 , & DECIMAL ) ) )
36
+ self . write_into ( f )
34
37
}
35
38
}
36
39
40
+ impl FastWritable for FilesizeFormatFilter {
41
+ fn write_into < W : fmt:: Write + ?Sized > ( & self , dest : & mut W ) -> fmt:: Result {
42
+ if self . 0 < 1e3 {
43
+ ( self . 0 as u32 ) . write_into ( dest) ?;
44
+ dest. write_str ( " B" )
45
+ } else if let Some ( ( prefix, factor) ) = SI_PREFIXES
46
+ . iter ( )
47
+ . copied ( )
48
+ . find_map ( |( prefix_factor, max) | ( self . 0 < max) . then_some ( prefix_factor) )
49
+ {
50
+ // u32 is big enough to hold the number 999_999
51
+ let scaled = ( self . 0 * factor) as u32 ;
52
+ ( scaled / 100 ) . write_into ( dest) ?;
53
+ dest. write_str ( format_frac ( & mut MaybeUninit :: uninit ( ) , prefix, scaled) )
54
+ } else {
55
+ too_big ( self . 0 , dest)
56
+ }
57
+ }
58
+ }
59
+
60
+ /// Formats `buffer` to contain the decimal point, decimal places and unit
61
+ fn format_frac ( buffer : & mut MaybeUninit < [ u8 ; 8 ] > , prefix : u8 , scaled : u32 ) -> & str {
62
+ // LLVM generates better byte code for register sized buffers, so we add some NULs
63
+ let buffer = buffer. write ( * b"..0 kB\0 \0 " ) ;
64
+ buffer[ 4 ] = prefix;
65
+
66
+ let frac = scaled % 100 ;
67
+ let buffer = if frac == 0 {
68
+ & buffer[ 3 ..6 ]
69
+ } else if frac % 10 == 0 {
70
+ // the decimal separator '.' is already contained in buffer[1]
71
+ buffer[ 2 ] = b'0' + ( frac / 10 ) as u8 ;
72
+ & buffer[ 1 ..6 ]
73
+ } else {
74
+ // the decimal separator '.' is already contained in buffer[0]
75
+ buffer[ 1 ] = b'0' + ( frac / 10 ) as u8 ;
76
+ buffer[ 2 ] = b'0' + ( frac % 10 ) as u8 ;
77
+ & buffer[ 0 ..6 ]
78
+ } ;
79
+ // SAFETY: we know that the buffer contains only ASCII data
80
+ unsafe { from_utf8_unchecked ( buffer) }
81
+ }
82
+
83
+ #[ cold]
84
+ fn too_big < W : fmt:: Write + ?Sized > ( value : f32 , dest : & mut W ) -> fmt:: Result {
85
+ // the value exceeds 999 QB, so we omit the decimal places
86
+ write ! ( dest, "{:.0} QB" , value / 1e30 )
87
+ }
88
+
89
+ /// `((si_prefix, factor), limit)`, the factor is offset by 10**2 to account for 2 decimal places
90
+ const SI_PREFIXES : & [ ( ( u8 , f32 ) , f32 ) ] = & [
91
+ ( ( b'k' , 1e-1 ) , 1e6 ) ,
92
+ ( ( b'M' , 1e-4 ) , 1e9 ) ,
93
+ ( ( b'G' , 1e-7 ) , 1e12 ) ,
94
+ ( ( b'T' , 1e-10 ) , 1e15 ) ,
95
+ ( ( b'P' , 1e-13 ) , 1e18 ) ,
96
+ ( ( b'E' , 1e-16 ) , 1e21 ) ,
97
+ ( ( b'Z' , 1e-19 ) , 1e24 ) ,
98
+ ( ( b'Y' , 1e-22 ) , 1e27 ) ,
99
+ ( ( b'R' , 1e-25 ) , 1e30 ) ,
100
+ ( ( b'Q' , 1e-28 ) , 1e33 ) ,
101
+ ] ;
102
+
37
103
#[ test]
104
+ #[ allow( clippy:: needless_borrows_for_generic_args) ]
38
105
fn test_filesizeformat ( ) {
39
- assert_eq ! ( filesizeformat( & 0 ) . unwrap( ) . to_string( ) , "0 B" ) ;
40
- assert_eq ! ( filesizeformat( & 999u64 ) . unwrap( ) . to_string( ) , "999 B" ) ;
41
- assert_eq ! ( filesizeformat( & 1000i32 ) . unwrap( ) . to_string( ) , "1 kB" ) ;
42
- assert_eq ! ( filesizeformat( & 1023 ) . unwrap( ) . to_string( ) , "1.02 kB" ) ;
43
- assert_eq ! ( filesizeformat( & 1024usize ) . unwrap( ) . to_string( ) , "1.02 kB" ) ;
106
+ assert_eq ! ( filesizeformat( 0. ) . unwrap( ) . to_string( ) , "0 B" ) ;
107
+ assert_eq ! ( filesizeformat( 999. ) . unwrap( ) . to_string( ) , "999 B" ) ;
108
+ assert_eq ! ( filesizeformat( 1000. ) . unwrap( ) . to_string( ) , "1 kB" ) ;
109
+ assert_eq ! ( filesizeformat( 1023. ) . unwrap( ) . to_string( ) , "1.02 kB" ) ;
110
+ assert_eq ! ( filesizeformat( 1024. ) . unwrap( ) . to_string( ) , "1.02 kB" ) ;
111
+ assert_eq ! ( filesizeformat( 1100. ) . unwrap( ) . to_string( ) , "1.1 kB" ) ;
112
+ assert_eq ! ( filesizeformat( 9_499_014. ) . unwrap( ) . to_string( ) , "9.49 MB" ) ;
113
+ assert_eq ! (
114
+ filesizeformat( 954_548_589.2 ) . unwrap( ) . to_string( ) ,
115
+ "954.54 MB"
116
+ ) ;
44
117
}
0 commit comments