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