lilliput_core/value/
int.rs

1use std::{
2    hash::{Hash, Hasher},
3    num::TryFromIntError,
4};
5
6#[cfg(any(test, feature = "testing"))]
7use proptest::prelude::*;
8#[cfg(any(test, feature = "testing"))]
9use proptest_derive::Arbitrary;
10
11mod signed;
12mod unsigned;
13
14pub use self::{signed::SignedIntValue, unsigned::UnsignedIntValue};
15
16/// Represents an integer number.
17#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
18#[derive(Copy, Clone)]
19pub enum IntValue {
20    /// Signed value.
21    Signed(SignedIntValue),
22    /// Unsigned value.
23    Unsigned(UnsignedIntValue),
24}
25
26impl IntValue {
27    /// Returns `true`, if `self` is signed, otherwise `false`.
28    pub fn is_signed(&self) -> bool {
29        match self {
30            Self::Signed(_) => true,
31            Self::Unsigned(_) => false,
32        }
33    }
34}
35
36impl Default for IntValue {
37    fn default() -> Self {
38        Self::Unsigned(Default::default())
39    }
40}
41
42macro_rules! impl_int_value_from {
43    ($t:ty => $v:ident) => {
44        impl From<$t> for IntValue {
45            fn from(value: $t) -> Self {
46                Self::$v(value.into())
47            }
48        }
49    };
50}
51
52impl_int_value_from!(i8 => Signed);
53impl_int_value_from!(i16 => Signed);
54impl_int_value_from!(i32 => Signed);
55impl_int_value_from!(i64 => Signed);
56
57impl_int_value_from!(u8 => Unsigned);
58impl_int_value_from!(u16 => Unsigned);
59impl_int_value_from!(u32 => Unsigned);
60impl_int_value_from!(u64 => Unsigned);
61
62macro_rules! impl_int_value_from_size {
63    ($t:ty) => {
64        impl From<$t> for IntValue {
65            fn from(value: $t) -> Self {
66                match <$t>::BITS {
67                    8 => Self::from(value as u8),
68                    16 => Self::from(value as u16),
69                    32 => Self::from(value as u32),
70                    64 => Self::from(value as u64),
71                    _ => unimplemented!(),
72                }
73            }
74        }
75    };
76}
77
78impl_int_value_from_size!(isize);
79impl_int_value_from_size!(usize);
80
81impl PartialEq for IntValue {
82    fn eq(&self, other: &Self) -> bool {
83        match (self, other) {
84            (Self::Signed(lhs), Self::Signed(rhs)) => lhs == rhs,
85            (Self::Signed(lhs), Self::Unsigned(rhs)) => {
86                let lhs = lhs.canonicalized();
87                let rhs = rhs.canonicalized();
88
89                if lhs.is_negative() {
90                    false
91                } else {
92                    (lhs as u64) == rhs
93                }
94            }
95            (Self::Unsigned(lhs), Self::Signed(rhs)) => {
96                let lhs = lhs.canonicalized();
97                let rhs = rhs.canonicalized();
98
99                if rhs.is_negative() {
100                    false
101                } else {
102                    lhs == (rhs as u64)
103                }
104            }
105            (Self::Unsigned(lhs), Self::Unsigned(rhs)) => lhs == rhs,
106        }
107    }
108}
109
110impl PartialOrd for IntValue {
111    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
112        Some(self.cmp(other))
113    }
114}
115
116impl Eq for IntValue {}
117
118impl Ord for IntValue {
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        match (self, other) {
121            (Self::Unsigned(lhs), Self::Unsigned(rhs)) => lhs.cmp(rhs),
122            (Self::Signed(lhs), Self::Signed(rhs)) => lhs.cmp(rhs),
123            (Self::Unsigned(lhs), Self::Signed(rhs)) => {
124                let lhs = lhs.canonicalized();
125                let rhs = rhs.canonicalized();
126                if rhs.is_negative() {
127                    std::cmp::Ordering::Greater
128                } else {
129                    lhs.cmp(&(rhs as u64))
130                }
131            }
132            (Self::Signed(lhs), Self::Unsigned(rhs)) => {
133                let lhs = lhs.canonicalized();
134                let rhs = rhs.canonicalized();
135                if lhs.is_negative() {
136                    std::cmp::Ordering::Less
137                } else {
138                    (lhs as u64).cmp(&rhs)
139                }
140            }
141        }
142    }
143}
144
145impl Hash for IntValue {
146    fn hash<H: Hasher>(&self, state: &mut H) {
147        match *self {
148            Self::Unsigned(value) => {
149                let value = value.canonicalized();
150                value.to_ne_bytes().hash(state)
151            }
152            Self::Signed(value) => {
153                let value = value.canonicalized();
154                if value.is_negative() {
155                    value.to_ne_bytes().hash(state)
156                } else {
157                    (value as u64).to_ne_bytes().hash(state)
158                }
159            }
160        }
161    }
162}
163
164impl std::fmt::Debug for IntValue {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        match self {
167            Self::Signed(value) => std::fmt::Debug::fmt(&value, f),
168            Self::Unsigned(value) => std::fmt::Debug::fmt(&value, f),
169        }
170    }
171}
172
173impl std::fmt::Display for IntValue {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        match self {
176            Self::Signed(value) => std::fmt::Display::fmt(value, f),
177            Self::Unsigned(value) => std::fmt::Display::fmt(value, f),
178        }
179    }
180}
181
182#[cfg(feature = "serde")]
183impl serde::Serialize for IntValue {
184    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185    where
186        S: serde::Serializer,
187    {
188        match self {
189            Self::Signed(value) => value.serialize(serializer),
190            Self::Unsigned(value) => value.serialize(serializer),
191        }
192    }
193}
194
195#[cfg(feature = "serde")]
196impl<'de> serde::Deserialize<'de> for IntValue {
197    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
198    where
199        D: serde::Deserializer<'de>,
200    {
201        struct ValueVisitor;
202
203        impl serde::de::Visitor<'_> for ValueVisitor {
204            type Value = IntValue;
205
206            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
207                formatter.write_str("integer value")
208            }
209
210            #[inline]
211            fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E> {
212                Ok(value.into())
213            }
214
215            #[inline]
216            fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E> {
217                Ok(value.into())
218            }
219
220            #[inline]
221            fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E> {
222                Ok(value.into())
223            }
224
225            #[inline]
226            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
227                Ok(value.into())
228            }
229
230            #[inline]
231            fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E> {
232                Ok(value.into())
233            }
234
235            #[inline]
236            fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E> {
237                Ok(value.into())
238            }
239
240            #[inline]
241            fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E> {
242                Ok(value.into())
243            }
244
245            #[inline]
246            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
247                Ok(value.into())
248            }
249        }
250
251        deserializer.deserialize_any(ValueVisitor)
252    }
253}
254
255impl IntValue {
256    /// Attempts to convert the value into a signed value.
257    pub fn to_signed(self) -> Result<SignedIntValue, TryFromIntError> {
258        match self {
259            IntValue::Signed(signed) => Ok(signed),
260            IntValue::Unsigned(unsigned) => unsigned.to_signed(),
261        }
262    }
263
264    /// Attempts to convert the value into an unsigned value.
265    pub fn to_unsigned(self) -> Result<UnsignedIntValue, TryFromIntError> {
266        match self {
267            IntValue::Signed(signed) => signed.to_unsigned(),
268            IntValue::Unsigned(unsigned) => Ok(unsigned),
269        }
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use std::{cmp::Ordering, hash::RandomState};
276
277    use proptest::prelude::*;
278    use test_log::test;
279
280    use crate::{
281        config::EncoderConfig,
282        decoder::Decoder,
283        encoder::Encoder,
284        io::{SliceReader, VecWriter},
285        value::Value,
286    };
287
288    use super::*;
289
290    proptest! {
291        #[test]
292        fn eq(signed in i8::MIN..=i8::MAX, unsigned in u8::MIN..=u8::MAX) {
293            let signed_values = [
294                IntValue::from(signed),
295                IntValue::from(signed as i16),
296                IntValue::from(signed as i32),
297                IntValue::from(signed as i64),
298            ];
299
300            let unsigned_values = [
301                IntValue::from(unsigned),
302                IntValue::from(unsigned as u16),
303                IntValue::from(unsigned as u32),
304                IntValue::from(unsigned as u64),
305            ];
306
307            // signed vs signed
308            for lhs_value in &signed_values {
309                for rhs_value in &signed_values {
310                    prop_assert_eq!(lhs_value, rhs_value);
311                    prop_assert_eq!(rhs_value, lhs_value);
312                }
313            }
314
315            // unsigned vs unsigned
316            for lhs_value in &unsigned_values {
317                for rhs_value in &unsigned_values {
318                    prop_assert_eq!(lhs_value, rhs_value);
319                    prop_assert_eq!(rhs_value, lhs_value);
320                }
321            }
322
323            // signed vs unsigned
324            for lhs_value in &signed_values {
325                for rhs_value in &unsigned_values {
326                    if u8::try_from(signed) == Ok(unsigned) {
327                        prop_assert_eq!(lhs_value, rhs_value);
328                        prop_assert_eq!(rhs_value, lhs_value);
329                    } else {
330                        prop_assert_ne!(lhs_value, rhs_value);
331                        prop_assert_ne!(rhs_value, lhs_value);
332                    }
333                }
334            }
335        }
336
337        #[test]
338        fn ord(signed in i8::MIN..=i8::MAX, unsigned in u8::MIN..=u8::MAX) {
339            let signed_values = [
340                IntValue::from(signed),
341                IntValue::from(signed as i16),
342                IntValue::from(signed as i32),
343                IntValue::from(signed as i64),
344            ];
345
346            let unsigned_values = [
347                IntValue::from(unsigned),
348                IntValue::from(unsigned as u16),
349                IntValue::from(unsigned as u32),
350                IntValue::from(unsigned as u64),
351            ];
352
353            // signed vs signed
354            for lhs_value in &signed_values {
355                for rhs_value in &signed_values {
356                    let value_ordering = lhs_value.cmp(rhs_value);
357                    prop_assert_eq!(value_ordering, Ordering::Equal);
358                }
359            }
360
361            // unsigned vs unsigned
362            for lhs_value in &unsigned_values {
363                for rhs_value in &unsigned_values {
364                    let value_ordering = lhs_value.cmp(rhs_value);
365                    prop_assert_eq!(value_ordering, Ordering::Equal);
366                }
367            }
368
369            // signed vs unsigned
370            for lhs_value in &signed_values {
371                for rhs_value in &unsigned_values {
372                    let value_ordering = lhs_value.cmp(rhs_value);
373
374                    if let Ok(positive) = u8::try_from(signed) {
375                        let int_ordering = positive.cmp(&unsigned);
376                        prop_assert_eq!(value_ordering, int_ordering);
377                    } else {
378                        prop_assert_eq!(value_ordering, Ordering::Less);
379                    }
380                }
381            }
382        }
383
384        #[test]
385        fn hash(signed in i8::MIN..=i8::MAX, unsigned in u8::MIN..=u8::MAX) {
386            use std::hash::BuildHasher as _;
387
388            let signed_values = [
389                IntValue::from(signed),
390                IntValue::from(signed as i16),
391                IntValue::from(signed as i32),
392                IntValue::from(signed as i64),
393            ];
394
395            let unsigned_values = [
396                IntValue::from(unsigned),
397                IntValue::from(unsigned as u16),
398                IntValue::from(unsigned as u32),
399                IntValue::from(unsigned as u64),
400            ];
401
402            // signed vs signed
403            for lhs_value in &signed_values {
404                for rhs_value in &signed_values {
405                    let build_hasher = RandomState::new();
406                    let lhs_hash = build_hasher.hash_one(lhs_value);
407                    let rhs_hash = build_hasher.hash_one(rhs_value);
408                    prop_assert_eq!(lhs_hash, rhs_hash);
409                }
410            }
411
412            // unsigned vs unsigned
413            for lhs_value in &unsigned_values {
414                for rhs_value in &unsigned_values {
415                    let build_hasher = RandomState::new();
416                    let lhs_hash = build_hasher.hash_one(lhs_value);
417                    let rhs_hash = build_hasher.hash_one(rhs_value);
418                    prop_assert_eq!(lhs_hash, rhs_hash);
419                }
420            }
421        }
422    }
423
424    #[test]
425    fn display() {
426        assert_eq!(format!("{}", IntValue::from(42_u8)), "42");
427        assert_eq!(format!("{}", IntValue::from(42_u16)), "42");
428        assert_eq!(format!("{}", IntValue::from(42_u32)), "42");
429        assert_eq!(format!("{}", IntValue::from(42_u64)), "42");
430
431        assert_eq!(format!("{}", IntValue::from(42_i8)), "42");
432        assert_eq!(format!("{}", IntValue::from(42_i16)), "42");
433        assert_eq!(format!("{}", IntValue::from(42_i32)), "42");
434        assert_eq!(format!("{}", IntValue::from(42_i64)), "42");
435    }
436
437    #[test]
438    fn debug() {
439        assert_eq!(format!("{:?}", IntValue::from(42_u8)), "42");
440        assert_eq!(format!("{:?}", IntValue::from(42_u16)), "42");
441        assert_eq!(format!("{:?}", IntValue::from(42_u32)), "42");
442        assert_eq!(format!("{:?}", IntValue::from(42_u64)), "42");
443
444        assert_eq!(format!("{:?}", IntValue::from(42_i8)), "42");
445        assert_eq!(format!("{:?}", IntValue::from(42_i16)), "42");
446        assert_eq!(format!("{:?}", IntValue::from(42_i32)), "42");
447        assert_eq!(format!("{:?}", IntValue::from(42_i64)), "42");
448
449        assert_eq!(format!("{:#?}", IntValue::from(42_u8)), "42_u8");
450        assert_eq!(format!("{:#?}", IntValue::from(42_u16)), "42_u16");
451        assert_eq!(format!("{:#?}", IntValue::from(42_u32)), "42_u32");
452        assert_eq!(format!("{:#?}", IntValue::from(42_u64)), "42_u64");
453
454        assert_eq!(format!("{:#?}", IntValue::from(42_i8)), "42_i8");
455        assert_eq!(format!("{:#?}", IntValue::from(42_i16)), "42_i16");
456        assert_eq!(format!("{:#?}", IntValue::from(42_i32)), "42_i32");
457        assert_eq!(format!("{:#?}", IntValue::from(42_i64)), "42_i64");
458    }
459
460    proptest! {
461        #[test]
462        fn encode_decode_roundtrip(value in IntValue::arbitrary(), config in EncoderConfig::arbitrary()) {
463            let mut encoded: Vec<u8> = Vec::new();
464            let writer = VecWriter::new(&mut encoded);
465            let mut encoder = Encoder::new(writer, config);
466            encoder.encode_int_value(&value).unwrap();
467
468            prop_assert!(encoded.len() <= 1 + 8);
469
470            let reader = SliceReader::new(&encoded);
471            let mut decoder = Decoder::from_reader(reader);
472            let decoded = decoder.decode_int_value().unwrap();
473            prop_assert_eq!(&decoded, &value);
474
475            let reader = SliceReader::new(&encoded);
476            let mut decoder = Decoder::from_reader(reader);
477            let decoded = decoder.decode_value().unwrap();
478            let Value::Int(decoded) = decoded else {
479                panic!("expected int value");
480            };
481            prop_assert_eq!(&decoded, &value);
482        }
483    }
484}