Skip to main content

vexil_runtime/
bit_writer.rs

1/// A byte-buffer builder that packs fields LSB-first at the bit level.
2///
3/// Created with [`BitWriter::new`], written to with `write_*` methods, and
4/// finalized with [`BitWriter::finish`] which flushes any partial byte and
5/// returns the completed buffer.
6///
7/// Sub-byte fields are accumulated in a single byte; once 8 bits are filled
8/// the byte is flushed. Multi-byte writes (e.g. [`write_u16`](Self::write_u16))
9/// first align to a byte boundary, then append little-endian bytes directly.
10pub struct BitWriter {
11    buf: Vec<u8>,
12    current_byte: u8,
13    bit_offset: u8,
14    recursion_depth: u32,
15}
16
17impl BitWriter {
18    /// Create a new, empty `BitWriter`.
19    pub fn new() -> Self {
20        Self::with_capacity(64)
21    }
22
23    /// Create a `BitWriter` with pre-allocated buffer capacity.
24    ///
25    /// Use this when the approximate wire size is known (from `wire_size_bits`)
26    /// to avoid repeated reallocations during encoding.
27    pub fn with_capacity(bytes: usize) -> Self {
28        Self {
29            buf: Vec::with_capacity(bytes),
30            current_byte: 0,
31            bit_offset: 0,
32            recursion_depth: 0,
33        }
34    }
35
36    /// Reset the writer for reuse, keeping the allocated buffer.
37    ///
38    /// This avoids re-allocation when encoding multiple messages of similar size.
39    pub fn reset(&mut self) {
40        self.buf.clear();
41        self.current_byte = 0;
42        self.bit_offset = 0;
43        self.recursion_depth = 0;
44    }
45
46    /// Internal: align to a byte boundary without the "empty = zero byte" rule.
47    /// Used before multi-byte writes to ensure alignment.
48    fn align(&mut self) {
49        if self.bit_offset > 0 {
50            self.buf.push(self.current_byte);
51            self.current_byte = 0;
52            self.bit_offset = 0;
53        }
54    }
55
56    /// Write `count` bits from `value`, LSB first.
57    ///
58    /// Fast path: if the value fits entirely within the remaining bits of the
59    /// current byte, no loop is needed — a single bitwise OR suffices.
60    pub fn write_bits(&mut self, value: u64, count: u8) {
61        debug_assert!(count <= 64, "write_bits: count must be <= 64");
62        if count == 0 {
63            return;
64        }
65
66        let remaining = 8 - self.bit_offset;
67
68        // Fast path: value fits entirely in the current byte.
69        // Mask to `count` bits (safe for count=8 since we branch),
70        // then shift into position within the byte.
71        if count <= remaining {
72            let masked = if count >= 8 {
73                value as u8
74            } else {
75                (value as u8) & ((1u8 << count) - 1)
76            };
77            self.current_byte |= masked << self.bit_offset;
78            self.bit_offset += count;
79            if self.bit_offset == 8 {
80                self.buf.push(self.current_byte);
81                self.current_byte = 0;
82                self.bit_offset = 0;
83            }
84            return;
85        }
86
87        // Slow path: value spans byte boundaries — write bits one at a time
88        let mut v = value;
89        for _ in 0..count {
90            let bit = (v & 1) as u8;
91            self.current_byte |= bit << self.bit_offset;
92            self.bit_offset += 1;
93            if self.bit_offset == 8 {
94                self.buf.push(self.current_byte);
95                self.current_byte = 0;
96                self.bit_offset = 0;
97            }
98            v >>= 1;
99        }
100    }
101
102    /// Write a single boolean as 1 bit.
103    pub fn write_bool(&mut self, v: bool) {
104        self.write_bits(v as u64, 1);
105    }
106
107    /// Flush any partial byte to the buffer.
108    ///
109    /// Special case per spec §4.1: if nothing has been written at all
110    /// (bit_offset == 0 AND buf is empty), push a zero byte anyway.
111    /// If bit_offset == 0 and buf is non-empty, this is a no-op.
112    pub fn flush_to_byte_boundary(&mut self) {
113        if self.bit_offset == 0 {
114            if self.buf.is_empty() {
115                self.buf.push(0x00);
116            }
117            // else: already aligned and something was written — no-op
118        } else {
119            self.buf.push(self.current_byte);
120            self.current_byte = 0;
121            self.bit_offset = 0;
122        }
123    }
124
125    /// Write a `u8`, aligning to a byte boundary first.
126    pub fn write_u8(&mut self, v: u8) {
127        self.align();
128        self.buf.push(v);
129    }
130
131    /// Write a `u16` in little-endian byte order, aligning first.
132    pub fn write_u16(&mut self, v: u16) {
133        self.align();
134        self.buf.extend_from_slice(&v.to_le_bytes());
135    }
136
137    /// Write a `u32` in little-endian byte order, aligning first.
138    pub fn write_u32(&mut self, v: u32) {
139        self.align();
140        self.buf.extend_from_slice(&v.to_le_bytes());
141    }
142
143    /// Write a `u64` in little-endian byte order, aligning first.
144    pub fn write_u64(&mut self, v: u64) {
145        self.align();
146        self.buf.extend_from_slice(&v.to_le_bytes());
147    }
148
149    /// Write an `i8`, aligning to a byte boundary first.
150    pub fn write_i8(&mut self, v: i8) {
151        self.align();
152        self.buf.extend_from_slice(&v.to_le_bytes());
153    }
154
155    /// Write an `i16` in little-endian byte order, aligning first.
156    pub fn write_i16(&mut self, v: i16) {
157        self.align();
158        self.buf.extend_from_slice(&v.to_le_bytes());
159    }
160
161    /// Write an `i32` in little-endian byte order, aligning first.
162    pub fn write_i32(&mut self, v: i32) {
163        self.align();
164        self.buf.extend_from_slice(&v.to_le_bytes());
165    }
166
167    /// Write an `i64` in little-endian byte order, aligning first.
168    pub fn write_i64(&mut self, v: i64) {
169        self.align();
170        self.buf.extend_from_slice(&v.to_le_bytes());
171    }
172
173    /// Write an f32, canonicalizing NaN to 0x7FC00000.
174    pub fn write_f32(&mut self, v: f32) {
175        self.align();
176        let bits: u32 = if v.is_nan() {
177            0x7FC00000u32
178        } else {
179            v.to_bits()
180        };
181        self.buf.extend_from_slice(&bits.to_le_bytes());
182    }
183
184    /// Write an f64, canonicalizing NaN to 0x7FF8000000000000.
185    pub fn write_f64(&mut self, v: f64) {
186        self.align();
187        let bits: u64 = if v.is_nan() {
188            0x7FF8000000000000u64
189        } else {
190            v.to_bits()
191        };
192        self.buf.extend_from_slice(&bits.to_le_bytes());
193    }
194
195    /// Write a LEB128-encoded unsigned integer.
196    pub fn write_leb128(&mut self, v: u64) {
197        self.align();
198        crate::leb128::encode(&mut self.buf, v);
199    }
200
201    /// Write a ZigZag + LEB128 encoded signed integer.
202    pub fn write_zigzag(&mut self, v: i64, type_bits: u8) {
203        let encoded = crate::zigzag::zigzag_encode(v, type_bits);
204        self.write_leb128(encoded);
205    }
206
207    /// Write a UTF-8 string with a LEB128 length prefix.
208    pub fn write_string(&mut self, s: &str) {
209        self.align();
210        crate::leb128::encode(&mut self.buf, s.len() as u64);
211        self.buf.extend_from_slice(s.as_bytes());
212    }
213
214    /// Write a byte slice with a LEB128 length prefix.
215    pub fn write_bytes(&mut self, data: &[u8]) {
216        self.align();
217        crate::leb128::encode(&mut self.buf, data.len() as u64);
218        self.buf.extend_from_slice(data);
219    }
220
221    /// Write raw bytes with no length prefix.
222    pub fn write_raw_bytes(&mut self, data: &[u8]) {
223        self.align();
224        self.buf.extend_from_slice(data);
225    }
226
227    /// Increment recursion depth; return error if limit exceeded.
228    pub fn enter_recursive(&mut self) -> Result<(), crate::error::EncodeError> {
229        self.recursion_depth += 1;
230        if self.recursion_depth > crate::MAX_RECURSION_DEPTH {
231            return Err(crate::error::EncodeError::RecursionLimitExceeded);
232        }
233        Ok(())
234    }
235
236    /// Decrement recursion depth.
237    pub fn leave_recursive(&mut self) {
238        self.recursion_depth = self.recursion_depth.saturating_sub(1);
239    }
240
241    /// Flush any partial byte and return the finished buffer.
242    pub fn finish(mut self) -> Vec<u8> {
243        self.flush_to_byte_boundary();
244        self.buf
245    }
246}
247
248impl Default for BitWriter {
249    fn default() -> Self {
250        Self::new()
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn write_single_bit_true() {
260        let mut w = BitWriter::new();
261        w.write_bool(true);
262        assert_eq!(w.finish(), [0x01]);
263    }
264
265    #[test]
266    fn write_single_bit_false() {
267        let mut w = BitWriter::new();
268        w.write_bool(false);
269        assert_eq!(w.finish(), [0x00]);
270    }
271
272    #[test]
273    fn write_bits_lsb_first() {
274        let mut w = BitWriter::new();
275        w.write_bits(5, 3); // 101
276        w.write_bits(19, 5); // 10011
277                             // LSB-first: byte = 10011_101 = 0x9D
278        assert_eq!(w.finish(), [0x9D]);
279    }
280
281    #[test]
282    fn write_bits_cross_byte_boundary() {
283        let mut w = BitWriter::new();
284        w.write_bits(5, 3);
285        w.write_bits(19, 5);
286        w.write_bits(42, 6); // 101010
287                             // Byte 0: 0x9D, Byte 1: 00_101010 = 0x2A
288        assert_eq!(w.finish(), [0x9D, 0x2A]);
289    }
290
291    #[test]
292    fn flush_to_byte_boundary_pads_zeros() {
293        let mut w = BitWriter::new();
294        w.write_bits(0b101, 3);
295        w.flush_to_byte_boundary();
296        w.write_bits(0xFF, 8);
297        assert_eq!(w.finish(), [0x05, 0xFF]);
298    }
299
300    #[test]
301    fn write_u8_flushes_first() {
302        let mut w = BitWriter::new();
303        w.write_bool(true);
304        w.write_u8(0xAB);
305        assert_eq!(w.finish(), [0x01, 0xAB]);
306    }
307
308    #[test]
309    fn write_u16_le() {
310        let mut w = BitWriter::new();
311        w.write_u16(0x0102);
312        assert_eq!(w.finish(), [0x02, 0x01]);
313    }
314
315    #[test]
316    fn write_u32_le() {
317        let mut w = BitWriter::new();
318        w.write_u32(0x01020304);
319        assert_eq!(w.finish(), [0x04, 0x03, 0x02, 0x01]);
320    }
321
322    #[test]
323    fn write_i16_negative() {
324        let mut w = BitWriter::new();
325        w.write_i16(-1);
326        assert_eq!(w.finish(), [0xFF, 0xFF]);
327    }
328
329    #[test]
330    fn write_f32_nan_canonicalized() {
331        let mut w = BitWriter::new();
332        w.write_f32(f32::NAN);
333        assert_eq!(w.finish(), [0x00, 0x00, 0xC0, 0x7F]);
334    }
335
336    #[test]
337    fn write_f64_nan_canonicalized() {
338        let mut w = BitWriter::new();
339        w.write_f64(f64::NAN);
340        assert_eq!(w.finish(), 0x7FF8000000000000u64.to_le_bytes());
341    }
342
343    #[test]
344    fn write_f32_negative_zero_preserved() {
345        let mut w = BitWriter::new();
346        w.write_f32(-0.0f32);
347        let buf = w.finish();
348        assert_eq!(buf, (-0.0f32).to_le_bytes());
349        assert_ne!(buf, 0.0f32.to_le_bytes());
350    }
351
352    #[test]
353    fn write_leb128_test() {
354        let mut w = BitWriter::new();
355        w.write_leb128(300);
356        assert_eq!(w.finish(), [0xAC, 0x02]);
357    }
358
359    #[test]
360    fn write_zigzag_neg1() {
361        let mut w = BitWriter::new();
362        w.write_zigzag(-1, 64);
363        assert_eq!(w.finish(), [0x01]);
364    }
365
366    #[test]
367    fn write_string_test() {
368        let mut w = BitWriter::new();
369        w.write_string("hi");
370        assert_eq!(w.finish(), [0x02, 0x68, 0x69]);
371    }
372
373    #[test]
374    fn write_bytes_test() {
375        let mut w = BitWriter::new();
376        w.write_bytes(&[0xDE, 0xAD]);
377        assert_eq!(w.finish(), [0x02, 0xDE, 0xAD]);
378    }
379
380    #[test]
381    fn write_raw_bytes_test() {
382        let mut w = BitWriter::new();
383        w.write_raw_bytes(&[0xCA, 0xFE]);
384        assert_eq!(w.finish(), [0xCA, 0xFE]);
385    }
386
387    #[test]
388    fn empty_flush_produces_zero_byte() {
389        let mut w = BitWriter::new();
390        w.flush_to_byte_boundary();
391        assert_eq!(w.finish(), [0x00]);
392    }
393
394    #[test]
395    fn recursion_depth_increment_decrement() {
396        let mut w = BitWriter::new();
397        w.enter_recursive().unwrap();
398        w.enter_recursive().unwrap();
399        w.leave_recursive();
400        w.leave_recursive();
401    }
402
403    #[test]
404    fn recursion_depth_max_64_succeeds() {
405        let mut w = BitWriter::new();
406        for _ in 0..64 {
407            w.enter_recursive().unwrap();
408        }
409    }
410
411    #[test]
412    fn recursion_depth_65_exceeds_limit() {
413        use crate::error::EncodeError;
414        let mut w = BitWriter::new();
415        for _ in 0..64 {
416            w.enter_recursive().unwrap();
417        }
418        assert_eq!(
419            w.enter_recursive().unwrap_err(),
420            EncodeError::RecursionLimitExceeded
421        );
422    }
423}