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