1 // SPDX-License-Identifier: Apache-2.0
2 use core::cmp::Ordering;
3 
4 macro_rules! implfrom {
5     ($( $(#[$($attr:meta)+])? $t:ident)+) => {
6         $(
7             $(#[$($attr)+])?
8             impl From<$t> for Integer {
9                 #[inline]
10                 fn from(value: $t) -> Self {
11                     Self(value as _)
12                 }
13             }
14 
15             impl TryFrom<Integer> for $t {
16                 type Error = core::num::TryFromIntError;
17 
18                 #[inline]
19                 fn try_from(value: Integer) -> Result<Self, Self::Error> {
20                     $t::try_from(value.0)
21                 }
22             }
23         )+
24     };
25 }
26 
27 /// An abstract integer value
28 ///
29 /// This opaque type represents an integer value which can be encoded in CBOR
30 /// without resulting to big integer encoding. Larger values may be encoded
31 /// using the big integer encoding as described in the CBOR RFC. See the
32 /// implementations for 128-bit integer conversions on `Value` for more
33 /// details.
34 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
35 pub struct Integer(i128);
36 
37 impl Integer {
38     /// Returns the canonical length this integer will have when serialized to bytes.
39     /// This is called `canonical` as it is only used for canonically comparing two
40     /// values. It shouldn't be used in any other context.
canonical_len(&self) -> usize41     fn canonical_len(&self) -> usize {
42         let x = self.0;
43 
44         if let Ok(x) = u8::try_from(x) {
45             if x < 24 {
46                 1
47             } else {
48                 2
49             }
50         } else if let Ok(x) = i8::try_from(x) {
51             if x >= -24i8 {
52                 1
53             } else {
54                 2
55             }
56         } else if u16::try_from(x).is_ok() || i16::try_from(x).is_ok() {
57             3
58         } else if u32::try_from(x).is_ok() || i32::try_from(x).is_ok() {
59             5
60         } else if u64::try_from(x).is_ok() || i64::try_from(x).is_ok() {
61             9
62         } else {
63             // Ciborium serializes u128/i128 as BigPos if they don't fit in 64 bits.
64             // In this special case we have to calculate the length.
65             // The Tag itself will always be 1 byte.
66             x.to_be_bytes().len() + 1
67         }
68     }
69 
70     /// Compare two integers as if we were to serialize them, but more efficiently.
canonical_cmp(&self, other: &Self) -> Ordering71     pub fn canonical_cmp(&self, other: &Self) -> Ordering {
72         match self.canonical_len().cmp(&other.canonical_len()) {
73             Ordering::Equal => {
74                 // Negative numbers are higher in byte-order than positive numbers.
75                 match (self.0.is_negative(), other.0.is_negative()) {
76                     (false, true) => Ordering::Less,
77                     (true, false) => Ordering::Greater,
78                     (true, true) => {
79                         // For negative numbers the byte order puts numbers closer to 0 which
80                         // are lexically higher, lower. So -1 < -2 when sorting by be_bytes().
81                         match self.0.cmp(&other.0) {
82                             Ordering::Less => Ordering::Greater,
83                             Ordering::Equal => Ordering::Equal,
84                             Ordering::Greater => Ordering::Less,
85                         }
86                     }
87                     (_, _) => self.0.cmp(&other.0),
88                 }
89             }
90             x => x,
91         }
92     }
93 }
94 
95 implfrom! {
96     u8 u16 u32 u64
97     i8 i16 i32 i64
98 
99     #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
100     usize
101 
102     #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
103     isize
104 }
105 
106 impl TryFrom<i128> for Integer {
107     type Error = core::num::TryFromIntError;
108 
109     #[inline]
try_from(value: i128) -> Result<Self, Self::Error>110     fn try_from(value: i128) -> Result<Self, Self::Error> {
111         u64::try_from(match value.is_negative() {
112             false => value,
113             true => value ^ !0,
114         })?;
115 
116         Ok(Integer(value))
117     }
118 }
119 
120 impl TryFrom<u128> for Integer {
121     type Error = core::num::TryFromIntError;
122 
123     #[inline]
try_from(value: u128) -> Result<Self, Self::Error>124     fn try_from(value: u128) -> Result<Self, Self::Error> {
125         Ok(Self(u64::try_from(value)?.into()))
126     }
127 }
128 
129 impl From<Integer> for i128 {
130     #[inline]
from(value: Integer) -> Self131     fn from(value: Integer) -> Self {
132         value.0
133     }
134 }
135 
136 impl TryFrom<Integer> for u128 {
137     type Error = core::num::TryFromIntError;
138 
139     #[inline]
try_from(value: Integer) -> Result<Self, Self::Error>140     fn try_from(value: Integer) -> Result<Self, Self::Error> {
141         u128::try_from(value.0)
142     }
143 }
144