ubyte/
arithmetic.rs

1use core::cmp::Ordering;
2use core::ops::{Add, Sub, Mul, Div, Rem, Shl, Shr};
3use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign, ShlAssign, ShrAssign};
4
5use crate::ByteUnit;
6
7impl<T: Into<ByteUnit>> Add<T> for ByteUnit {
8    type Output = Self;
9
10    #[inline(always)]
11    fn add(self, rhs: T) -> Self::Output {
12        ByteUnit(self.0.saturating_add(rhs.into().0))
13    }
14}
15
16impl<T: Into<ByteUnit>> Sub<T> for ByteUnit {
17    type Output = Self;
18
19    #[inline(always)]
20    fn sub(self, rhs: T) -> Self::Output {
21        ByteUnit(self.0.saturating_sub(rhs.into().0))
22    }
23}
24
25impl<T: Into<ByteUnit>> Mul<T> for ByteUnit {
26    type Output = Self;
27
28    #[inline(always)]
29    fn mul(self, rhs: T) -> Self::Output {
30        ByteUnit(self.0.saturating_mul(rhs.into().0))
31    }
32}
33
34impl<T: Into<ByteUnit>> Div<T> for ByteUnit {
35    type Output = Self;
36
37    #[inline(always)]
38    fn div(self, rhs: T) -> Self::Output {
39        let value = rhs.into().0;
40        match value {
41            0 => ByteUnit::max_value(),
42            _ => ByteUnit(self.0 / value)
43        }
44    }
45}
46
47impl<T: Into<ByteUnit>> Rem<T> for ByteUnit {
48    type Output = Self;
49
50    #[inline(always)]
51    fn rem(self, rhs: T) -> Self::Output {
52        let value = rhs.into().0;
53        match value {
54            0 => ByteUnit(0),
55            _ => ByteUnit(self.0 % value)
56        }
57    }
58}
59
60impl<T: Into<ByteUnit>> Shl<T> for ByteUnit {
61    type Output = Self;
62    fn shl(self, rhs: T) -> Self::Output {
63        let wanted = rhs.into().0;
64        let available = self.0.leading_zeros() as u64;
65        if wanted > available {
66            ByteUnit::max_value()
67        } else {
68            ByteUnit(self.0 << wanted)
69        }
70    }
71}
72
73impl<T: Into<ByteUnit>> Shr<T> for ByteUnit {
74    type Output = Self;
75    fn shr(self, rhs: T) -> Self::Output {
76        ByteUnit(self.0 >> rhs.into().0)
77    }
78}
79
80impl<T: Into<ByteUnit> + Copy> PartialEq<T> for ByteUnit {
81    fn eq(&self, other: &T) -> bool {
82        self.0 == (*other).into().0
83    }
84}
85
86impl<T: Into<ByteUnit> + Copy> PartialOrd<T> for ByteUnit {
87    fn partial_cmp(&self, other: &T) -> Option<Ordering> {
88        self.0.partial_cmp(&(*other).into().0)
89    }
90}
91
92macro_rules! impl_self_assign_op {
93    ($Trait:ident, $func:ident, $op:tt) => (
94        impl<T: Into<ByteUnit>> $Trait<T> for ByteUnit {
95            #[inline(always)]
96            fn $func(&mut self, rhs: T) {
97                *self = *self $op rhs.into();
98            }
99        }
100    )
101}
102
103impl_self_assign_op!(AddAssign, add_assign, +);
104impl_self_assign_op!(SubAssign, sub_assign, -);
105impl_self_assign_op!(MulAssign, mul_assign, *);
106impl_self_assign_op!(DivAssign, div_assign, /);
107impl_self_assign_op!(RemAssign, rem_assign, %);
108impl_self_assign_op!(ShrAssign, shr_assign, >>);
109impl_self_assign_op!(ShlAssign, shl_assign, <<);
110
111macro_rules! impl_arith_op_on_core_type {
112    ($T:ident, $Trait:ident, $func:ident, $op:tt) => (
113        impl $Trait<ByteUnit> for $T {
114            type Output = ByteUnit;
115
116            #[inline(always)]
117            fn $func(self, rhs: ByteUnit) -> Self::Output {
118                ByteUnit::from(self) $op rhs
119            }
120        }
121    )
122}
123
124macro_rules! impl_arith_ops_on_core {
125    ($T:ident) => (
126        impl_arith_op_on_core_type!($T, Add, add, +);
127        impl_arith_op_on_core_type!($T, Sub, sub, -);
128        impl_arith_op_on_core_type!($T, Mul, mul, *);
129        impl_arith_op_on_core_type!($T, Div, div, /);
130        impl_arith_op_on_core_type!($T, Rem, rem, %);
131        impl_arith_op_on_core_type!($T, Shl, shl, <<);
132        impl_arith_op_on_core_type!($T, Shr, shr, >>);
133
134        impl PartialEq<ByteUnit> for $T {
135            #[inline(always)]
136            fn eq(&self, other: &ByteUnit) -> bool {
137                ByteUnit::from(*self).eq(other)
138            }
139        }
140
141        impl PartialOrd<ByteUnit> for $T {
142            #[inline(always)]
143            fn partial_cmp(&self, other: &ByteUnit) -> Option<Ordering> {
144                ByteUnit::from(*self).partial_cmp(other)
145            }
146        }
147    )
148}
149
150impl_arith_ops_on_core!(usize);
151impl_arith_ops_on_core!(u8);
152impl_arith_ops_on_core!(u16);
153impl_arith_ops_on_core!(u32);
154impl_arith_ops_on_core!(u64);
155impl_arith_ops_on_core!(u128);
156
157impl_arith_ops_on_core!(isize);
158impl_arith_ops_on_core!(i8);
159impl_arith_ops_on_core!(i16);
160impl_arith_ops_on_core!(i32);
161impl_arith_ops_on_core!(i64);
162impl_arith_ops_on_core!(i128);
163
164#[cfg(test)]
165mod tests {
166    use crate::{ByteUnit, ToByteUnit};
167
168    #[test]
169    fn test_saturation() {
170        assert_eq!(ByteUnit::B * -1, 0);
171        assert_eq!(ByteUnit::B * -3, 0);
172        assert_eq!(ByteUnit::B / -3, ByteUnit::max_value());
173        assert_eq!(ByteUnit::B - 100, 0);
174        assert_eq!((100 * ByteUnit::B) % -10, 0);
175
176        // These are suprising, but ~correct. Should we document?
177        assert_eq!(ByteUnit::B + (-100i32), 1);
178        assert_eq!(-100 + ByteUnit::B, 1);
179    }
180
181    #[test]
182    fn test_core_types_operations() {
183        assert_eq!(1000 - 300.bytes(), 700);
184        assert_eq!(1024 >> 2.bytes(), 256);
185        assert_eq!(2 << 2.bytes(), 8);
186        assert_eq!(2048 / 4.bytes(), 512);
187        assert!((500 + 700) < 2.mebibytes());
188        assert!((500 + 700) > 2.bytes());
189    }
190
191    #[test]
192    fn test_add_assign_op() {
193        let mut b = 0.bytes();
194        b += 10;
195        assert_eq!(b, 10);
196
197        let mut b = 10.bytes();
198        b *= 100.kibibytes();
199        assert_eq!(b, 1024.kilobytes());
200    }
201}