Skip to main content

oxihuman_export/
protobuf_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Protocol Buffers binary wire-format encoding.
6
7/// Protobuf wire types.
8#[allow(dead_code)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum WireType {
11    Varint = 0,
12    Bit64 = 1,
13    LenDelimited = 2,
14    Bit32 = 5,
15}
16
17/// Encode a protobuf tag (field number + wire type).
18#[allow(dead_code)]
19pub fn encode_tag(field: u32, wire: WireType) -> Vec<u8> {
20    let tag = (field << 3) | wire as u32;
21    encode_varint(tag as u64)
22}
23
24/// Encode an unsigned varint.
25#[allow(dead_code)]
26pub fn encode_varint(mut val: u64) -> Vec<u8> {
27    let mut out = Vec::new();
28    loop {
29        let byte = (val & 0x7F) as u8;
30        val >>= 7;
31        if val == 0 {
32            out.push(byte);
33            break;
34        } else {
35            out.push(byte | 0x80);
36        }
37    }
38    out
39}
40
41/// Zigzag-encode a signed int (for sint32/sint64).
42#[allow(dead_code)]
43pub fn zigzag32(val: i32) -> u32 {
44    ((val << 1) ^ (val >> 31)) as u32
45}
46
47/// Zigzag-encode a signed int64.
48#[allow(dead_code)]
49pub fn zigzag64(val: i64) -> u64 {
50    ((val << 1) ^ (val >> 63)) as u64
51}
52
53/// A protobuf encoder.
54#[allow(dead_code)]
55#[derive(Debug, Clone, Default)]
56pub struct ProtoEncoder {
57    pub buf: Vec<u8>,
58}
59
60impl ProtoEncoder {
61    #[allow(dead_code)]
62    pub fn new() -> Self {
63        ProtoEncoder::default()
64    }
65
66    #[allow(dead_code)]
67    pub fn write_varint_field(&mut self, field: u32, val: u64) {
68        self.buf.extend(encode_tag(field, WireType::Varint));
69        self.buf.extend(encode_varint(val));
70    }
71
72    #[allow(dead_code)]
73    pub fn write_fixed64_field(&mut self, field: u32, val: u64) {
74        self.buf.extend(encode_tag(field, WireType::Bit64));
75        self.buf.extend_from_slice(&val.to_le_bytes());
76    }
77
78    #[allow(dead_code)]
79    pub fn write_fixed32_field(&mut self, field: u32, val: u32) {
80        self.buf.extend(encode_tag(field, WireType::Bit32));
81        self.buf.extend_from_slice(&val.to_le_bytes());
82    }
83
84    #[allow(dead_code)]
85    pub fn write_bytes_field(&mut self, field: u32, data: &[u8]) {
86        self.buf.extend(encode_tag(field, WireType::LenDelimited));
87        self.buf.extend(encode_varint(data.len() as u64));
88        self.buf.extend_from_slice(data);
89    }
90
91    #[allow(dead_code)]
92    pub fn write_string_field(&mut self, field: u32, s: &str) {
93        self.write_bytes_field(field, s.as_bytes());
94    }
95
96    #[allow(dead_code)]
97    pub fn write_f32_field(&mut self, field: u32, val: f32) {
98        self.write_fixed32_field(field, val.to_bits());
99    }
100
101    #[allow(dead_code)]
102    pub fn write_f64_field(&mut self, field: u32, val: f64) {
103        self.write_fixed64_field(field, val.to_bits());
104    }
105
106    #[allow(dead_code)]
107    pub fn as_bytes(&self) -> &[u8] {
108        &self.buf
109    }
110
111    #[allow(dead_code)]
112    pub fn len(&self) -> usize {
113        self.buf.len()
114    }
115
116    #[allow(dead_code)]
117    pub fn is_empty(&self) -> bool {
118        self.buf.is_empty()
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn varint_small_single_byte() {
128        let b = encode_varint(1);
129        assert_eq!(b, vec![0x01]);
130    }
131
132    #[test]
133    fn varint_128_two_bytes() {
134        let b = encode_varint(128);
135        assert_eq!(b.len(), 2);
136    }
137
138    #[test]
139    fn tag_field1_varint() {
140        let t = encode_tag(1, WireType::Varint);
141        assert_eq!(t, vec![0x08]);
142    }
143
144    #[test]
145    fn zigzag32_positive() {
146        assert_eq!(zigzag32(1), 2);
147    }
148
149    #[test]
150    fn zigzag32_negative() {
151        assert_eq!(zigzag32(-1), 1);
152    }
153
154    #[test]
155    fn zigzag64_zero() {
156        assert_eq!(zigzag64(0), 0);
157    }
158
159    #[test]
160    fn write_varint_field() {
161        let mut enc = ProtoEncoder::new();
162        enc.write_varint_field(1, 42);
163        assert!(!enc.is_empty());
164    }
165
166    #[test]
167    fn write_string_field_contains_bytes() {
168        let mut enc = ProtoEncoder::new();
169        enc.write_string_field(1, "hi");
170        let s = &enc.buf;
171        assert!(s.windows(2).any(|w| w == b"hi"));
172    }
173
174    #[test]
175    fn write_f32_field_nine_bytes() {
176        let mut enc = ProtoEncoder::new();
177        enc.write_f32_field(1, 1.0);
178        assert_eq!(enc.len(), 5);
179    }
180
181    #[test]
182    fn write_f64_field_ten_bytes() {
183        let mut enc = ProtoEncoder::new();
184        enc.write_f64_field(1, 1.0);
185        assert_eq!(enc.len(), 9);
186    }
187}