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