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.
16pub fn decode(buf: &[u8]) -> Option<(u64, usize)> {
17    if buf.is_empty() {
18        return None;
19    }
20    let prefix = (buf[0] & 0xC0) >> 6;
21    match prefix {
22        0 => {
23            // 1-byte encoding: 6-bit value
24            Some(((buf[0] & 0x3F) as u64, 1))
25        },
26        1 => {
27            // 2-byte encoding: 14-bit value
28            if buf.len() < 2 {
29                return None;
30            }
31            let value = u16::from_be_bytes([buf[0] & 0x3F, buf[1]]) as u64;
32            Some((value, 2))
33        },
34        2 => {
35            // 4-byte encoding: 30-bit value
36            if buf.len() < 4 {
37                return None;
38            }
39            let value = u32::from_be_bytes([buf[0] & 0x3F, buf[1], buf[2], buf[3]]) as u64;
40            Some((value, 4))
41        },
42        3 => {
43            // 8-byte encoding: 62-bit value
44            if buf.len() < 8 {
45                return None;
46            }
47            let value = u64::from_be_bytes([
48                buf[0] & 0x3F,
49                buf[1],
50                buf[2],
51                buf[3],
52                buf[4],
53                buf[5],
54                buf[6],
55                buf[7],
56            ]);
57            Some((value, 8))
58        },
59        _ => unreachable!("prefix is always 0..=3"),
60    }
61}
62
63/// Encode a QUIC variable-length integer into bytes.
64///
65/// # Panics
66///
67/// Panics if `value` exceeds `VARINT_MAX` (4611686018427387903).
68pub fn encode(value: u64) -> Vec<u8> {
69    assert!(
70        value <= VARINT_MAX,
71        "QUIC varint value {} exceeds maximum {}",
72        value,
73        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`.
100pub fn encoded_len(value: u64) -> usize {
101    assert!(
102        value <= VARINT_MAX,
103        "QUIC varint value {} exceeds maximum {}",
104        value,
105        VARINT_MAX
106    );
107    if value < 64 {
108        1
109    } else if value < 16_384 {
110        2
111    } else if value < 1_073_741_824 {
112        4
113    } else {
114        8
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_decode_1byte() {
124        let buf = [0x25u8]; // 0b00100101 = 37
125        let (val, len) = decode(&buf).unwrap();
126        assert_eq!(val, 37);
127        assert_eq!(len, 1);
128    }
129
130    #[test]
131    fn test_decode_2bytes() {
132        // 0x4001 => prefix=01, value=0x0001=1 (but high bits cleared)
133        // Actually: 0x40 | 0x01 = 0x4001 encodes value=1
134        let buf = [0x40u8, 0x01u8];
135        let (val, len) = decode(&buf).unwrap();
136        assert_eq!(val, 1);
137        assert_eq!(len, 2);
138    }
139
140    #[test]
141    fn test_decode_4bytes() {
142        // Encode 494878333 in 4-byte form: 0x80000000 | 494878333 = 0x9D7F3B3D
143        let encoded = (494_878_333u32 | 0x8000_0000u32).to_be_bytes();
144        let (val, len) = decode(&encoded).unwrap();
145        assert_eq!(val, 494_878_333);
146        assert_eq!(len, 4);
147    }
148
149    #[test]
150    fn test_decode_8bytes() {
151        let v: u64 = 151_288_809_941_952_652;
152        let encoded = (v | 0xC000_0000_0000_0000).to_be_bytes();
153        let (val, len) = decode(&encoded).unwrap();
154        assert_eq!(val, v);
155        assert_eq!(len, 8);
156    }
157
158    #[test]
159    fn test_encode_decode_roundtrip() {
160        for &v in &[
161            0u64,
162            1,
163            63,
164            64,
165            16383,
166            16384,
167            1_073_741_823,
168            1_073_741_824,
169            VARINT_MAX,
170        ] {
171            let encoded = encode(v);
172            let (decoded, _) = decode(&encoded).unwrap();
173            assert_eq!(decoded, v, "roundtrip failed for {}", v);
174        }
175    }
176
177    #[test]
178    fn test_encoded_len() {
179        assert_eq!(encoded_len(0), 1);
180        assert_eq!(encoded_len(63), 1);
181        assert_eq!(encoded_len(64), 2);
182        assert_eq!(encoded_len(16383), 2);
183        assert_eq!(encoded_len(16384), 4);
184        assert_eq!(encoded_len(1_073_741_823), 4);
185        assert_eq!(encoded_len(1_073_741_824), 8);
186        assert_eq!(encoded_len(VARINT_MAX), 8);
187    }
188
189    #[test]
190    fn test_decode_empty() {
191        assert!(decode(&[]).is_none());
192    }
193
194    #[test]
195    fn test_decode_too_short() {
196        // 2-byte prefix but only 1 byte
197        assert!(decode(&[0x40]).is_none());
198        // 4-byte prefix but only 2 bytes
199        assert!(decode(&[0x80, 0x01]).is_none());
200        // 8-byte prefix but only 4 bytes
201        assert!(decode(&[0xC0, 0x01, 0x02, 0x03]).is_none());
202    }
203}