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#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
17#[derive(Copy, Clone)]
18pub enum UnsignedIntValue {
19 U8(u8),
21 U16(u16),
23 U32(u32),
25 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 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}