Skip to main content

typed_bytes/
iec.rs

1//! IEC units (binary)
2//! For more information visit <https://en.wikipedia.org/wiki/Binary_prefix>
3
4use crate::Bytes;
5use crate::impl_comparison;
6use core::fmt;
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
8
9/// 1 KiB = 1024 Bytes
10pub const KIB: u64 = 1024;
11/// 1 MiB = 1024 * 1024 Bytes
12pub const MIB: u64 = 1024 * KIB;
13/// 1 GiB = 1024 * 1024 * 1024 Bytes
14pub const GIB: u64 = 1024 * MIB;
15/// 1 TiB = 1024 * 1024 * 1024 * 1024 Bytes
16pub const TIB: u64 = 1024 * GIB;
17/// 1 PiB = 1024 * 1024 * 1024 * 1024 * 1024 Bytes
18pub const PIB: u64 = 1024 * TIB;
19
20macro_rules! impl_unit {
21    ($name:ident, $unit_str:expr, $multiplier:expr) => {
22        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
23        pub struct $name(pub u64);
24
25        impl $name {
26            pub const fn new(val: u64) -> Self {
27                Self(val)
28            }
29
30            pub const fn as_u64(&self) -> u64 {
31                self.0
32            }
33
34            pub const fn as_bytes(self) -> Bytes {
35                Bytes(self.0 * $multiplier)
36            }
37
38            pub const fn as_kib(self) -> KiB {
39                KiB((self.0 * $multiplier) / KIB)
40            }
41
42            pub const fn as_mib(self) -> MiB {
43                MiB((self.0 * $multiplier) / MIB)
44            }
45
46            pub const fn as_gib(self) -> GiB {
47                GiB((self.0 * $multiplier) / GIB)
48            }
49
50            pub const fn as_tib(self) -> TiB {
51                TiB((self.0 * $multiplier) / TIB)
52            }
53
54            pub const fn as_pib(self) -> PiB {
55                PiB((self.0 * $multiplier) / PIB)
56            }
57        }
58
59        impl fmt::Display for $name {
60            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61                write!(f, "{} {}", self.0, $unit_str)
62            }
63        }
64
65        impl Add for $name {
66            type Output = Self;
67            fn add(self, rhs: Self) -> Self::Output {
68                Self(self.0 + rhs.0)
69            }
70        }
71        impl AddAssign for $name {
72            fn add_assign(&mut self, rhs: Self) {
73                self.0 += rhs.0;
74            }
75        }
76
77        impl Sub for $name {
78            type Output = Self;
79            fn sub(self, rhs: Self) -> Self::Output {
80                Self(self.0 - rhs.0)
81            }
82        }
83        impl SubAssign for $name {
84            fn sub_assign(&mut self, rhs: Self) {
85                self.0 -= rhs.0;
86            }
87        }
88
89        impl Mul<u64> for $name {
90            type Output = Self;
91            fn mul(self, rhs: u64) -> Self::Output {
92                Self(self.0 * rhs)
93            }
94        }
95        impl Mul<$name> for u64 {
96            type Output = $name;
97            fn mul(self, rhs: $name) -> Self::Output {
98                $name(self * rhs.0)
99            }
100        }
101        impl MulAssign<u64> for $name {
102            fn mul_assign(&mut self, rhs: u64) {
103                self.0 *= rhs;
104            }
105        }
106
107        impl Div<u64> for $name {
108            type Output = Self;
109            fn div(self, rhs: u64) -> Self::Output {
110                Self(self.0 / rhs)
111            }
112        }
113        impl DivAssign<u64> for $name {
114            fn div_assign(&mut self, rhs: u64) {
115                self.0 /= rhs;
116            }
117        }
118
119        impl Div for $name {
120            type Output = u64;
121            fn div(self, rhs: Self) -> Self::Output {
122                self.0 / rhs.0
123            }
124        }
125
126        impl Rem for $name {
127            type Output = Self;
128            fn rem(self, rhs: Self) -> Self::Output {
129                Self(self.0 % rhs.0)
130            }
131        }
132        impl RemAssign for $name {
133            fn rem_assign(&mut self, rhs: Self) {
134                self.0 %= rhs.0;
135            }
136        }
137    };
138}
139
140impl_unit!(KiB, "KiB", KIB);
141impl_unit!(MiB, "MiB", MIB);
142impl_unit!(GiB, "GiB", GIB);
143impl_unit!(TiB, "TiB", TIB);
144impl_unit!(PiB, "PiB", PIB);
145
146// Conversions to Bytes
147impl From<KiB> for Bytes {
148    fn from(val: KiB) -> Self {
149        Self(val.0 * KIB)
150    }
151}
152
153impl From<MiB> for Bytes {
154    fn from(val: MiB) -> Self {
155        Self(val.0 * MIB)
156    }
157}
158
159impl From<GiB> for Bytes {
160    fn from(val: GiB) -> Self {
161        Self(val.0 * GIB)
162    }
163}
164
165impl From<TiB> for Bytes {
166    fn from(val: TiB) -> Self {
167        Self(val.0 * TIB)
168    }
169}
170
171impl From<PiB> for Bytes {
172    fn from(val: PiB) -> Self {
173        Self(val.0 * PIB)
174    }
175}
176
177impl From<MiB> for KiB {
178    fn from(val: MiB) -> Self {
179        Self(val.0 * 1024)
180    }
181}
182
183impl From<GiB> for MiB {
184    fn from(val: GiB) -> Self {
185        Self(val.0 * 1024)
186    }
187}
188
189impl From<TiB> for GiB {
190    fn from(val: TiB) -> Self {
191        Self(val.0 * 1024)
192    }
193}
194
195impl From<PiB> for TiB {
196    fn from(val: PiB) -> Self {
197        Self(val.0 * 1024)
198    }
199}
200
201impl_comparison!(Bytes, KiB, MiB, GiB, TiB, PiB);
202impl_comparison!(KiB, MiB, GiB, TiB, PiB);
203impl_comparison!(MiB, GiB, TiB, PiB);
204impl_comparison!(GiB, TiB, PiB);
205impl_comparison!(TiB, PiB);
206
207#[cfg(test)]
208mod tests {
209    extern crate std;
210    use super::*;
211    use crate::Bytes;
212    use std::format;
213
214    #[test]
215    fn test_unit_constants() {
216        assert_eq!(KIB, 1024);
217        assert_eq!(MIB, 1024 * 1024);
218        assert_eq!(GIB, 1024 * 1024 * 1024);
219        assert_eq!(TIB, 1024 * 1024 * 1024 * 1024);
220        assert_eq!(PIB, 1024 * 1024 * 1024 * 1024 * 1024);
221    }
222
223    #[test]
224    fn test_new_and_as_u64() {
225        let b = Bytes::new(100);
226        assert_eq!(b.as_u64(), 100);
227
228        let k = KiB::new(5);
229        assert_eq!(k.as_u64(), 5);
230    }
231
232    #[test]
233    fn test_conversions() {
234        // Conversions to Bytes
235        assert_eq!(Bytes::from(KiB(1)), Bytes(1024));
236        assert_eq!(Bytes::from(MiB(1)), Bytes(1024 * 1024));
237        assert_eq!(Bytes::from(GiB(1)), Bytes(1024 * 1024 * 1024));
238        assert_eq!(Bytes::from(TiB(1)), Bytes(1024 * 1024 * 1024 * 1024));
239        assert_eq!(Bytes::from(PiB(1)), Bytes(1024 * 1024 * 1024 * 1024 * 1024));
240
241        // Internal inter-conversions
242        assert_eq!(KiB::from(MiB(1)), KiB(1024));
243        assert_eq!(MiB::from(GiB(1)), MiB(1024));
244        assert_eq!(GiB::from(TiB(1)), GiB(1024));
245        assert_eq!(TiB::from(PiB(1)), TiB(1024));
246        assert_eq!(GiB::from(GiB(1)), Bytes(1024 * 1024 * 1024));
247    }
248
249    #[test]
250    fn test_arithmetic() {
251        // Add
252        assert_eq!(Bytes(10) + Bytes(20), Bytes(30));
253        let mut b = Bytes(10);
254        b += Bytes(20);
255        assert_eq!(b, Bytes(30));
256
257        // Sub
258        assert_eq!(MiB(50) - MiB(20), MiB(30));
259        let mut m = MiB(50);
260        m -= MiB(20);
261        assert_eq!(m, MiB(30));
262
263        // Mul (scalar)
264        assert_eq!(Bytes(10) * 5, Bytes(50));
265        assert_eq!(5 * Bytes(10), Bytes(50));
266        let mut b = Bytes(10);
267        b *= 5;
268        assert_eq!(b, Bytes(50));
269
270        // Div (scalar)
271        assert_eq!(KiB(100) / 2, KiB(50));
272        let mut k = KiB(100);
273        k /= 2;
274        assert_eq!(k, KiB(50));
275
276        // Div (self)
277        assert_eq!(Bytes(100) / Bytes(20), 5);
278
279        // Rem
280        assert_eq!(Bytes(10) % Bytes(3), Bytes(1));
281        let mut r = Bytes(10);
282        r %= Bytes(3);
283        assert_eq!(r, Bytes(1));
284    }
285
286    #[test]
287    fn test_display() {
288        assert_eq!(format!("{}", Bytes(100)), "100 B");
289        assert_eq!(format!("{}", KiB(5)), "5 KiB");
290        assert_eq!(format!("{}", MiB(10)), "10 MiB");
291        assert_eq!(format!("{}", GiB(2)), "2 GiB");
292        assert_eq!(format!("{}", TiB(3)), "3 TiB");
293        assert_eq!(format!("{}", PiB(4)), "4 PiB");
294    }
295
296    #[test]
297    fn test_comparisons() {
298        assert!(Bytes(1025) > KiB(1));
299        assert!(Bytes(1023) < KiB(1));
300        assert!(MiB(1) == KiB(1024));
301        assert!(MiB(1) > KiB(100));
302        assert!(GiB(1) > MiB(100));
303        assert!(TiB(1) > GiB(100));
304        assert!(PiB(1) > TiB(100));
305    }
306}