lilliput_core/value/int/
signed.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
11use crate::num::{TryFromInt, TryIntoInt as _};
12
13use super::UnsignedIntValue;
14
15/// Represents a signed integer number.
16#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
17#[derive(Copy, Clone)]
18pub enum SignedIntValue {
19    /// 8-bit value.
20    I8(i8),
21    /// 16-bit value.
22    I16(i16),
23    /// 32-bit value.
24    I32(i32),
25    /// 64-bit value.
26    I64(i64),
27}
28
29impl Default for SignedIntValue {
30    fn default() -> Self {
31        Self::I8(0)
32    }
33}
34
35macro_rules! impl_signed_int_value_from {
36    ($t:ty => $v:ident) => {
37        impl From<$t> for SignedIntValue {
38            fn from(value: $t) -> Self {
39                Self::$v(value)
40            }
41        }
42    };
43}
44
45impl_signed_int_value_from!(i8 => I8);
46impl_signed_int_value_from!(i16 => I16);
47impl_signed_int_value_from!(i32 => I32);
48impl_signed_int_value_from!(i64 => I64);
49
50macro_rules! impl_try_from_signed_int_value {
51    ($t:ty) => {
52        impl TryFrom<SignedIntValue> for $t {
53            type Error = std::num::TryFromIntError;
54
55            fn try_from(value: SignedIntValue) -> Result<Self, Self::Error> {
56                match value {
57                    SignedIntValue::I8(value) => value.try_into_int(),
58                    SignedIntValue::I16(value) => value.try_into_int(),
59                    SignedIntValue::I32(value) => value.try_into_int(),
60                    SignedIntValue::I64(value) => value.try_into_int(),
61                }
62            }
63        }
64    };
65}
66
67impl_try_from_signed_int_value!(i8);
68impl_try_from_signed_int_value!(i16);
69impl_try_from_signed_int_value!(i32);
70impl_try_from_signed_int_value!(i64);
71impl_try_from_signed_int_value!(isize);
72
73impl PartialEq for SignedIntValue {
74    fn eq(&self, other: &Self) -> bool {
75        let lhs = match *self {
76            Self::I8(value) => value as i64,
77            Self::I16(value) => value as i64,
78            Self::I32(value) => value as i64,
79            Self::I64(value) => value,
80        };
81        let rhs = match *other {
82            Self::I8(value) => value as i64,
83            Self::I16(value) => value as i64,
84            Self::I32(value) => value as i64,
85            Self::I64(value) => value,
86        };
87        lhs == rhs
88    }
89}
90
91impl PartialOrd for SignedIntValue {
92    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
93        Some(self.cmp(other))
94    }
95}
96
97impl Eq for SignedIntValue {}
98
99impl Ord for SignedIntValue {
100    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
101        self.canonicalized().cmp(&other.canonicalized())
102    }
103}
104
105impl Hash for SignedIntValue {
106    fn hash<H: Hasher>(&self, state: &mut H) {
107        self.canonicalized().hash(state)
108    }
109}
110
111impl std::fmt::Debug for SignedIntValue {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        if f.alternate() {
114            match self {
115                Self::I8(value) => write!(f, "{value:#?}_i8"),
116                Self::I16(value) => write!(f, "{value:#?}_i16"),
117                Self::I32(value) => write!(f, "{value:#?}_i32"),
118                Self::I64(value) => write!(f, "{value:#?}_i64"),
119            }
120        } else {
121            match self {
122                Self::I8(value) => std::fmt::Debug::fmt(value, f),
123                Self::I16(value) => std::fmt::Debug::fmt(value, f),
124                Self::I32(value) => std::fmt::Debug::fmt(value, f),
125                Self::I64(value) => std::fmt::Debug::fmt(value, f),
126            }
127        }
128    }
129}
130
131impl std::fmt::Display for SignedIntValue {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        match self {
134            Self::I8(value) => std::fmt::Display::fmt(value, f),
135            Self::I16(value) => std::fmt::Display::fmt(value, f),
136            Self::I32(value) => std::fmt::Display::fmt(value, f),
137            Self::I64(value) => std::fmt::Display::fmt(value, f),
138        }
139    }
140}
141
142#[cfg(feature = "serde")]
143impl serde::Serialize for SignedIntValue {
144    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
145    where
146        S: serde::Serializer,
147    {
148        match self {
149            Self::I8(value) => value.serialize(serializer),
150            Self::I16(value) => value.serialize(serializer),
151            Self::I32(value) => value.serialize(serializer),
152            Self::I64(value) => value.serialize(serializer),
153        }
154    }
155}
156
157#[cfg(feature = "serde")]
158impl<'de> serde::Deserialize<'de> for SignedIntValue {
159    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160    where
161        D: serde::Deserializer<'de>,
162    {
163        struct ValueVisitor;
164
165        impl serde::de::Visitor<'_> for ValueVisitor {
166            type Value = SignedIntValue;
167
168            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
169                formatter.write_str("signed integer value")
170            }
171
172            #[inline]
173            fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E> {
174                Ok(value.into())
175            }
176
177            #[inline]
178            fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E> {
179                Ok(value.into())
180            }
181
182            #[inline]
183            fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E> {
184                Ok(value.into())
185            }
186
187            #[inline]
188            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
189                Ok(value.into())
190            }
191        }
192
193        deserializer.deserialize_any(ValueVisitor)
194    }
195}
196
197impl SignedIntValue {
198    /// Attempts to convert the value into an unsigned value.
199    pub fn to_unsigned(self) -> Result<UnsignedIntValue, TryFromIntError> {
200        match self {
201            Self::I8(signed) => u8::try_from_int(signed).map(UnsignedIntValue::U8),
202            Self::I16(signed) => u16::try_from_int(signed).map(UnsignedIntValue::U16),
203            Self::I32(signed) => u32::try_from_int(signed).map(UnsignedIntValue::U32),
204            Self::I64(signed) => u64::try_from_int(signed).map(UnsignedIntValue::U64),
205        }
206    }
207
208    pub(crate) fn canonicalized(&self) -> i64 {
209        match *self {
210            Self::I8(value) => value as i64,
211            Self::I16(value) => value as i64,
212            Self::I32(value) => value as i64,
213            Self::I64(value) => value,
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use std::hash::RandomState;
221
222    use proptest::prelude::*;
223    use test_log::test;
224
225    use crate::{
226        config::EncoderConfig,
227        decoder::Decoder,
228        encoder::Encoder,
229        io::{SliceReader, VecWriter},
230        value::{IntValue, Value},
231    };
232
233    use super::*;
234
235    proptest! {
236        #[test]
237        fn eq(lhs in i8::MIN..=i8::MAX, rhs in i8::MIN..=i8::MAX) {
238
239            let lhs_values = [
240                SignedIntValue::I8(lhs),
241                SignedIntValue::I16(lhs as i16),
242                SignedIntValue::I32(lhs as i32),
243                SignedIntValue::I64(lhs as i64),
244            ];
245
246            let rhs_values = [
247                SignedIntValue::I8(rhs),
248                SignedIntValue::I16(rhs as i16),
249                SignedIntValue::I32(rhs as i32),
250                SignedIntValue::I64(rhs as i64),
251            ];
252
253            for lhs_value in &lhs_values {
254                for rhs_value in &rhs_values {
255                    let int_is_equal = lhs.eq(&rhs);
256                    prop_assert_eq!(lhs_value.eq(rhs_value), int_is_equal);
257                    prop_assert_eq!(rhs_value.eq(lhs_value), int_is_equal);
258                }
259            }
260        }
261
262        #[test]
263        fn ord(lhs in i8::MIN..=i8::MAX, rhs in i8::MIN..=i8::MAX) {
264            let lhs_values = [
265                SignedIntValue::I8(lhs),
266                SignedIntValue::I16(lhs as i16),
267                SignedIntValue::I32(lhs as i32),
268                SignedIntValue::I64(lhs as i64),
269            ];
270
271            let rhs_values = [
272                SignedIntValue::I8(rhs),
273                SignedIntValue::I16(rhs as i16),
274                SignedIntValue::I32(rhs as i32),
275                SignedIntValue::I64(rhs as i64),
276            ];
277
278            for lhs_value in &lhs_values {
279                for rhs_value in &rhs_values {
280                    let int_ordering = lhs.cmp(&rhs);
281                    prop_assert_eq!(lhs_value.cmp(rhs_value), int_ordering);
282                    prop_assert_eq!(rhs_value.cmp(lhs_value), int_ordering.reverse());
283                }
284            }
285        }
286
287        #[test]
288        fn hash(lhs in i8::MIN..=i8::MAX) {
289            use std::hash::BuildHasher as _;
290
291            let values = [
292                SignedIntValue::I8(lhs),
293                SignedIntValue::I16(lhs as i16),
294                SignedIntValue::I32(lhs as i32),
295                SignedIntValue::I64(lhs as i64),
296            ];
297
298            for lhs_value in &values {
299                for rhs_value in &values {
300                    let build_hasher = RandomState::new();
301                    let lhs_hash = build_hasher.hash_one(lhs_value);
302                    let rhs_hash = build_hasher.hash_one(rhs_value);
303                    prop_assert_eq!(lhs_hash, rhs_hash);
304                }
305            }
306        }
307    }
308
309    #[test]
310    fn display() {
311        assert_eq!(format!("{}", SignedIntValue::from(42_i8)), "42");
312        assert_eq!(format!("{}", SignedIntValue::from(42_i16)), "42");
313        assert_eq!(format!("{}", SignedIntValue::from(42_i32)), "42");
314        assert_eq!(format!("{}", SignedIntValue::from(42_i64)), "42");
315    }
316
317    #[test]
318    fn debug() {
319        assert_eq!(format!("{:?}", SignedIntValue::from(42_i8)), "42");
320        assert_eq!(format!("{:?}", SignedIntValue::from(42_i16)), "42");
321        assert_eq!(format!("{:?}", SignedIntValue::from(42_i32)), "42");
322        assert_eq!(format!("{:?}", SignedIntValue::from(42_i64)), "42");
323
324        assert_eq!(format!("{:#?}", SignedIntValue::from(42_i8)), "42_i8");
325        assert_eq!(format!("{:#?}", SignedIntValue::from(42_i16)), "42_i16");
326        assert_eq!(format!("{:#?}", SignedIntValue::from(42_i32)), "42_i32");
327        assert_eq!(format!("{:#?}", SignedIntValue::from(42_i64)), "42_i64");
328    }
329
330    proptest! {
331        #[test]
332        fn encode_decode_roundtrip(value in SignedIntValue::arbitrary(), config in EncoderConfig::arbitrary()) {
333            let mut encoded: Vec<u8> = Vec::new();
334            let writer = VecWriter::new(&mut encoded);
335            let mut encoder = Encoder::new(writer, config);
336            encoder.encode_signed_int_value(&value).unwrap();
337
338            prop_assert!(encoded.len() <= 1 + 8);
339
340            let reader = SliceReader::new(&encoded);
341            let mut decoder = Decoder::from_reader(reader);
342            let decoded = decoder.decode_signed_int_value().unwrap();
343            prop_assert_eq!(&decoded, &value);
344
345            let reader = SliceReader::new(&encoded);
346            let mut decoder = Decoder::from_reader(reader);
347            let decoded = decoder.decode_value().unwrap();
348            let Value::Int(decoded) = decoded else {
349                panic!("expected int value");
350            };
351            prop_assert_eq!(&decoded, &IntValue::Signed(value));
352        }
353    }
354}