Skip to main content

oxihuman_core/
protobuf_varint.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Protobuf varint encode/decode stub.
6
7/// Error type for varint operations.
8#[derive(Debug, Clone, PartialEq)]
9pub enum VarintError {
10    BufferTooShort,
11    Overflow,
12    TrailingBytes,
13}
14
15impl std::fmt::Display for VarintError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            Self::BufferTooShort => write!(f, "buffer too short for varint"),
19            Self::Overflow => write!(f, "varint overflows u64"),
20            Self::TrailingBytes => write!(f, "unexpected trailing bytes"),
21        }
22    }
23}
24
25/// Encode a `u64` as a protobuf varint into a byte buffer.
26pub fn encode_varint(mut value: u64, buf: &mut Vec<u8>) {
27    loop {
28        let mut byte = (value & 0x7F) as u8;
29        value >>= 7;
30        if value != 0 {
31            byte |= 0x80;
32        }
33        buf.push(byte);
34        if value == 0 {
35            break;
36        }
37    }
38}
39
40/// Decode a protobuf varint from a byte slice.
41/// Returns `(decoded_value, bytes_consumed)`.
42pub fn decode_varint(buf: &[u8]) -> Result<(u64, usize), VarintError> {
43    let mut result: u64 = 0;
44    let mut shift = 0u32;
45    for (i, &byte) in buf.iter().enumerate() {
46        if shift >= 64 {
47            return Err(VarintError::Overflow);
48        }
49        result |= ((byte & 0x7F) as u64) << shift;
50        if byte & 0x80 == 0 {
51            return Ok((result, i + 1));
52        }
53        shift += 7;
54    }
55    Err(VarintError::BufferTooShort)
56}
57
58/// Encode a `i64` using zigzag encoding then varint.
59pub fn encode_zigzag(value: i64, buf: &mut Vec<u8>) {
60    let zz = ((value << 1) ^ (value >> 63)) as u64;
61    encode_varint(zz, buf);
62}
63
64/// Decode a zigzag-encoded varint from a byte slice.
65pub fn decode_zigzag(buf: &[u8]) -> Result<(i64, usize), VarintError> {
66    let (zz, n) = decode_varint(buf)?;
67    let value = ((zz >> 1) as i64) ^ (-((zz & 1) as i64));
68    Ok((value, n))
69}
70
71/// Return the number of bytes needed to encode a varint.
72pub fn varint_size(mut value: u64) -> usize {
73    let mut n = 1;
74    loop {
75        value >>= 7;
76        if value == 0 {
77            break;
78        }
79        n += 1;
80    }
81    n
82}
83
84/// Encode then immediately decode, verifying the roundtrip.
85pub fn varint_roundtrip_ok(value: u64) -> bool {
86    let mut buf = vec![];
87    encode_varint(value, &mut buf);
88    decode_varint(&buf)
89        .map(|(v, _)| v == value)
90        .unwrap_or(false)
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_encode_zero() {
99        /* zero encodes to single 0x00 byte */
100        let mut buf = vec![];
101        encode_varint(0, &mut buf);
102        assert_eq!(buf, &[0x00]);
103    }
104
105    #[test]
106    fn test_encode_one() {
107        /* 1 encodes to 0x01 */
108        let mut buf = vec![];
109        encode_varint(1, &mut buf);
110        assert_eq!(buf, &[0x01]);
111    }
112
113    #[test]
114    fn test_decode_single_byte() {
115        /* single-byte varint */
116        let (v, n) = decode_varint(&[0x05]).expect("should succeed");
117        assert_eq!(v, 5);
118        assert_eq!(n, 1);
119    }
120
121    #[test]
122    fn test_roundtrip_small() {
123        /* small value roundtrip */
124        assert!(varint_roundtrip_ok(42));
125    }
126
127    #[test]
128    fn test_roundtrip_large() {
129        /* large value roundtrip */
130        assert!(varint_roundtrip_ok(u64::MAX));
131    }
132
133    #[test]
134    fn test_varint_size_one_byte() {
135        /* values 0..=127 fit in one byte */
136        assert_eq!(varint_size(127), 1);
137    }
138
139    #[test]
140    fn test_varint_size_two_bytes() {
141        /* 128 requires two bytes */
142        assert_eq!(varint_size(128), 2);
143    }
144
145    #[test]
146    fn test_zigzag_positive() {
147        /* positive zigzag roundtrip */
148        let mut buf = vec![];
149        encode_zigzag(100, &mut buf);
150        let (v, _) = decode_zigzag(&buf).expect("should succeed");
151        assert_eq!(v, 100);
152    }
153
154    #[test]
155    fn test_zigzag_negative() {
156        /* negative zigzag roundtrip */
157        let mut buf = vec![];
158        encode_zigzag(-50, &mut buf);
159        let (v, _) = decode_zigzag(&buf).expect("should succeed");
160        assert_eq!(v, -50);
161    }
162
163    #[test]
164    fn test_buffer_too_short() {
165        /* truncated varint returns error */
166        assert_eq!(
167            decode_varint(&[0x80]).unwrap_err(),
168            VarintError::BufferTooShort
169        );
170    }
171}