1mod parse;
29
30#[cfg(feature = "serde")]
31extern crate serde;
32#[cfg(feature = "serde")]
33use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
34#[cfg(feature = "serde")]
35use std::convert::TryFrom;
36
37use std::fmt::{self, Debug, Display, Formatter};
38use std::ops::{Add, AddAssign, Mul, MulAssign};
39
40pub const B: u64 = 1;
42pub const KB: u64 = 1_000;
44pub const MB: u64 = 1_000_000;
46pub const GB: u64 = 1_000_000_000;
48pub const TB: u64 = 1_000_000_000_000;
50pub const PB: u64 = 1_000_000_000_000_000;
52
53pub const KIB: u64 = 1_024;
55pub const MIB: u64 = 1_048_576;
57pub const GIB: u64 = 1_073_741_824;
59pub const TIB: u64 = 1_099_511_627_776;
61pub const PIB: u64 = 1_125_899_906_842_624;
63
64static UNITS: &str = "KMGTPE";
65static UNITS_SI: &str = "kMGTPE";
66static LN_KB: f64 = 6.931471806; static LN_KIB: f64 = 6.907755279; pub fn kb<V: Into<u64>>(size: V) -> u64 {
70 size.into() * KB
71}
72
73pub fn kib<V: Into<u64>>(size: V) -> u64 {
74 size.into() * KIB
75}
76
77pub fn mb<V: Into<u64>>(size: V) -> u64 {
78 size.into() * MB
79}
80
81pub fn mib<V: Into<u64>>(size: V) -> u64 {
82 size.into() * MIB
83}
84
85pub fn gb<V: Into<u64>>(size: V) -> u64 {
86 size.into() * GB
87}
88
89pub fn gib<V: Into<u64>>(size: V) -> u64 {
90 size.into() * GIB
91}
92
93pub fn tb<V: Into<u64>>(size: V) -> u64 {
94 size.into() * TB
95}
96
97pub fn tib<V: Into<u64>>(size: V) -> u64 {
98 size.into() * TIB
99}
100
101pub fn pb<V: Into<u64>>(size: V) -> u64 {
102 size.into() * PB
103}
104
105pub fn pib<V: Into<u64>>(size: V) -> u64 {
106 size.into() * PIB
107}
108
109#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
111pub struct ByteSize(pub u64);
112
113impl ByteSize {
114 #[inline(always)]
115 pub const fn b(size: u64) -> ByteSize {
116 ByteSize(size)
117 }
118
119 #[inline(always)]
120 pub const fn kb(size: u64) -> ByteSize {
121 ByteSize(size * KB)
122 }
123
124 #[inline(always)]
125 pub const fn kib(size: u64) -> ByteSize {
126 ByteSize(size * KIB)
127 }
128
129 #[inline(always)]
130 pub const fn mb(size: u64) -> ByteSize {
131 ByteSize(size * MB)
132 }
133
134 #[inline(always)]
135 pub const fn mib(size: u64) -> ByteSize {
136 ByteSize(size * MIB)
137 }
138
139 #[inline(always)]
140 pub const fn gb(size: u64) -> ByteSize {
141 ByteSize(size * GB)
142 }
143
144 #[inline(always)]
145 pub const fn gib(size: u64) -> ByteSize {
146 ByteSize(size * GIB)
147 }
148
149 #[inline(always)]
150 pub const fn tb(size: u64) -> ByteSize {
151 ByteSize(size * TB)
152 }
153
154 #[inline(always)]
155 pub const fn tib(size: u64) -> ByteSize {
156 ByteSize(size * TIB)
157 }
158
159 #[inline(always)]
160 pub const fn pb(size: u64) -> ByteSize {
161 ByteSize(size * PB)
162 }
163
164 #[inline(always)]
165 pub const fn pib(size: u64) -> ByteSize {
166 ByteSize(size * PIB)
167 }
168
169 #[inline(always)]
170 pub const fn as_u64(&self) -> u64 {
171 self.0
172 }
173
174 #[inline(always)]
175 pub fn to_string_as(&self, si_unit: bool) -> String {
176 to_string(self.0, si_unit)
177 }
178}
179
180pub 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
208impl Display for ByteSize {
209 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
210 f.pad(&to_string(self.0, false))
211 }
212}
213
214impl Debug for ByteSize {
215 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
216 write!(f, "{}", self)
217 }
218}
219
220macro_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
240commutative_op!(u64);
241commutative_op!(u32);
242commutative_op!(u16);
243commutative_op!(u8);
244
245impl Add<ByteSize> for ByteSize {
246 type Output = ByteSize;
247
248 #[inline(always)]
249 fn add(self, rhs: ByteSize) -> ByteSize {
250 ByteSize(self.0 + rhs.0)
251 }
252}
253
254impl AddAssign<ByteSize> for ByteSize {
255 #[inline(always)]
256 fn add_assign(&mut self, rhs: ByteSize) {
257 self.0 += rhs.0
258 }
259}
260
261impl<T> Add<T> for ByteSize
262where
263 T: Into<u64>,
264{
265 type Output = ByteSize;
266 #[inline(always)]
267 fn add(self, rhs: T) -> ByteSize {
268 ByteSize(self.0 + (rhs.into() as u64))
269 }
270}
271
272impl<T> AddAssign<T> for ByteSize
273where
274 T: Into<u64>,
275{
276 #[inline(always)]
277 fn add_assign(&mut self, rhs: T) {
278 self.0 += rhs.into() as u64;
279 }
280}
281
282impl<T> Mul<T> for ByteSize
283where
284 T: Into<u64>,
285{
286 type Output = ByteSize;
287 #[inline(always)]
288 fn mul(self, rhs: T) -> ByteSize {
289 ByteSize(self.0 * (rhs.into() as u64))
290 }
291}
292
293impl<T> MulAssign<T> for ByteSize
294where
295 T: Into<u64>,
296{
297 #[inline(always)]
298 fn mul_assign(&mut self, rhs: T) {
299 self.0 *= rhs.into() as u64;
300 }
301}
302
303#[cfg(feature = "serde")]
304impl<'de> Deserialize<'de> for ByteSize {
305 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
306 where
307 D: Deserializer<'de>,
308 {
309 struct ByteSizeVistor;
310
311 impl<'de> de::Visitor<'de> for ByteSizeVistor {
312 type Value = ByteSize;
313
314 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
315 formatter.write_str("an integer or string")
316 }
317
318 fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
319 if let Ok(val) = u64::try_from(value) {
320 Ok(ByteSize(val))
321 } else {
322 Err(E::invalid_value(
323 de::Unexpected::Signed(value),
324 &"integer overflow",
325 ))
326 }
327 }
328
329 fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
330 Ok(ByteSize(value))
331 }
332
333 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
334 if let Ok(val) = value.parse() {
335 Ok(val)
336 } else {
337 Err(E::invalid_value(
338 de::Unexpected::Str(value),
339 &"parsable string",
340 ))
341 }
342 }
343 }
344
345 if deserializer.is_human_readable() {
346 deserializer.deserialize_any(ByteSizeVistor)
347 } else {
348 deserializer.deserialize_u64(ByteSizeVistor)
349 }
350 }
351}
352
353#[cfg(feature = "serde")]
354impl Serialize for ByteSize {
355 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
356 where
357 S: Serializer,
358 {
359 if serializer.is_human_readable() {
360 <str>::serialize(self.to_string().as_str(), serializer)
361 } else {
362 self.0.serialize(serializer)
363 }
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_arithmetic_op() {
373 let mut x = ByteSize::mb(1);
374 let y = ByteSize::kb(100);
375
376 assert_eq!((x + y).as_u64(), 1_100_000u64);
377
378 assert_eq!((x + (100 * 1000) as u64).as_u64(), 1_100_000);
379
380 assert_eq!((x * 2u64).as_u64(), 2_000_000);
381
382 x += y;
383 assert_eq!(x.as_u64(), 1_100_000);
384 x *= 2u64;
385 assert_eq!(x.as_u64(), 2_200_000);
386 }
387
388 #[test]
389 fn test_arithmetic_primitives() {
390 let mut x = ByteSize::mb(1);
391
392 assert_eq!((x + MB as u64).as_u64(), 2_000_000);
393
394 assert_eq!((x + MB as u32).as_u64(), 2_000_000);
395
396 assert_eq!((x + KB as u16).as_u64(), 1_001_000);
397
398 assert_eq!((x + B as u8).as_u64(), 1_000_001);
399
400 x += MB as u64;
401 x += MB as u32;
402 x += 10u16;
403 x += 1u8;
404 assert_eq!(x.as_u64(), 3_000_011);
405 }
406
407 #[test]
408 fn test_comparison() {
409 assert!(ByteSize::mb(1) == ByteSize::kb(1000));
410 assert!(ByteSize::mib(1) == ByteSize::kib(1024));
411 assert!(ByteSize::mb(1) != ByteSize::kib(1024));
412 assert!(ByteSize::mb(1) < ByteSize::kib(1024));
413 assert!(ByteSize::b(0) < ByteSize::tib(1));
414 }
415
416 fn assert_display(expected: &str, b: ByteSize) {
417 assert_eq!(expected, format!("{}", b));
418 }
419
420 #[test]
421 fn test_display() {
422 assert_display("215 B", ByteSize::b(215));
423 assert_display("1.0 KB", ByteSize::kb(1));
424 assert_display("301.0 KB", ByteSize::kb(301));
425 assert_display("419.0 MB", ByteSize::mb(419));
426 assert_display("518.0 GB", ByteSize::gb(518));
427 assert_display("815.0 TB", ByteSize::tb(815));
428 assert_display("609.0 PB", ByteSize::pb(609));
429 }
430
431 #[test]
432 fn test_display_alignment() {
433 assert_eq!("|357 B |", format!("|{:10}|", ByteSize(357)));
434 assert_eq!("| 357 B|", format!("|{:>10}|", ByteSize(357)));
435 assert_eq!("|357 B |", format!("|{:<10}|", ByteSize(357)));
436 assert_eq!("| 357 B |", format!("|{:^10}|", ByteSize(357)));
437
438 assert_eq!("|-----357 B|", format!("|{:->10}|", ByteSize(357)));
439 assert_eq!("|357 B-----|", format!("|{:-<10}|", ByteSize(357)));
440 assert_eq!("|--357 B---|", format!("|{:-^10}|", ByteSize(357)));
441 }
442
443 fn assert_to_string(expected: &str, b: ByteSize, si: bool) {
444 assert_eq!(expected.to_string(), b.to_string_as(si));
445 }
446
447 #[test]
448 fn test_to_string_as() {
449 assert_to_string("215 B", ByteSize::b(215), true);
450 assert_to_string("215 B", ByteSize::b(215), false);
451
452 assert_to_string("1.0 kiB", ByteSize::kib(1), true);
453 assert_to_string("1.0 KB", ByteSize::kib(1), false);
454
455 assert_to_string("293.9 kiB", ByteSize::kb(301), true);
456 assert_to_string("301.0 KB", ByteSize::kb(301), false);
457
458 assert_to_string("1.0 MiB", ByteSize::mib(1), true);
459 assert_to_string("1048.6 KB", ByteSize::mib(1), false);
460
461 assert_to_string("1.9 GiB", ByteSize::mib(1907), true);
463 assert_to_string("2.0 GB", ByteSize::mib(1908), false);
464
465 assert_to_string("399.6 MiB", ByteSize::mb(419), true);
466 assert_to_string("419.0 MB", ByteSize::mb(419), false);
467
468 assert_to_string("482.4 GiB", ByteSize::gb(518), true);
469 assert_to_string("518.0 GB", ByteSize::gb(518), false);
470
471 assert_to_string("741.2 TiB", ByteSize::tb(815), true);
472 assert_to_string("815.0 TB", ByteSize::tb(815), false);
473
474 assert_to_string("540.9 PiB", ByteSize::pb(609), true);
475 assert_to_string("609.0 PB", ByteSize::pb(609), false);
476 }
477
478 #[test]
479 fn test_default() {
480 assert_eq!(ByteSize::b(0), ByteSize::default());
481 }
482
483 #[test]
484 fn test_to_string() {
485 assert_to_string("609.0 PB", ByteSize::pb(609), false);
486 }
487
488 #[cfg(feature = "serde")]
489 #[test]
490 fn test_serde() {
491 #[derive(Serialize, Deserialize)]
492 struct S {
493 x: ByteSize,
494 }
495
496 let s: S = serde_json::from_str(r#"{ "x": "5 B" }"#).unwrap();
497 assert_eq!(s.x, ByteSize(5));
498
499 let s: S = serde_json::from_str(r#"{ "x": 1048576 }"#).unwrap();
500 assert_eq!(s.x, "1 MiB".parse::<ByteSize>().unwrap());
501
502 let s: S = toml::from_str(r#"x = "2.5 MiB""#).unwrap();
503 assert_eq!(s.x, "2.5 MiB".parse::<ByteSize>().unwrap());
504
505 let s: S = toml::from_str(r#"x = "9223372036854775807""#).unwrap();
507 assert_eq!(s.x, "9223372036854775807".parse::<ByteSize>().unwrap());
508 }
509}