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#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
17#[derive(Copy, Clone)]
18pub enum SignedIntValue {
19 I8(i8),
21 I16(i16),
23 I32(i32),
25 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 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}