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