1 //! ByteSize is an utility that easily makes bytes size representation
2 //! and helps its arithmetic operations.
3 //!
4 //! ## Example
5 //!
6 //! ```ignore
7 //! extern crate bytesize;
8 //!
9 //! use bytesize::ByteSize;
10 //!
11 //! fn byte_arithmetic_operator() {
12 //!   let x = ByteSize::mb(1);
13 //!   let y = ByteSize::kb(100);
14 //!
15 //!   let plus = x + y;
16 //!   print!("{} bytes", plus.as_u64());
17 //!
18 //!   let minus = ByteSize::tb(100) - ByteSize::gb(4);
19 //!   print!("{} bytes", minus.as_u64());
20 //! }
21 //! ```
22 //!
23 //! It also provides its human readable string as follows:
24 //!
25 //! ```ignore=
26 //!  assert_eq!("482 GiB".to_string(), ByteSize::gb(518).to_string(true));
27 //!  assert_eq!("518 GB".to_string(), ByteSize::gb(518).to_string(false));
28 //! ```
29 
30 mod parse;
31 
32 #[cfg(feature = "serde")]
33 #[macro_use]
34 extern crate serde;
35 
36 use std::fmt::{Debug, Display, Formatter, Result};
37 use std::ops::{Add, AddAssign, Mul, MulAssign};
38 
39 /// byte size for 1 byte
40 pub const B: u64 = 1;
41 /// bytes size for 1 kilobyte
42 pub const KB: u64 = 1_000;
43 /// bytes size for 1 megabyte
44 pub const MB: u64 = 1_000_000;
45 /// bytes size for 1 gigabyte
46 pub const GB: u64 = 1_000_000_000;
47 /// bytes size for 1 terabyte
48 pub const TB: u64 = 1_000_000_000_000;
49 /// bytes size for 1 petabyte
50 pub const PB: u64 = 1_000_000_000_000_000;
51 
52 /// bytes size for 1 kibibyte
53 pub const KIB: u64 = 1_024;
54 /// bytes size for 1 mebibyte
55 pub const MIB: u64 = 1_048_576;
56 /// bytes size for 1 gibibyte
57 pub const GIB: u64 = 1_073_741_824;
58 /// bytes size for 1 tebibyte
59 pub const TIB: u64 = 1_099_511_627_776;
60 /// bytes size for 1 pebibyte
61 pub const PIB: u64 = 1_125_899_906_842_624;
62 
63 static UNITS: &str = "KMGTPE";
64 static UNITS_SI: &str = "kMGTPE";
65 static LN_KB: f64 = 6.931471806; // ln 1024
66 static LN_KIB: f64 = 6.907755279; // ln 1000
67 
kb<V: Into<u64>>(size: V) -> u6468 pub fn kb<V: Into<u64>>(size: V) -> u64 {
69     size.into() * KB
70 }
71 
kib<V: Into<u64>>(size: V) -> u6472 pub fn kib<V: Into<u64>>(size: V) -> u64 {
73     size.into() * KIB
74 }
75 
mb<V: Into<u64>>(size: V) -> u6476 pub fn mb<V: Into<u64>>(size: V) -> u64 {
77     size.into() * MB
78 }
79 
mib<V: Into<u64>>(size: V) -> u6480 pub fn mib<V: Into<u64>>(size: V) -> u64 {
81     size.into() * MIB
82 }
83 
gb<V: Into<u64>>(size: V) -> u6484 pub fn gb<V: Into<u64>>(size: V) -> u64 {
85     size.into() * GB
86 }
87 
gib<V: Into<u64>>(size: V) -> u6488 pub fn gib<V: Into<u64>>(size: V) -> u64 {
89     size.into() * GIB
90 }
91 
tb<V: Into<u64>>(size: V) -> u6492 pub fn tb<V: Into<u64>>(size: V) -> u64 {
93     size.into() * TB
94 }
95 
tib<V: Into<u64>>(size: V) -> u6496 pub fn tib<V: Into<u64>>(size: V) -> u64 {
97     size.into() * TIB
98 }
99 
pb<V: Into<u64>>(size: V) -> u64100 pub fn pb<V: Into<u64>>(size: V) -> u64 {
101     size.into() * PB
102 }
103 
pib<V: Into<u64>>(size: V) -> u64104 pub fn pib<V: Into<u64>>(size: V) -> u64 {
105     size.into() * PIB
106 }
107 
108 /// Byte size representation
109 #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
110 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
111 pub struct ByteSize(pub u64);
112 
113 impl ByteSize {
114     #[inline(always)]
b(size: u64) -> ByteSize115     pub const fn b(size: u64) -> ByteSize {
116         ByteSize(size)
117     }
118 
119     #[inline(always)]
kb(size: u64) -> ByteSize120     pub const fn kb(size: u64) -> ByteSize {
121         ByteSize(size * KB)
122     }
123 
124     #[inline(always)]
kib(size: u64) -> ByteSize125     pub const fn kib(size: u64) -> ByteSize {
126         ByteSize(size * KIB)
127     }
128 
129     #[inline(always)]
mb(size: u64) -> ByteSize130     pub const fn mb(size: u64) -> ByteSize {
131         ByteSize(size * MB)
132     }
133 
134     #[inline(always)]
mib(size: u64) -> ByteSize135     pub const fn mib(size: u64) -> ByteSize {
136         ByteSize(size * MIB)
137     }
138 
139     #[inline(always)]
gb(size: u64) -> ByteSize140     pub const fn gb(size: u64) -> ByteSize {
141         ByteSize(size * GB)
142     }
143 
144     #[inline(always)]
gib(size: u64) -> ByteSize145     pub const fn gib(size: u64) -> ByteSize {
146         ByteSize(size * GIB)
147     }
148 
149     #[inline(always)]
tb(size: u64) -> ByteSize150     pub const fn tb(size: u64) -> ByteSize {
151         ByteSize(size * TB)
152     }
153 
154     #[inline(always)]
tib(size: u64) -> ByteSize155     pub const fn tib(size: u64) -> ByteSize {
156         ByteSize(size * TIB)
157     }
158 
159     #[inline(always)]
pb(size: u64) -> ByteSize160     pub const fn pb(size: u64) -> ByteSize {
161         ByteSize(size * PB)
162     }
163 
164     #[inline(always)]
pib(size: u64) -> ByteSize165     pub const fn pib(size: u64) -> ByteSize {
166         ByteSize(size * PIB)
167     }
168 
169     #[inline(always)]
as_u64(&self) -> u64170     pub fn as_u64(&self) -> u64 {
171         self.0
172     }
173 
174     #[inline(always)]
to_string_as(&self, si_unit: bool) -> String175     pub fn to_string_as(&self, si_unit: bool) -> String {
176         to_string(self.0, si_unit)
177     }
178 }
179 
to_string(bytes: u64, si_prefix: bool) -> String180 pub fn to_string(bytes: u64, si_prefix: bool) -> String {
181     let unit = if si_prefix { KIB } else { KB };
182     let unit_base = if si_prefix { LN_KIB } else { LN_KB };
183     let unit_prefix = if si_prefix {
184         UNITS_SI.as_bytes()
185     } else {
186         UNITS.as_bytes()
187     };
188     let unit_suffix = if si_prefix { "iB" } else { "B" };
189 
190     if bytes < unit {
191         format!("{} B", bytes)
192     } else {
193         let size = bytes as f64;
194         let exp = match (size.ln() / unit_base) as usize {
195             e if e == 0 => 1,
196             e => e,
197         };
198 
199         format!(
200             "{:.1} {}{}",
201             (size / unit.pow(exp as u32) as f64),
202             unit_prefix[exp - 1] as char,
203             unit_suffix
204         )
205     }
206 }
207 
208 impl Display for ByteSize {
fmt(&self, f: &mut Formatter) -> Result209     fn fmt(&self, f: &mut Formatter) -> Result {
210         f.pad(&to_string(self.0, false))
211     }
212 }
213 
214 impl Debug for ByteSize {
fmt(&self, f: &mut Formatter) -> Result215     fn fmt(&self, f: &mut Formatter) -> Result {
216         write!(f, "{}", self)
217     }
218 }
219 
220 macro_rules! commutative_op {
221     ($t:ty) => {
222         impl Add<ByteSize> for $t {
223             type Output = ByteSize;
224             #[inline(always)]
225             fn add(self, rhs: ByteSize) -> ByteSize {
226                 ByteSize(rhs.0 + (self as u64))
227             }
228         }
229 
230         impl Mul<ByteSize> for $t {
231             type Output = ByteSize;
232             #[inline(always)]
233             fn mul(self, rhs: ByteSize) -> ByteSize {
234                 ByteSize(rhs.0 * (self as u64))
235             }
236         }
237     };
238 }
239 
240 commutative_op!(u64);
241 commutative_op!(u32);
242 commutative_op!(u16);
243 commutative_op!(u8);
244 
245 impl Add<ByteSize> for ByteSize {
246     type Output = ByteSize;
247 
248     #[inline(always)]
add(self, rhs: ByteSize) -> ByteSize249     fn add(self, rhs: ByteSize) -> ByteSize {
250         ByteSize(self.0 + rhs.0)
251     }
252 }
253 
254 impl AddAssign<ByteSize> for ByteSize {
255     #[inline(always)]
add_assign(&mut self, rhs: ByteSize)256     fn add_assign(&mut self, rhs: ByteSize) {
257         self.0 += rhs.0
258     }
259 }
260 
261 impl<T> Add<T> for ByteSize
262     where T: Into<u64> {
263     type Output = ByteSize;
264     #[inline(always)]
add(self, rhs: T) -> ByteSize265     fn add(self, rhs: T) -> ByteSize {
266         ByteSize(self.0 + (rhs.into() as u64))
267     }
268 }
269 
270 impl<T> AddAssign<T> for ByteSize
271     where T: Into<u64> {
272     #[inline(always)]
add_assign(&mut self, rhs: T)273     fn add_assign(&mut self, rhs: T) {
274         self.0 += rhs.into() as u64;
275     }
276 }
277 
278 impl<T> Mul<T> for ByteSize
279     where T: Into<u64> {
280     type Output = ByteSize;
281     #[inline(always)]
mul(self, rhs: T) -> ByteSize282     fn mul(self, rhs: T) -> ByteSize {
283         ByteSize(self.0 * (rhs.into() as u64))
284     }
285 }
286 
287 impl<T> MulAssign<T> for ByteSize
288     where T: Into<u64> {
289     #[inline(always)]
mul_assign(&mut self, rhs: T)290     fn mul_assign(&mut self, rhs: T) {
291         self.0 *= rhs.into() as u64;
292     }
293 }
294 
295 #[cfg(test)]
296 mod tests {
297     use super::*;
298 
299     #[test]
test_arithmetic_op()300     fn test_arithmetic_op() {
301         let mut x = ByteSize::mb(1);
302         let y = ByteSize::kb(100);
303 
304         assert_eq!((x + y).as_u64(), 1_100_000u64);
305 
306         assert_eq!((x + (100 * 1000) as u64).as_u64(), 1_100_000);
307 
308         assert_eq!((x * 2u64).as_u64(), 2_000_000);
309 
310         x += y;
311         assert_eq!(x.as_u64(), 1_100_000);
312         x *= 2u64;
313         assert_eq!(x.as_u64(), 2_200_000);
314     }
315 
316     #[test]
test_arithmetic_primitives()317     fn test_arithmetic_primitives() {
318         let mut x = ByteSize::mb(1);
319 
320         assert_eq!((x + MB as u64).as_u64(), 2_000_000);
321 
322         assert_eq!((x + MB as u32).as_u64(), 2_000_000);
323 
324         assert_eq!((x + KB as u16).as_u64(), 1_001_000);
325 
326         assert_eq!((x + B as u8).as_u64(), 1_000_001);
327 
328         x += MB as u64;
329         x += MB as u32;
330         x += 10 as u16;
331         x += 1 as u8;
332         assert_eq!(x.as_u64(), 3_000_011);
333     }
334 
335     #[test]
test_comparison()336     fn test_comparison() {
337         assert!(ByteSize::mb(1) == ByteSize::kb(1000));
338         assert!(ByteSize::mib(1) == ByteSize::kib(1024));
339         assert!(ByteSize::mb(1) != ByteSize::kib(1024));
340         assert!(ByteSize::mb(1) < ByteSize::kib(1024));
341         assert!(ByteSize::b(0) < ByteSize::tib(1));
342     }
343 
assert_display(expected: &str, b: ByteSize)344     fn assert_display(expected: &str, b: ByteSize) {
345         assert_eq!(expected, format!("{}", b));
346     }
347 
348     #[test]
test_display()349     fn test_display() {
350         assert_display("215 B", ByteSize::b(215));
351         assert_display("1.0 KB", ByteSize::kb(1));
352         assert_display("301.0 KB", ByteSize::kb(301));
353         assert_display("419.0 MB", ByteSize::mb(419));
354         assert_display("518.0 GB", ByteSize::gb(518));
355         assert_display("815.0 TB", ByteSize::tb(815));
356         assert_display("609.0 PB", ByteSize::pb(609));
357     }
358 
359     #[test]
test_display_alignment()360     fn test_display_alignment() {
361         assert_eq!("|357 B     |", format!("|{:10}|", ByteSize(357)));
362         assert_eq!("|     357 B|", format!("|{:>10}|", ByteSize(357)));
363         assert_eq!("|357 B     |", format!("|{:<10}|", ByteSize(357)));
364         assert_eq!("|  357 B   |", format!("|{:^10}|", ByteSize(357)));
365 
366         assert_eq!("|-----357 B|", format!("|{:->10}|", ByteSize(357)));
367         assert_eq!("|357 B-----|", format!("|{:-<10}|", ByteSize(357)));
368         assert_eq!("|--357 B---|", format!("|{:-^10}|", ByteSize(357)));
369     }
370 
assert_to_string(expected: &str, b: ByteSize, si: bool)371     fn assert_to_string(expected: &str, b: ByteSize, si: bool) {
372         assert_eq!(expected.to_string(), b.to_string_as(si));
373     }
374 
375     #[test]
test_to_string_as()376     fn test_to_string_as() {
377         assert_to_string("215 B", ByteSize::b(215), true);
378         assert_to_string("215 B", ByteSize::b(215), false);
379 
380         assert_to_string("1.0 kiB", ByteSize::kib(1), true);
381         assert_to_string("1.0 KB", ByteSize::kib(1), false);
382 
383         assert_to_string("293.9 kiB", ByteSize::kb(301), true);
384         assert_to_string("301.0 KB", ByteSize::kb(301), false);
385 
386         assert_to_string("1.0 MiB", ByteSize::mib(1), true);
387         assert_to_string("1048.6 KB", ByteSize::mib(1), false);
388 
389         // a bug case: https://github.com/flang-project/bytesize/issues/8
390         assert_to_string("1.9 GiB", ByteSize::mib(1907), true);
391         assert_to_string("2.0 GB", ByteSize::mib(1908), false);
392 
393         assert_to_string("399.6 MiB", ByteSize::mb(419), true);
394         assert_to_string("419.0 MB", ByteSize::mb(419), false);
395 
396         assert_to_string("482.4 GiB", ByteSize::gb(518), true);
397         assert_to_string("518.0 GB", ByteSize::gb(518), false);
398 
399         assert_to_string("741.2 TiB", ByteSize::tb(815), true);
400         assert_to_string("815.0 TB", ByteSize::tb(815), false);
401 
402         assert_to_string("540.9 PiB", ByteSize::pb(609), true);
403         assert_to_string("609.0 PB", ByteSize::pb(609), false);
404     }
405 
406     #[test]
test_default()407     fn test_default() {
408         assert_eq!(ByteSize::b(0), ByteSize::default());
409     }
410 
411     #[test]
test_to_string()412     fn test_to_string() {
413         assert_to_string("609.0 PB", ByteSize::pb(609), false);
414     }
415 }
416