ragc_common/
varint.rs

1// Variable-length integer encoding
2// Rust equivalent of write/read template methods in src/common/archive.h
3
4use std::io::{self, Read, Write};
5
6/// Write a value with variable-length encoding
7/// Format: [num_bytes: u8][value bytes in big-endian]
8/// Returns number of bytes written
9pub fn write_varint<W: Write>(writer: &mut W, value: u64) -> io::Result<usize> {
10    // Count number of bytes needed
11    let mut no_bytes = 0u8;
12    let mut tmp = value;
13    while tmp > 0 {
14        no_bytes += 1;
15        tmp >>= 8;
16    }
17
18    // Special case: value is 0
19    if no_bytes == 0 {
20        writer.write_all(&[0])?;
21        return Ok(1);
22    }
23
24    // Write number of bytes
25    writer.write_all(&[no_bytes])?;
26
27    // Write bytes in big-endian order (most significant first)
28    for i in (0..no_bytes).rev() {
29        let byte = ((value >> (i * 8)) & 0xff) as u8;
30        writer.write_all(&[byte])?;
31    }
32
33    Ok((no_bytes + 1) as usize)
34}
35
36/// Read a value with variable-length encoding
37/// Returns (value, bytes_read)
38pub fn read_varint<R: Read>(reader: &mut R) -> io::Result<(u64, usize)> {
39    // Read number of bytes
40    let mut no_bytes_buf = [0u8; 1];
41    reader.read_exact(&mut no_bytes_buf)?;
42    let no_bytes = no_bytes_buf[0];
43
44    // Special case: value is 0
45    if no_bytes == 0 {
46        return Ok((0, 1));
47    }
48
49    // Read bytes and assemble value
50    let mut value = 0u64;
51    for _ in 0..no_bytes {
52        let mut byte_buf = [0u8; 1];
53        reader.read_exact(&mut byte_buf)?;
54        value <<= 8;
55        value += byte_buf[0] as u64;
56    }
57
58    Ok((value, (no_bytes + 1) as usize))
59}
60
61/// Write a fixed 8-byte unsigned integer
62pub fn write_fixed_u64<W: Write>(writer: &mut W, value: u64) -> io::Result<usize> {
63    let bytes = value.to_le_bytes();
64    writer.write_all(&bytes)?;
65    Ok(8)
66}
67
68/// Read a fixed 8-byte unsigned integer
69pub fn read_fixed_u64<R: Read>(reader: &mut R) -> io::Result<u64> {
70    let mut bytes = [0u8; 8];
71    reader.read_exact(&mut bytes)?;
72    Ok(u64::from_le_bytes(bytes))
73}
74
75/// Write a varint to a byte vector (convenience wrapper)
76pub fn encode_varint(value: u64) -> Vec<u8> {
77    let mut buf = Vec::new();
78    write_varint(&mut buf, value).expect("Writing to Vec should not fail");
79    buf
80}
81
82/// Read a varint from a byte slice (convenience wrapper)
83pub fn decode_varint(bytes: &[u8]) -> io::Result<(u64, usize)> {
84    let mut cursor = std::io::Cursor::new(bytes);
85    read_varint(&mut cursor)
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_varint_roundtrip() {
94        let test_values = vec![
95            0u64,
96            1,
97            127,
98            128,
99            255,
100            256,
101            65535,
102            65536,
103            0xFFFFFFFF,
104            0x1234567890ABCDEF,
105            u64::MAX,
106        ];
107
108        for value in test_values {
109            let encoded = encode_varint(value);
110            let (decoded, _) = decode_varint(&encoded).unwrap();
111            assert_eq!(value, decoded, "Roundtrip failed for value {value}");
112        }
113    }
114
115    #[test]
116    fn test_varint_encoding_lengths() {
117        assert_eq!(encode_varint(0).len(), 1); // [0]
118        assert_eq!(encode_varint(1).len(), 2); // [1, 0x01]
119        assert_eq!(encode_varint(255).len(), 2); // [1, 0xff]
120        assert_eq!(encode_varint(256).len(), 3); // [2, 0x01, 0x00]
121        assert_eq!(encode_varint(65535).len(), 3); // [2, 0xff, 0xff]
122        assert_eq!(encode_varint(65536).len(), 4); // [3, 0x01, 0x00, 0x00]
123    }
124
125    #[test]
126    fn test_fixed_u64_roundtrip() {
127        let test_values = vec![0u64, 1, 42, 0xDEADBEEF, u64::MAX];
128
129        for value in test_values {
130            let mut buf = Vec::new();
131            write_fixed_u64(&mut buf, value).unwrap();
132            assert_eq!(buf.len(), 8);
133
134            let decoded = read_fixed_u64(&mut std::io::Cursor::new(&buf)).unwrap();
135            assert_eq!(value, decoded);
136        }
137    }
138}