wtransport_proto/
varint.rs

1use std::fmt;
2
3/// Error returned when constructing a [`VarInt`] from a value >= 2^62
4#[derive(Debug, thiserror::Error)]
5#[error("varint value is out of bounds")]
6pub struct VarIntBoundsExceeded;
7
8/// Variable-length integer.
9///
10/// A non-negative integer value, less than 2^62.
11#[derive(Default, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct VarInt(u64);
13
14impl VarInt {
15    /// The largest value that can be represented by this integer type.
16    pub const MAX: Self = Self(4_611_686_018_427_387_903);
17
18    /// The smallest value that can be represented by this integer type.
19    pub const MIN: Self = Self(0);
20
21    /// Maximum number of bytes for varint encoding.
22    pub const MAX_SIZE: usize = 8;
23
24    /// Constructs a [`VarInt`] from `u32`.
25    #[inline(always)]
26    pub const fn from_u32(value: u32) -> Self {
27        Self(value as u64)
28    }
29
30    /// Tries to construct a [`VarInt`] from `u64`.
31    #[inline(always)]
32    pub const fn try_from_u64(value: u64) -> Result<Self, VarIntBoundsExceeded> {
33        if value <= Self::MAX.0 {
34            Ok(Self(value))
35        } else {
36            Err(VarIntBoundsExceeded)
37        }
38    }
39
40    /// Creates a [`VarInt`] without ensuring it's in range.
41    ///
42    /// # Safety
43    ///
44    /// `value` must be less than 2^62.
45    #[inline(always)]
46    pub const unsafe fn from_u64_unchecked(value: u64) -> Self {
47        debug_assert!(value <= Self::MAX.into_inner());
48        Self(value)
49    }
50
51    /// Extracts the integer value as `u64`.
52    #[inline(always)]
53    pub const fn into_inner(self) -> u64 {
54        self.0
55    }
56
57    /// Returns how many bytes it would take to encode this value as
58    /// a variable-length integer.
59    ///
60    /// This value cannot be larger than [`Self::MAX_SIZE`].
61    pub const fn size(self) -> usize {
62        if self.0 <= 63 {
63            1
64        } else if self.0 <= 16383 {
65            2
66        } else if self.0 <= 1_073_741_823 {
67            4
68        } else if self.0 <= 4_611_686_018_427_387_903 {
69            8
70        } else {
71            unreachable!()
72        }
73    }
74
75    /// Returns how long the variable-length integer is, given its first byte.
76    pub const fn parse_size(first: u8) -> usize {
77        match first >> 6 {
78            0 => 1,
79            1 => 2,
80            2 => 4,
81            3 => 8,
82            _ => unreachable!(),
83        }
84    }
85}
86
87impl From<u8> for VarInt {
88    #[inline(always)]
89    fn from(value: u8) -> Self {
90        Self::from_u32(u32::from(value))
91    }
92}
93
94impl From<u16> for VarInt {
95    #[inline(always)]
96    fn from(value: u16) -> Self {
97        Self::from_u32(u32::from(value))
98    }
99}
100
101impl From<u32> for VarInt {
102    #[inline(always)]
103    fn from(value: u32) -> Self {
104        Self::from_u32(value)
105    }
106}
107
108impl TryFrom<u64> for VarInt {
109    type Error = VarIntBoundsExceeded;
110
111    #[inline(always)]
112    fn try_from(value: u64) -> Result<Self, Self::Error> {
113        Self::try_from_u64(value)
114    }
115}
116
117impl From<VarInt> for u64 {
118    #[inline]
119    fn from(value: VarInt) -> Self {
120        value.0
121    }
122}
123
124impl fmt::Debug for VarInt {
125    #[inline]
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        self.0.fmt(f)
128    }
129}
130
131impl fmt::Display for VarInt {
132    #[inline]
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        self.0.fmt(f)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn bounds() {
144        assert!(VarInt::try_from_u64(VarInt::MAX.into_inner()).is_ok());
145        assert!(VarInt::try_from_u64(VarInt::MAX.into_inner() + 1).is_err());
146        assert!(VarInt::try_from_u64(2_u64.pow(62)).is_err());
147        assert!(VarInt::try_from_u64(2_u64.pow(62) - 1).is_ok());
148    }
149
150    #[test]
151    fn size() {
152        assert!((1..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(0).unwrap().size()));
153        assert!((1..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(63).unwrap().size()));
154
155        assert!((2..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(64).unwrap().size()));
156        assert!((2..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(16383).unwrap().size()));
157
158        assert!((4..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(16384).unwrap().size()));
159        assert!(
160            (4..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(1_073_741_823).unwrap().size())
161        );
162
163        assert!(
164            (8..=VarInt::MAX_SIZE).contains(&VarInt::try_from_u64(1_073_741_824).unwrap().size())
165        );
166        assert!((8..=VarInt::MAX_SIZE).contains(
167            &VarInt::try_from_u64(4_611_686_018_427_387_903)
168                .unwrap()
169                .size()
170        ));
171
172        assert_eq!(VarInt::MAX_SIZE, 8);
173        assert_eq!(VarInt::MAX.size(), VarInt::MAX_SIZE);
174    }
175
176    #[test]
177    fn parse() {
178        assert_eq!(VarInt::parse_size(0xc2), 8);
179        assert_eq!(VarInt::parse_size(0x9d), 4);
180        assert_eq!(VarInt::parse_size(0x7b), 2);
181        assert_eq!(VarInt::parse_size(0x25), 1);
182    }
183}