ubyte/
byte_unit.rs

1/// A unit of bytes with saturating `const` constructors and arithmetic.
2///
3/// # Overview
4///
5/// A `ByteUnit` represents a unit, a count, a number, of bytes. All operations
6/// on a `ByteUnit` -- constructors, arithmetic, conversions -- saturate.
7/// Overflow, underflow, and divide-by-zero are impossible. See the [top-level
8/// documentation](./index.html) for more.
9///
10/// [`ToByteUnit`] provides human-friendly methods on all integer types for
11/// converting into a `ByteUnit`: [`512.megabytes()`](ToByteUnit::megabytes).
12///
13/// # Parsing
14///
15/// `ByteUnit` implements `FromStr` for parsing byte unit strings into a
16/// `ByteUnit`. The grammar accepted by the parser is:
17///
18/// ```ebnf
19/// byte_unit := uint+ ('.' uint+)? WHITESPACE* suffix
20///
21/// uint := '0'..'9'
22/// suffix := case insensitive SI byte unit suffix ('b' to 'eib')
23/// WHITESPACE := the ' ' character
24/// ```
25///
26/// ```rust
27/// use ubyte::{ByteUnit, ToByteUnit};
28///
29/// let one_gib: ByteUnit = "1GiB".parse().unwrap();
30/// assert_eq!(one_gib, 1.gibibytes());
31///
32/// let quarter_mb: ByteUnit = "256 kB".parse().unwrap();
33/// assert_eq!(quarter_mb, 256.kilobytes());
34///
35/// let half_mb: ByteUnit = "0.5MB".parse().unwrap();
36/// assert_eq!(half_mb, 500.kilobytes());
37///
38/// let half_mib: ByteUnit = "0.500 mib".parse().unwrap();
39/// assert_eq!(half_mib, 512.kibibytes());
40///
41/// let some_mb: ByteUnit = "20.5MB".parse().unwrap();
42/// assert_eq!(some_mb, 20.megabytes() + 500.kilobytes());
43/// ```
44///
45/// # (De)serialization
46///
47/// With the `serde` feaure enabled (disabled by default), `ByteUnit` implements
48/// [`Deserialize`](#impl-Deserialize<%27de>) from strings, using the same
49/// grammar as the `FromStr` implementation, defined above, as well as all
50/// integer types. The [`Serialize`](struct.ByteUnit.html#impl-Serialize)
51/// implementation serializes into a `u64`.
52///
53/// # Example
54///
55/// ```rust
56/// use ubyte::{ByteUnit, ToByteUnit};
57///
58/// // Construct with unit-valued associated constants, `const` constructors, or
59/// // human-friendly methods from the `ToByteUnit` integer extension trait.
60/// const HALF_GB: ByteUnit = ByteUnit::Megabyte(500);
61/// const HALF_GIB: ByteUnit = ByteUnit::Mebibyte(512);
62/// let half_gb = 500 * ByteUnit::MB;
63/// let half_gib = 512 * ByteUnit::MiB;
64/// let half_gb = 500.megabytes();
65/// let half_gib = 512.mebibytes();
66///
67/// // All arithmetic operations and conversions saturate.
68/// let exbibyte = ByteUnit::Exbibyte(1);
69/// let exbibyte_too_large_a = 1024 * ByteUnit::EiB;
70/// let exbibyte_too_large_b = ByteUnit::Exbibyte(1024);
71/// let exbibyte_too_large_c = 1024.exbibytes();
72/// let div_by_zero = 1024.exbibytes() / 0;
73/// let too_small = 1000.megabytes() - 1.gibibytes();
74/// assert_eq!(exbibyte << 4, ByteUnit::max_value());
75/// assert_eq!(exbibyte << 10, ByteUnit::max_value());
76/// assert_eq!(exbibyte_too_large_a, ByteUnit::max_value());
77/// assert_eq!(exbibyte_too_large_b, ByteUnit::max_value());
78/// assert_eq!(exbibyte_too_large_c, ByteUnit::max_value());
79/// assert_eq!(div_by_zero, ByteUnit::max_value());
80/// assert_eq!(too_small, 0);
81/// ```
82#[repr(transparent)]
83#[derive(Debug, Default, Copy, Clone, Eq, Hash, Ord)]
84pub struct ByteUnit(pub(crate) u64);
85
86macro_rules! rem_and_suffix {
87    ($n:expr => $(($isuffix:ident, $suffix:ident)),+ $or_else:ident) => {
88        loop {
89            $(
90                let i_val = ByteUnit::$isuffix.as_u64();
91                let s_val = ByteUnit::$suffix.as_u64();
92
93                if $n >= s_val {
94                    let (u_val, unit, string) = if $n % s_val >= i_val - s_val {
95                        (i_val, ByteUnit::$isuffix, stringify!($isuffix))
96                    } else {
97                        (s_val, ByteUnit::$suffix, stringify!($suffix))
98                    };
99
100                    break ($n / u_val, ($n % u_val) as f64 / u_val as f64, string, unit)
101                }
102            )+
103
104            break ($n, 0f64, stringify!($or_else), ByteUnit::$or_else)
105        }
106    };
107}
108
109macro_rules! const_if {
110    ($cond:expr, $on_true:expr, $on_false:expr) => (
111        [$on_false, $on_true][$cond as usize]
112    )
113}
114
115macro_rules! constructor_fns {
116    ($($sstr:expr, $nstr:expr, $example:expr, $suffix:ident, $name:ident = $size:expr),*) => (
117        $(
118            /// Number of bytes in 1
119            #[doc = $sstr]
120            /// (`
121            #[doc = $nstr]
122            /// `).
123            #[allow(non_upper_case_globals)]
124            pub const $suffix: ByteUnit = ByteUnit::$name(1);
125        )*
126
127        $(
128            /// Constructs a `ByteUnit` representing `n`
129            #[doc = $sstr]
130            /// .
131            ///
132            /// # Example
133            ///
134            /// ```rust
135            /// # use ubyte::ByteUnit;
136            #[doc = $example]
137            /// ```
138            #[allow(non_snake_case)]
139            pub const fn $name(n: u64) -> ByteUnit {
140                let size: u64 = $size;
141                let v = const_if!(n as u128 * size as u128 > u64::max_value() as u128,
142                    ByteUnit::max_value().as_u128(),
143                    n as u128 * size as u128
144                );
145
146                ByteUnit(v as u64)
147            }
148        )*
149    );
150
151    ($($suffix:ident, $name:ident = $size:expr),* $(,)?) => (
152        constructor_fns!($(
153            stringify!($suffix), stringify!($size), concat!(
154                "assert_eq!(ByteUnit::", stringify!($name), "(10), ",
155                "10 * ByteUnit::", stringify!($suffix), ");"
156            ), $suffix, $name = $size
157        ),*);
158    )
159}
160
161impl ByteUnit {
162    constructor_fns! {
163        B, Byte = 1,
164        kB, Kilobyte = 1_000,
165        KiB, Kibibyte = 1 << 10,
166        MB, Megabyte = 1_000_000,
167        MiB, Mebibyte = 1 << 20,
168        GB, Gigabyte = 1_000_000_000,
169        GiB, Gibibyte = 1 << 30,
170        TB, Terabyte = 1_000_000_000_000,
171        TiB, Tebibyte = 1 << 40,
172        PB, Petabyte = 1_000_000_000_000_000,
173        PiB, Pebibyte = 1 << 50,
174        EB, Exabyte = 1_000_000_000_000_000_000,
175        EiB, Exbibyte = 1  << 60,
176    }
177
178    /// The maximum value of bytes representable by `ByteUnit`.
179    ///
180    /// # Example
181    ///
182    /// ```rust
183    /// # use ubyte::ByteUnit;
184    /// assert_eq!(ByteUnit::max_value(), u64::max_value());
185    /// ```
186    pub const fn max_value() -> ByteUnit {
187        ByteUnit(u64::max_value())
188    }
189
190    /// Returns the value of bytes represented by `self` as a `u64`.
191    ///
192    /// # Example
193    ///
194    /// ```rust
195    /// # use ubyte::ByteUnit;
196    /// let int: u64 = ByteUnit::Gigabyte(4).as_u64();
197    /// assert_eq!(int, 4 * ByteUnit::GB);
198    ///
199    /// assert_eq!(ByteUnit::Megabyte(42).as_u64(), 42 * 1_000_000);
200    /// assert_eq!(ByteUnit::Exbibyte(7).as_u64(), 7 * 1 << 60);
201    /// ```
202    pub const fn as_u64(self) -> u64 {
203        self.0
204    }
205
206    /// Returns the value of bytes represented by `self` as a `u128`.
207    ///
208    /// # Example
209    ///
210    /// ```rust
211    /// # use ubyte::ByteUnit;
212    /// let int: u128 = ByteUnit::Gigabyte(4).as_u128();
213    /// assert_eq!(int, 4 * ByteUnit::GB);
214    ///
215    /// assert_eq!(ByteUnit::Megabyte(42).as_u64(), 42 * 1_000_000);
216    /// assert_eq!(ByteUnit::Exbibyte(7).as_u64(), 7 * 1 << 60);
217    /// ```
218    pub const fn as_u128(self) -> u128 {
219        self.0 as u128
220    }
221
222    /// Returns the components of the minimal unit representation of `self`.
223    ///
224    /// The "minimal unit representation" is the representation that maximizes
225    /// the SI-unit while minimizing the whole part of the value. For example,
226    /// `1024.bytes()` is minimally represented by `1KiB`, while `1023.bytes()`
227    /// is minimally represented by `1.023kB`.
228    ///
229    /// The four components returned, in tuple-order, are:
230    ///   * `whole` - the whole part of the minimal representation.
231    ///   * `frac` - the fractional part of the minimal representation.
232    ///   * `suffix` - the suffix of the minimal representation.
233    ///   * `unit` - the `1`-unit of the minimal representation.
234    ///
235    /// Succinctly, this is: `(whole, frac, suffix, unit)`. Observe that `(whole
236    /// + frac) * unit` reconstructs the original value.
237    ///
238    /// # Example
239    ///
240    /// ```rust
241    /// use ubyte::{ByteUnit, ToByteUnit};
242    ///
243    /// let value = 2.mebibytes() + 512.kibibytes();
244    /// assert_eq!(value.to_string(), "2.50MiB");
245    ///
246    /// let (whole, frac, suffix, unit) = value.repr();
247    /// assert_eq!(whole, 2);
248    /// assert_eq!(frac, 0.5);
249    /// assert_eq!(suffix, "MiB");
250    /// assert_eq!(unit, ByteUnit::MiB);
251    ///
252    /// let reconstructed = (whole as f64 + frac) * unit.as_u64() as f64;
253    /// assert_eq!(reconstructed as u64, value);
254    /// ```
255    pub fn repr(self) -> (u64, f64, &'static str, ByteUnit) {
256        rem_and_suffix! { self.as_u64() =>
257            (EiB, EB), (TiB, TB), (GiB, GB), (MiB, MB), (KiB, kB) B
258        }
259    }
260}
261
262impl From<ByteUnit> for u64 {
263    #[inline(always)]
264    fn from(v: ByteUnit) -> Self {
265        v.as_u64()
266    }
267}
268
269impl From<ByteUnit> for u128 {
270    #[inline(always)]
271    fn from(v: ByteUnit) -> Self {
272        v.as_u128()
273    }
274}
275
276macro_rules! impl_from_int_unknown {
277    ($T:ty) => (
278        impl From<$T> for ByteUnit {
279            #[inline(always)]
280            fn from(value: $T) -> Self {
281                if core::mem::size_of::<$T>() <= core::mem::size_of::<i64>() {
282                    ByteUnit::from(value as i64)
283                } else if value <= i64::max_value() as $T {
284                    ByteUnit::from(value as i64)
285                } else {
286                    ByteUnit::max_value()
287                }
288            }
289        }
290    )
291}
292
293macro_rules! impl_from_uint_unknown {
294    ($T:ty) => (
295        impl From<$T> for ByteUnit {
296            #[inline(always)]
297            fn from(value: $T) -> Self {
298                if core::mem::size_of::<$T>() <= core::mem::size_of::<u64>() {
299                    ByteUnit(value as u64)
300                } else if value <= u64::max_value() as $T {
301                    ByteUnit(value as u64)
302                } else {
303                    ByteUnit::max_value()
304                }
305            }
306        }
307    )
308}
309
310macro_rules! impl_from_unsigned {
311    ($T:ty) => (
312        impl From<$T> for ByteUnit {
313            #[inline(always)] fn from(v: $T) -> Self { ByteUnit(v as u64) }
314        }
315    )
316}
317
318macro_rules! impl_from_signed {
319    ($T:ty) => (
320        impl From<$T> for ByteUnit {
321            #[inline(always)] fn from(v: $T) -> Self {
322                ByteUnit(core::cmp::max(v, 0) as u64)
323            }
324        }
325    )
326}
327
328impl_from_unsigned!(u8);
329impl_from_unsigned!(u16);
330impl_from_unsigned!(u32);
331impl_from_unsigned!(u64);
332
333impl_from_signed!(i8);
334impl_from_signed!(i16);
335impl_from_signed!(i32);
336impl_from_signed!(i64);
337
338impl_from_uint_unknown!(usize);
339impl_from_uint_unknown!(u128);
340impl_from_int_unknown!(isize);
341impl_from_int_unknown!(i128);
342
343macro_rules! helper_fn {
344    ($kindstr:expr, $name:ident = $kind:ident) => (
345        /// Converts `self` to a `ByteUnit` representing `self`
346        #[doc = $kindstr]
347        /// .
348        #[inline(always)]
349        fn $name(self) -> ByteUnit {
350            self.bytes() * ByteUnit::$kind
351        }
352    );
353
354    ($name:ident = $kind:ident) => (
355        helper_fn!(stringify!($kind), $name = $kind);
356    )
357}
358
359/// Extension trait for conversion from integer types to [`ByteUnit`].
360///
361/// The `ToByteUnit` trait provides methods on integer types that convert the
362/// integer type into the [`ByteUnit`] unit represented by the method name. To
363/// use the trait, simply import it. The trait is implemented for all integer
364/// types.
365///
366/// As with all other `ByteUnit` operations, conversions saturate.
367///
368/// # Example
369///
370/// ```rust
371/// use ubyte::ToByteUnit;
372///
373/// assert_eq!(512.kilobytes(), 512000.bytes());
374/// assert_eq!(512.kibibytes(), 524288.bytes());
375/// assert_eq!(512.kilobytes(), 512 * 1.kilobytes());
376///
377/// assert_eq!(1000.bytes(), 1.kilobytes());
378/// assert_eq!(1000.bytes() + 24, 1.kibibytes());
379/// assert_eq!(2048.mebibytes(), 2.gibibytes());
380///
381/// assert!(2.megabytes() + 500.kilobytes() > 2.mebibytes());
382/// assert!(2.pebibytes() > 2.petabytes());
383///
384/// // As with other `ByteUnit` operations, conversions saturate.
385/// assert_eq!((1 << 10).exbibytes(), (1 << 20).exbibytes());
386/// ```
387pub trait ToByteUnit: Into<ByteUnit> {
388    /// Converts `self` to a `ByteUnit` representing `self` bytes.
389    #[inline(always)]
390    fn bytes(self) -> ByteUnit {
391        self.into()
392    }
393
394    helper_fn!(kilobytes = kB);
395    helper_fn!(kibibytes = KiB);
396    helper_fn!(megabytes = MB);
397    helper_fn!(mebibytes = MiB);
398    helper_fn!(gigabytes = GB);
399    helper_fn!(gibibytes = GiB);
400    helper_fn!(terabytes = TB);
401    helper_fn!(tibibytes = TiB);
402    helper_fn!(petabytes = PB);
403    helper_fn!(pebibytes = PiB);
404    helper_fn!(exabytes = EB);
405    helper_fn!(exbibytes = EiB);
406}
407
408impl<T: Into<ByteUnit> + Copy> ToByteUnit for T {}
409
410/// Display `self` as best as possible. For perfectly custom display output,
411/// consider using [`ByteUnit::repr()`].
412///
413/// # Example
414///
415/// ```rust
416/// use ubyte::{ByteUnit, ToByteUnit};
417///
418/// assert_eq!(323.kilobytes().to_string(), "323kB");
419/// assert_eq!(3.megabytes().to_string(), "3MB");
420/// assert_eq!(3.mebibytes().to_string(), "3MiB");
421///
422/// assert_eq!((3.mebibytes() + 140.kilobytes()).to_string(), "3.13MiB");
423/// assert_eq!((3.mebibytes() + 2.mebibytes()).to_string(), "5MiB");
424/// assert_eq!((7.gigabytes() + 58.mebibytes() + 3.kilobytes()).to_string(), "7.06GB");
425/// assert_eq!((7.gibibytes() + 920.mebibytes()).to_string(), "7.90GiB");
426/// assert_eq!(7231.kilobytes().to_string(), "6.90MiB");
427///
428/// assert_eq!(format!("{:.0}", 7.gibibytes() + 920.mebibytes()), "8GiB");
429/// assert_eq!(format!("{:.1}", 7.gibibytes() + 920.mebibytes()), "7.9GiB");
430/// assert_eq!(format!("{:.2}", 7.gibibytes() + 920.mebibytes()), "7.90GiB");
431/// assert_eq!(format!("{:.3}", 7.gibibytes() + 920.mebibytes()), "7.898GiB");
432/// assert_eq!(format!("{:.4}", 7.gibibytes() + 920.mebibytes()), "7.8984GiB");
433/// assert_eq!(format!("{:.4}", 7231.kilobytes()), "6.8960MiB");
434/// assert_eq!(format!("{:.0}", 7231.kilobytes()), "7MiB");
435/// assert_eq!(format!("{:.2}", 999.kilobytes() + 990.bytes()), "976.55KiB");
436/// assert_eq!(format!("{:.0}", 999.kilobytes() + 990.bytes()), "1MB");
437///
438/// assert_eq!(format!("{:04.2}", 999.kilobytes() + 990.bytes()), "0976.55KiB");
439/// assert_eq!(format!("{:02.0}", 999.kilobytes() + 990.bytes()), "01MB");
440/// assert_eq!(format!("{:04.0}", 999.kilobytes() + 990.bytes()), "0001MB");
441/// ```
442impl core::fmt::Display for ByteUnit {
443    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
444        let (whole, rem, suffix, unit) = self.repr();
445        let width = f.width().unwrap_or(0);
446        if rem != 0f64 && f.precision().map(|p| p > 0).unwrap_or(true) {
447            let p = f.precision().unwrap_or(2);
448            let k = 10u64.saturating_pow(p as u32) as f64;
449            write!(f, "{:0width$}.{:0p$.0}{}", whole, rem * k, suffix,
450                p = p, width = width)
451        } else if rem > 0.5f64 {
452            ((whole.bytes() + 1) * unit).fmt(f)
453        } else {
454            write!(f, "{:0width$}{}", whole, suffix, width = width)
455        }
456    }
457}