Skip to main content

stackforge_core/layer/quic/
varint.rs

1//! QUIC variable-length integer encoding (RFC 9000 Section 16).
2//!
3//! The high 2 bits of the first byte encode the length:
4//! - `00` = 1 byte  (6-bit value, max 63)
5//! - `01` = 2 bytes (14-bit value, max 16383)
6//! - `10` = 4 bytes (30-bit value, max 1073741823)
7//! - `11` = 8 bytes (62-bit value, max 4611686018427387903)
8
9/// Maximum value that fits in a QUIC variable-length integer (62 bits).
10pub const VARINT_MAX: u64 = (1u64 << 62) - 1;
11
12/// Decode a QUIC variable-length integer from a buffer.
13///
14/// Returns `Some((value, bytes_consumed))` on success, or `None` if the buffer
15/// is too short or the prefix is invalid.
16#[must_use]
17pub fn decode(buf: &[u8]) -> Option<(u64, usize)> {
18    if buf.is_empty() {
19        return None;
20    }
21    let prefix = (buf[0] & 0xC0) >> 6;
22    match prefix {
23        0 => {
24            // 1-byte encoding: 6-bit value
25            Some((u64::from(buf[0] & 0x3F), 1))
26        },
27        1 => {
28            // 2-byte encoding: 14-bit value
29            if buf.len() < 2 {
30                return None;
31            }
32            let value = u64::from(u16::from_be_bytes([buf[0] & 0x3F, buf[1]]));
33            Some((value, 2))
34        },
35        2 => {
36            // 4-byte encoding: 30-bit value
37            if buf.len() < 4 {
38                return None;
39            }
40            let value = u64::from(u32::from_be_bytes([buf[0] & 0x3F, buf[1], buf[2], buf[3]]));
41            Some((value, 4))
42        },
43        3 => {
44            // 8-byte encoding: 62-bit value
45            if buf.len() < 8 {
46                return None;
47            }
48            let value = u64::from_be_bytes([
49                buf[0] & 0x3F,
50                buf[1],
51                buf[2],
52                buf[3],
53                buf[4],
54                buf[5],
55                buf[6],
56                buf[7],
57            ]);
58            Some((value, 8))
59        },
60        _ => unreachable!("prefix is always 0..=3"),
61    }
62}
63
64/// Encode a QUIC variable-length integer into bytes.
65///
66/// # Panics
67///
68/// Panics if `value` exceeds `VARINT_MAX` (4611686018427387903).
69#[must_use]
70pub fn encode(value: u64) -> Vec<u8> {
71    assert!(
72        value <= VARINT_MAX,
73        "QUIC varint value {value} exceeds maximum {VARINT_MAX}"
74    );
75    if value < 64 {
76        // 1-byte encoding
77        vec![value as u8]
78    } else if value < 16_384 {
79        // 2-byte encoding: set top 2 bits to 0b01
80        let encoded = (value as u16) | 0x4000;
81        encoded.to_be_bytes().to_vec()
82    } else if value < 1_073_741_824 {
83        // 4-byte encoding: set top 2 bits to 0b10
84        let encoded = (value as u32) | 0x8000_0000;
85        encoded.to_be_bytes().to_vec()
86    } else {
87        // 8-byte encoding: set top 2 bits to 0b11
88        let encoded = value | 0xC000_0000_0000_0000;
89        encoded.to_be_bytes().to_vec()
90    }
91}
92
93/// Get the byte length that would be needed to encode `value`.
94///
95/// Returns 1, 2, 4, or 8.
96///
97/// # Panics
98///
99/// Panics if `value` exceeds `VARINT_MAX`.
100#[must_use]
101pub fn encoded_len(value: u64) -> usize {
102    assert!(
103        value <= VARINT_MAX,
104        "QUIC varint value {value} exceeds maximum {VARINT_MAX}"
105    );
106    if value < 64 {
107        1
108    } else if value < 16_384 {
109        2
110    } else if value < 1_073_741_824 {
111        4
112    } else {
113        8
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_decode_1byte() {
123        let buf = [0x25u8]; // 0b00100101 = 37
124        let (val, len) = decode(&buf).unwrap();
125        assert_eq!(val, 37);
126        assert_eq!(len, 1);
127    }
128
129    #[test]
130    fn test_decode_2bytes() {
131        // 0x4001 => prefix=01, value=0x0001=1 (but high bits cleared)
132        // Actually: 0x40 | 0x01 = 0x4001 encodes value=1
133        let buf = [0x40u8, 0x01u8];
134        let (val, len) = decode(&buf).unwrap();
135        assert_eq!(val, 1);
136        assert_eq!(len, 2);
137    }
138
139    #[test]
140    fn test_decode_4bytes() {
141        // Encode 494878333 in 4-byte form: 0x80000000 | 494878333 = 0x9D7F3B3D
142        let encoded = (494_878_333u32 | 0x8000_0000u32).to_be_bytes();
143        let (val, len) = decode(&encoded).unwrap();
144        assert_eq!(val, 494_878_333);
145        assert_eq!(len, 4);
146    }
147
148    #[test]
149    fn test_decode_8bytes() {
150        let v: u64 = 151_288_809_941_952_652;
151        let encoded = (v | 0xC000_0000_0000_0000).to_be_bytes();
152        let (val, len) = decode(&encoded).unwrap();
153        assert_eq!(val, v);
154        assert_eq!(len, 8);
155    }
156
157    #[test]
158    fn test_encode_decode_roundtrip() {
159        for &v in &[
160            0u64,
161            1,
162            63,
163            64,
164            16383,
165            16384,
166            1_073_741_823,
167            1_073_741_824,
168            VARINT_MAX,
169        ] {
170            let encoded = encode(v);
171            let (decoded, _) = decode(&encoded).unwrap();
172            assert_eq!(decoded, v, "roundtrip failed for {}", v);
173        }
174    }
175
176    #[test]
177    fn test_encoded_len() {
178        assert_eq!(encoded_len(0), 1);
179        assert_eq!(encoded_len(63), 1);
180        assert_eq!(encoded_len(64), 2);
181        assert_eq!(encoded_len(16383), 2);
182        assert_eq!(encoded_len(16384), 4);
183        assert_eq!(encoded_len(1_073_741_823), 4);
184        assert_eq!(encoded_len(1_073_741_824), 8);
185        assert_eq!(encoded_len(VARINT_MAX), 8);
186    }
187
188    #[test]
189    fn test_decode_empty() {
190        assert!(decode(&[]).is_none());
191    }
192
193    #[test]
194    fn test_decode_too_short() {
195        // 2-byte prefix but only 1 byte
196        assert!(decode(&[0x40]).is_none());
197        // 4-byte prefix but only 2 bytes
198        assert!(decode(&[0x80, 0x01]).is_none());
199        // 8-byte prefix but only 4 bytes
200        assert!(decode(&[0xC0, 0x01, 0x02, 0x03]).is_none());
201    }
202}