fog_pack/
integer.rs

1use std::cmp;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug, Display, LowerHex, UpperHex};
4use std::ops;
5
6#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
7pub(crate) enum IntPriv {
8    /// Always non-less than zero.
9    PosInt(u64),
10    /// Always less than zero.
11    NegInt(i64),
12}
13
14/// Represents a fog-pack integer, whether signed or unsigned.
15///
16/// A `Value` or `ValueRef` that contains integer can be constructed using `From` trait.
17#[derive(Copy, Clone, PartialEq, Eq, Hash)]
18pub struct Integer {
19    n: IntPriv,
20}
21
22impl Integer {
23    /// Minimum possible integer that can be represented. Equivalent to `i64::min_value()`.
24    pub fn min_value() -> Integer {
25        Integer {
26            n: IntPriv::NegInt(i64::min_value()),
27        }
28    }
29
30    /// Maximum possible integer that can be represented. Equivalent to `u64::max_value()`.
31    pub fn max_value() -> Integer {
32        Integer {
33            n: IntPriv::PosInt(u64::max_value()),
34        }
35    }
36
37    /// Returns `true` if the integer can be represented as `i64`.
38    #[inline]
39    pub fn is_i64(&self) -> bool {
40        match self.n {
41            IntPriv::PosInt(n) => n <= ::std::i64::MAX as u64,
42            IntPriv::NegInt(..) => true,
43        }
44    }
45
46    /// Returns `true` if the integer can be represented as `u64`.
47    #[inline]
48    pub fn is_u64(&self) -> bool {
49        match self.n {
50            IntPriv::PosInt(..) => true,
51            IntPriv::NegInt(..) => false,
52        }
53    }
54
55    /// Returns the integer represented as `i64` if possible, or else `None`.
56    #[inline]
57    pub fn as_i64(&self) -> Option<i64> {
58        match self.n {
59            IntPriv::PosInt(n) => i64::try_from(n).ok(),
60            IntPriv::NegInt(n) => Some(n),
61        }
62    }
63
64    /// Returns the integer represented as `u64` if possible, or else `None`.
65    #[inline]
66    pub fn as_u64(&self) -> Option<u64> {
67        match self.n {
68            IntPriv::PosInt(n) => Some(n),
69            IntPriv::NegInt(n) => u64::try_from(n).ok(),
70        }
71    }
72
73    /// Forcibly casts the value to u64 without modification.
74    #[inline]
75    pub fn as_bits(&self) -> u64 {
76        match self.n {
77            IntPriv::PosInt(n) => n,
78            IntPriv::NegInt(n) => n as u64,
79        }
80    }
81}
82
83pub(crate) fn get_int_internal(val: &Integer) -> IntPriv {
84    val.n
85}
86
87impl std::default::Default for Integer {
88    fn default() -> Self {
89        Self {
90            n: IntPriv::PosInt(0),
91        }
92    }
93}
94
95impl cmp::Ord for Integer {
96    fn cmp(&self, other: &Integer) -> Ordering {
97        match (self.n, other.n) {
98            (IntPriv::NegInt(lhs), IntPriv::NegInt(ref rhs)) => lhs.cmp(rhs),
99            (IntPriv::NegInt(_), IntPriv::PosInt(_)) => Ordering::Less,
100            (IntPriv::PosInt(_), IntPriv::NegInt(_)) => Ordering::Greater,
101            (IntPriv::PosInt(lhs), IntPriv::PosInt(ref rhs)) => lhs.cmp(rhs),
102        }
103    }
104}
105
106impl cmp::PartialOrd for Integer {
107    fn partial_cmp(&self, other: &Integer) -> Option<Ordering> {
108        Some(self.cmp(other))
109    }
110}
111
112impl ops::Add<i64> for Integer {
113    type Output = Integer;
114
115    fn add(self, other: i64) -> Integer {
116        match self.n {
117            IntPriv::PosInt(lhs) => {
118                if other >= 0 {
119                    Integer::from(lhs + (other as u64))
120                } else if lhs >= (1u64 << 63) {
121                    Integer::from(lhs.wrapping_add(other as u64))
122                } else {
123                    Integer::from((lhs as i64) + other)
124                }
125            }
126            IntPriv::NegInt(lhs) => Integer::from(lhs + other),
127        }
128    }
129}
130
131impl ops::Sub<i64> for Integer {
132    type Output = Integer;
133
134    fn sub(self, other: i64) -> Integer {
135        match self.n {
136            IntPriv::PosInt(lhs) => {
137                if other < 0 {
138                    Integer::from(lhs.wrapping_sub(other as u64))
139                } else if lhs >= (1u64 << 63) {
140                    Integer::from(lhs - (other as u64))
141                } else {
142                    Integer::from((lhs as i64) - other)
143                }
144            }
145            IntPriv::NegInt(lhs) => Integer::from(lhs - other),
146        }
147    }
148}
149
150impl Debug for Integer {
151    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
152        Debug::fmt(&self.n, fmt)
153    }
154}
155
156impl Display for Integer {
157    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
158        match self.n {
159            IntPriv::PosInt(v) => Display::fmt(&v, fmt),
160            IntPriv::NegInt(v) => Display::fmt(&v, fmt),
161        }
162    }
163}
164
165impl UpperHex for Integer {
166    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
167        UpperHex::fmt(&self.as_bits(), fmt)
168    }
169}
170
171impl LowerHex for Integer {
172    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
173        LowerHex::fmt(&self.as_bits(), fmt)
174    }
175}
176
177macro_rules! impl_from_unsigned {
178    ($t: ty) => {
179        impl From<$t> for Integer {
180            fn from(n: $t) -> Self {
181                Integer {
182                    n: IntPriv::PosInt(n as u64),
183                }
184            }
185        }
186    };
187}
188
189macro_rules! impl_from_signed {
190    ($t: ty) => {
191        impl From<$t> for Integer {
192            fn from(n: $t) -> Self {
193                if n < 0 {
194                    Integer {
195                        n: IntPriv::NegInt(n as i64),
196                    }
197                } else {
198                    Integer {
199                        n: IntPriv::PosInt(n as u64),
200                    }
201                }
202            }
203        }
204    };
205}
206
207impl_from_unsigned!(u8);
208impl_from_unsigned!(u16);
209impl_from_unsigned!(u32);
210impl_from_unsigned!(u64);
211impl_from_unsigned!(usize);
212impl_from_signed!(i8);
213impl_from_signed!(i16);
214impl_from_signed!(i32);
215impl_from_signed!(i64);
216impl_from_signed!(isize);
217
218use std::convert::TryFrom;
219
220macro_rules! impl_try_from {
221    ($t: ty) => {
222        impl TryFrom<Integer> for $t {
223            type Error = Integer;
224            fn try_from(v: Integer) -> Result<Self, Self::Error> {
225                match v.n {
226                    IntPriv::PosInt(n) => TryFrom::try_from(n).map_err(|_| v),
227                    IntPriv::NegInt(n) => TryFrom::try_from(n).map_err(|_| v),
228                }
229            }
230        }
231    };
232}
233
234impl_try_from!(u8);
235impl_try_from!(u16);
236impl_try_from!(u32);
237impl_try_from!(u64);
238impl_try_from!(usize);
239impl_try_from!(i8);
240impl_try_from!(i16);
241impl_try_from!(i32);
242impl_try_from!(i64);
243impl_try_from!(isize);
244
245use serde::{
246    de::{Deserialize, Deserializer},
247    ser::{Serialize, Serializer},
248};
249
250impl Serialize for Integer {
251    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
252        match self.n {
253            IntPriv::PosInt(v) => serializer.serialize_u64(v),
254            IntPriv::NegInt(v) => serializer.serialize_i64(v),
255        }
256    }
257}
258
259impl<'de> Deserialize<'de> for Integer {
260    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261    where
262        D: Deserializer<'de>,
263    {
264        struct IntVisitor;
265        impl<'de> serde::de::Visitor<'de> for IntVisitor {
266            type Value = Integer;
267
268            fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
269                write!(fmt, "an integer")
270            }
271
272            fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
273                Ok(Integer::from(v))
274            }
275
276            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
277                Ok(Integer::from(v))
278            }
279        }
280
281        deserializer.deserialize_any(IntVisitor)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn add() {
291        let x = Integer::min_value();
292        let y = i64::max_value();
293        assert_eq!(x + y, Integer::from(-1));
294        let y = 1i64;
295        assert_eq!(x + y, Integer::from(i64::min_value() + 1));
296        let x = Integer::from(1u64 << 63);
297        assert_eq!(x + y, Integer::from((1u64 << 63) + 1));
298        let x = Integer::from((1u64 << 63) - 1);
299        assert_eq!(x + y, Integer::from(1u64 << 63));
300
301        let x = Integer::max_value();
302        let y = i64::min_value();
303        assert_eq!(x + y, Integer::from(u64::max_value() >> 1));
304        let y = -1i64;
305        assert_eq!(x + y, Integer::from(u64::max_value() - 1));
306        let x = Integer::from(1u64 << 63);
307        assert_eq!(x + y, Integer::from((1u64 << 63) - 1));
308        let x = Integer::from((1u64 << 63) - 1);
309        assert_eq!(x + y, Integer::from((1u64 << 63) - 2));
310    }
311
312    #[test]
313    fn sub() {
314        let x = Integer::min_value();
315        let y = i64::min_value();
316        assert_eq!(x - y, Integer::from(0));
317        let y = -1i64;
318        assert_eq!(x - y, Integer::from(i64::min_value() + 1));
319        let x = Integer::from(1u64 << 63);
320        assert_eq!(x - y, Integer::from((1u64 << 63) + 1));
321        let x = Integer::from((1u64 << 63) - 1);
322        assert_eq!(x - y, Integer::from(1u64 << 63));
323
324        let x = Integer::max_value();
325        let y = i64::max_value();
326        assert_eq!(x - y, Integer::from(1u64 << 63));
327        let y = 1i64;
328        assert_eq!(x - y, Integer::from(u64::max_value() - 1));
329        let x = Integer::from(1u64 << 63);
330        assert_eq!(x - y, Integer::from((1u64 << 63) - 1));
331        let x = Integer::from((1u64 << 63) - 1);
332        assert_eq!(x - y, Integer::from((1u64 << 63) - 2));
333    }
334}