ciborium/value/
integer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: Apache-2.0
use core::cmp::Ordering;

macro_rules! implfrom {
    ($( $(#[$($attr:meta)+])? $t:ident)+) => {
        $(
            $(#[$($attr)+])?
            impl From<$t> for Integer {
                #[inline]
                fn from(value: $t) -> Self {
                    Self(value as _)
                }
            }

            impl TryFrom<Integer> for $t {
                type Error = core::num::TryFromIntError;

                #[inline]
                fn try_from(value: Integer) -> Result<Self, Self::Error> {
                    $t::try_from(value.0)
                }
            }
        )+
    };
}

/// An abstract integer value
///
/// This opaque type represents an integer value which can be encoded in CBOR
/// without resulting to big integer encoding. Larger values may be encoded
/// using the big integer encoding as described in the CBOR RFC. See the
/// implementations for 128-bit integer conversions on `Value` for more
/// details.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Integer(i128);

impl Integer {
    /// Returns the canonical length this integer will have when serialized to bytes.
    /// This is called `canonical` as it is only used for canonically comparing two
    /// values. It shouldn't be used in any other context.
    fn canonical_len(&self) -> usize {
        let x = self.0;

        if let Ok(x) = u8::try_from(x) {
            if x < 24 {
                1
            } else {
                2
            }
        } else if let Ok(x) = i8::try_from(x) {
            if x >= -24i8 {
                1
            } else {
                2
            }
        } else if u16::try_from(x).is_ok() || i16::try_from(x).is_ok() {
            3
        } else if u32::try_from(x).is_ok() || i32::try_from(x).is_ok() {
            5
        } else if u64::try_from(x).is_ok() || i64::try_from(x).is_ok() {
            9
        } else {
            // Ciborium serializes u128/i128 as BigPos if they don't fit in 64 bits.
            // In this special case we have to calculate the length.
            // The Tag itself will always be 1 byte.
            x.to_be_bytes().len() + 1
        }
    }

    /// Compare two integers as if we were to serialize them, but more efficiently.
    pub fn canonical_cmp(&self, other: &Self) -> Ordering {
        match self.canonical_len().cmp(&other.canonical_len()) {
            Ordering::Equal => {
                // Negative numbers are higher in byte-order than positive numbers.
                match (self.0.is_negative(), other.0.is_negative()) {
                    (false, true) => Ordering::Less,
                    (true, false) => Ordering::Greater,
                    (true, true) => {
                        // For negative numbers the byte order puts numbers closer to 0 which
                        // are lexically higher, lower. So -1 < -2 when sorting by be_bytes().
                        match self.0.cmp(&other.0) {
                            Ordering::Less => Ordering::Greater,
                            Ordering::Equal => Ordering::Equal,
                            Ordering::Greater => Ordering::Less,
                        }
                    }
                    (_, _) => self.0.cmp(&other.0),
                }
            }
            x => x,
        }
    }
}

implfrom! {
    u8 u16 u32 u64
    i8 i16 i32 i64

    #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
    usize

    #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
    isize
}

impl TryFrom<i128> for Integer {
    type Error = core::num::TryFromIntError;

    #[inline]
    fn try_from(value: i128) -> Result<Self, Self::Error> {
        u64::try_from(match value.is_negative() {
            false => value,
            true => value ^ !0,
        })?;

        Ok(Integer(value))
    }
}

impl TryFrom<u128> for Integer {
    type Error = core::num::TryFromIntError;

    #[inline]
    fn try_from(value: u128) -> Result<Self, Self::Error> {
        Ok(Self(u64::try_from(value)?.into()))
    }
}

impl From<Integer> for i128 {
    #[inline]
    fn from(value: Integer) -> Self {
        value.0
    }
}

impl TryFrom<Integer> for u128 {
    type Error = core::num::TryFromIntError;

    #[inline]
    fn try_from(value: Integer) -> Result<Self, Self::Error> {
        u128::try_from(value.0)
    }
}