1// SPDX-License-Identifier: Apache-2.0
2use core::cmp::Ordering;
3
4macro_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)]
35pub struct Integer(i128);
36
37impl 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.
41 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.
71 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
95implfrom! {
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
106impl TryFrom<i128> for Integer {
107 type Error = core::num::TryFromIntError;
108
109 #[inline]
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
120impl TryFrom<u128> for Integer {
121 type Error = core::num::TryFromIntError;
122
123 #[inline]
124 fn try_from(value: u128) -> Result<Self, Self::Error> {
125 Ok(Self(u64::try_from(value)?.into()))
126 }
127}
128
129impl From<Integer> for i128 {
130 #[inline]
131 fn from(value: Integer) -> Self {
132 value.0
133 }
134}
135
136impl TryFrom<Integer> for u128 {
137 type Error = core::num::TryFromIntError;
138
139 #[inline]
140 fn try_from(value: Integer) -> Result<Self, Self::Error> {
141 u128::try_from(value.0)
142 }
143}
144