Skip to main content

bcp_wire/
varint.rs

1/// Maximum number of bytes a u64 varint can occupy.
2/// ceil(64 / 7) = 10 bytes.
3const MAX_VARINT_BYTES: usize = 10;
4
5/// Encode a `u64` value as an unsigned LEB128 varint into the provided buffer.
6///
7/// # Returns
8///
9/// The number of bytes written (1–10).
10///
11/// # Panics
12///
13/// Panics if `buf` is shorter than the required encoding length.
14/// A 10-byte buffer is always sufficient for any `u64`.
15///
16/// # Wire format examples
17///
18/// | Value   | Encoded bytes        | Length |
19/// |---------|----------------------|--------|
20/// | 0       | `[0x00]`             | 1      |
21/// | 1       | `[0x01]`             | 1      |
22/// | 127     | `[0x7F]`             | 1      |
23/// | 128     | `[0x80, 0x01]`       | 2      |
24/// | 300     | `[0xAC, 0x02]`       | 2      |
25/// | 16383   | `[0xFF, 0x7F]`       | 2      |
26/// | 16384   | `[0x80, 0x80, 0x01]` | 3      |
27pub fn encode_varint(mut value: u64, buf: &mut [u8]) -> usize {
28    let mut i = 0;
29    loop {
30        // Take the lowest 7 bits
31        let mut byte = (value & 0x7F) as u8;
32        value >>= 7;
33
34        if value > 0 {
35            // More bytes to come: set the continuation bit
36            byte |= 0x80;
37        }
38
39        buf[i] = byte;
40        i += 1;
41
42        if value == 0 {
43            break;
44        }
45    }
46    i
47}
48
49use crate::error::WireError;
50
51/// Decode an unsigned LEB128 varint from the provided byte slice.
52///
53/// # Returns
54///
55/// `(decoded_value, bytes_consumed)` on success.
56///
57/// # Errors
58///
59/// - [`WireError::VarintTooLong`] if more than 10 bytes are consumed
60///   without finding a terminating byte.
61/// - [`WireError::UnexpectedEof`] if the slice ends mid-varint.
62pub fn decode_varint(buf: &[u8]) -> Result<(u64, usize), WireError> {
63    let mut result: u64 = 0;
64    let mut shift: u32 = 0;
65
66    for (i, &byte) in buf.iter().enumerate() {
67        if i >= MAX_VARINT_BYTES {
68            return Err(WireError::VarintTooLong);
69        }
70
71        // Extract the 7 data bits and shift them into position
72        let data = u64::from(byte & 0x7F);
73        result |= data << shift;
74        shift += 7;
75
76        // If MSB is clear, this is the last byte
77        if byte & 0x80 == 0 {
78            return Ok((result, i + 1));
79        }
80    }
81
82    // We ran out of input bytes while MSB was still set
83    Err(WireError::UnexpectedEof { offset: buf.len() })
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    // Helper: encode a value and return just the used bytes
91    fn encode(value: u64) -> Vec<u8> {
92        let mut buf = [0u8; MAX_VARINT_BYTES];
93        let len = encode_varint(value, &mut buf);
94        buf[..len].to_vec()
95    }
96
97    #[test]
98    fn encode_zero() {
99        assert_eq!(encode(0), vec![0x00]);
100    }
101
102    #[test]
103    fn encode_one() {
104        assert_eq!(encode(1), vec![0x01]);
105    }
106
107    #[test]
108    fn encode_127() {
109        // Largest single-byte value (7 bits all set)
110        assert_eq!(encode(127), vec![0x7F]);
111    }
112
113    #[test]
114    fn encode_128() {
115        // First value requiring 2 bytes
116        assert_eq!(encode(128), vec![0x80, 0x01]);
117    }
118
119    #[test]
120    fn encode_300() {
121        // The protobuf spec example value
122        assert_eq!(encode(300), vec![0xAC, 0x02]);
123    }
124
125    #[test]
126    fn encode_16383() {
127        // Largest 2-byte value (14 bits all set)
128        assert_eq!(encode(16383), vec![0xFF, 0x7F]);
129    }
130
131    #[test]
132    fn encode_16384() {
133        // First 3-byte value
134        assert_eq!(encode(16384), vec![0x80, 0x80, 0x01]);
135    }
136
137    #[test]
138    fn encode_u32_max() {
139        let bytes = encode(u64::from(u32::MAX));
140        assert_eq!(bytes.len(), 5);
141    }
142
143    #[test]
144    fn encode_u64_max() {
145        let bytes = encode(u64::MAX);
146        assert_eq!(bytes.len(), MAX_VARINT_BYTES);
147    }
148
149    #[test]
150    fn roundtrip_boundary_values() {
151        let values = [
152            0,
153            1,
154            127,
155            128,
156            255,
157            256,
158            16383,
159            16384,
160            u64::from(u32::MAX),
161            u64::MAX,
162        ];
163        for &value in &values {
164            let encoded = encode(value);
165            let (decoded, consumed) = decode_varint(&encoded).unwrap();
166            assert_eq!(decoded, value, "roundtrip failed for {value}");
167            assert_eq!(consumed, encoded.len());
168        }
169    }
170
171    #[test]
172    fn decode_with_trailing_bytes() {
173        // Decoder should only consume the varint, leaving trailing data alone
174        let buf = [0xAC, 0x02, 0xFF, 0xFF];
175        let (value, consumed) = decode_varint(&buf).unwrap();
176        assert_eq!(value, 300);
177        assert_eq!(consumed, 2);
178    }
179
180    #[test]
181    fn decode_empty_input() {
182        let result = decode_varint(&[]);
183        assert!(matches!(
184            result,
185            Err(WireError::UnexpectedEof { offset: 0 })
186        ));
187    }
188
189    #[test]
190    fn decode_truncated_varint() {
191        // 0x80 has continuation bit set but there's no next byte
192        let result = decode_varint(&[0x80]);
193        assert!(matches!(result, Err(WireError::UnexpectedEof { .. })));
194    }
195
196    #[test]
197    fn decode_too_long() {
198        // 11 bytes all with continuation bit set
199        let buf = [0x80; 11];
200        let result = decode_varint(&buf);
201        assert!(matches!(result, Err(WireError::VarintTooLong)));
202    }
203}