Skip to main content

tempest_core/encoding/
varint.rs

1use bytes::{BufMut, BytesMut};
2use integer_encoding::VarInt;
3
4/// Encodes `i` as a variable-length integer, writing it into `buf`.
5pub fn encode_varint(buf: &mut BytesMut, i: usize) {
6    // reserve space
7    let varint_size = i.required_space();
8    buf.put_bytes(0, varint_size);
9    // encode varint
10    let buflen = buf.len();
11    let written = i.encode_var(&mut buf[buflen - varint_size..]);
12    debug_assert_eq!(written, varint_size);
13}
14
15/// Decodes a variable-length integer from `src`.
16///
17/// # Returns
18///
19/// - `Some((varint, bytes_read))`, if successfully decoding the varint.
20/// - `None`, if all bytes has MSB set.
21pub fn decode_varint(src: &[u8]) -> Option<(usize, usize)> {
22    usize::decode_var(src)
23}
24
25#[cfg(test)]
26mod tests {
27    use bytes::BytesMut;
28
29    use super::*;
30
31    fn roundtrip(i: usize) -> (usize, usize) {
32        let mut buf = BytesMut::new();
33        encode_varint(&mut buf, i);
34        let (val, bytes_read) = decode_varint(&buf).expect("decode failed");
35        assert_eq!(val, i, "roundtrip failed for {}", i);
36        (val, bytes_read)
37    }
38
39    // -- roundtrip --
40
41    #[test]
42    fn test_roundtrip_zero() {
43        roundtrip(0);
44    }
45
46    #[test]
47    fn test_roundtrip_one() {
48        roundtrip(1);
49    }
50
51    #[test]
52    fn test_roundtrip_max_one_byte() {
53        roundtrip(127); // largest value that fits in 1 byte
54    }
55
56    #[test]
57    fn test_roundtrip_min_two_bytes() {
58        roundtrip(128); // smallest value requiring 2 bytes
59    }
60
61    #[test]
62    fn test_roundtrip_large_values() {
63        for val in [255, 256, 1024, 65535, 65536, 1 << 20, 1 << 28] {
64            roundtrip(val);
65        }
66    }
67
68    #[test]
69    fn test_roundtrip_usize_max() {
70        roundtrip(usize::MAX);
71    }
72
73    // -- encoding size --
74
75    #[test]
76    fn test_single_byte_encoding() {
77        let mut buf = BytesMut::new();
78        encode_varint(&mut buf, 127);
79        assert_eq!(buf.len(), 1, "127 should encode to 1 byte");
80    }
81
82    #[test]
83    fn test_two_byte_encoding() {
84        let mut buf = BytesMut::new();
85        encode_varint(&mut buf, 128);
86        assert_eq!(buf.len(), 2, "128 should encode to 2 bytes");
87    }
88
89    #[test]
90    fn test_zero_encodes_to_one_byte() {
91        let mut buf = BytesMut::new();
92        encode_varint(&mut buf, 0);
93        assert_eq!(buf.len(), 1);
94    }
95
96    // -- decode --
97
98    #[test]
99    fn test_decode_returns_correct_bytes_read() {
100        let mut buf = BytesMut::new();
101        encode_varint(&mut buf, 128);
102        let (_, bytes_read) = decode_varint(&buf).unwrap();
103        assert_eq!(bytes_read, 2);
104    }
105
106    #[test]
107    fn test_decode_none_on_all_msb_set() {
108        // all bytes have MSB set - no terminating byte, decode should return None
109        let buf = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
110        assert!(decode_varint(buf).is_none());
111    }
112
113    #[test]
114    fn test_decode_ignores_trailing_bytes() {
115        // decode should stop at the varint boundary, not consume trailing data
116        let mut buf = BytesMut::new();
117        encode_varint(&mut buf, 42);
118        buf.extend_from_slice(&[0xAB, 0xCD]); // trailing garbage
119        let (val, bytes_read) = decode_varint(&buf).unwrap();
120        assert_eq!(val, 42);
121        assert!(bytes_read < buf.len(), "should not consume trailing bytes");
122    }
123
124    // -- sequential encoding --
125
126    #[test]
127    fn test_multiple_varints_sequential() {
128        // encode several varints back to back, decode them in order
129        let values = [0usize, 1, 127, 128, 300, 100_000];
130        let mut buf = BytesMut::new();
131        for &v in &values {
132            encode_varint(&mut buf, v);
133        }
134
135        let mut slice = &buf[..];
136        for &expected in &values {
137            let (val, bytes_read) = decode_varint(slice).unwrap();
138            assert_eq!(val, expected);
139            slice = &slice[bytes_read..];
140        }
141        assert!(slice.is_empty(), "buffer should be fully consumed");
142    }
143}