1use crate::Bytes;
5use crate::impl_comparison;
6use core::fmt;
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
8
9pub const KIB: u64 = 1024;
11pub const MIB: u64 = 1024 * KIB;
13pub const GIB: u64 = 1024 * MIB;
15pub const TIB: u64 = 1024 * GIB;
17pub 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
146impl 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 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 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 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 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 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 assert_eq!(KiB(100) / 2, KiB(50));
272 let mut k = KiB(100);
273 k /= 2;
274 assert_eq!(k, KiB(50));
275
276 assert_eq!(Bytes(100) / Bytes(20), 5);
278
279 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}