vex_cdc/
varint.rs

1use core::fmt;
2
3use crate::decode::{Decode, DecodeError};
4use crate::encode::Encode;
5
6/// A variable-width encoded `u16`.
7///
8/// `VarU16` encodes a 16-bit unsigned integer in a compact form, where the
9/// number of bytes required depends on the value being stored. Small values
10/// fit into a single byte, while larger values require two bytes.
11///
12/// This encoding scheme reserves the most significant bit of the first
13/// byte as a flag, indicating the size of the type:
14///
15/// - If `MSB` is `0`, the value fits in one byte.
16/// - If `MSB` is `1`, the value is stored across two bytes.
17///
18/// # Invariants
19///
20/// - Encoded values fit into 15 bits (`value <= u16::MAX >> 1`).
21#[repr(transparent)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct VarU16 {
24    inner: u16,
25}
26
27impl VarU16 {
28    /// Creates a new [`VarU16`].
29    ///
30    /// # Panics
31    ///
32    /// Panics if the given value exceeds the maximum encodable range
33    /// (`value > u16::MAX >> 1`).
34    pub fn new(value: u16) -> Self {
35        Self::try_new(value).expect("Value too large for variable-length u16")
36    }
37
38    /// Tries to create a new [`VarU16`].
39    ///
40    /// # Errors
41    ///
42    /// Returns a [`VarU16SizeError`] if the given value exceeds the
43    /// maximum encodable range (`value > u16::MAX >> 1`).
44    pub const fn try_new(value: u16) -> Result<Self, VarU16SizeError> {
45        if value > (u16::MAX >> 1) {
46            Err(VarU16SizeError { value })
47        } else {
48            Ok(Self { inner: value })
49        }
50    }
51
52    /// Returns the inner raw `u16` value.
53    pub fn into_inner(self) -> u16 {
54        self.inner
55    }
56
57    /// Checks whether the given first byte indicates a wide (two-byte) value.
58    pub fn check_wide(first: u8) -> bool {
59        first > (u8::MAX >> 1) as _
60    }
61}
62
63impl Encode for VarU16 {
64    fn size(&self) -> usize {
65        if self.inner > (u8::MAX >> 1) as _ {
66            2
67        } else {
68            1
69        }
70    }
71
72    fn encode(&self, data: &mut [u8]) {
73        if self.inner > (u8::MAX >> 1) as _ {
74            data[0] = (self.inner >> 8) as u8 | 0x80;
75            data[1] = (self.inner & u8::MAX as u16) as u8;
76        } else {
77            data[0] = self.inner as u8;
78        }
79    }
80}
81
82impl Decode for VarU16 {
83    fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
84        let first = u8::decode(data)?;
85        let wide = first & (1 << 7) != 0;
86
87        Ok(Self {
88            inner: if wide {
89                let last = u8::decode(data)?;
90                let both = [first & u8::MAX >> 1, last];
91                u16::from_be_bytes(both)
92            } else {
93                first as u16
94            },
95        })
96    }
97}
98
99/// Returned when a [`VarU16`] cannot fit the specified value.
100#[derive(Clone, PartialEq, Eq, Debug)]
101pub struct VarU16SizeError {
102    pub value: u16,
103}
104
105impl fmt::Display for VarU16SizeError {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        write!(
108            f,
109            "value {} cannot fit in a variable-length u16",
110            self.value
111        )
112    }
113}
114
115impl core::error::Error for VarU16SizeError {
116    fn description(&self) -> &str {
117        "value too large for variable-length u16"
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use crate::{decode::Decode, encode::Encode, varint::VarU16};
124
125    #[test]
126    fn wide() {
127        // A value that will be encoded as a wide variable length u16.
128        const VAL: u16 = 0xF00;
129        const EXPECTED_ENCODING: [u8; 2] = [0x8f, 0x00];
130
131        let mut buf = [0; 2];
132
133        let var = VarU16::new(VAL);
134        var.encode(&mut buf);
135
136        assert_eq!(EXPECTED_ENCODING, buf);
137        assert_eq!(
138            VAL,
139            VarU16::decode(&mut EXPECTED_ENCODING.as_slice())
140                .unwrap()
141                .into_inner()
142        )
143    }
144
145    #[test]
146    fn thin() {
147        // A value that will be encoded as a thin variable length u16.
148        const VAL: u16 = 0x0F;
149        const EXPECTED_ENCODING: [u8; 1] = [0x0F];
150
151        let mut buf = [0; 1];
152
153        let var = VarU16::new(VAL);
154        var.encode(&mut buf);
155
156        assert_eq!(EXPECTED_ENCODING, buf);
157        assert_eq!(
158            VAL,
159            VarU16::decode(&mut EXPECTED_ENCODING.as_slice())
160                .unwrap()
161                .into_inner()
162        )
163    }
164}