Skip to main content

vexil_runtime/
bit_reader.rs

1use crate::error::DecodeError;
2use crate::{MAX_BYTES_LENGTH, MAX_RECURSION_DEPTH};
3
4/// A cursor over a byte slice that reads fields LSB-first at the bit level.
5///
6/// Created with [`BitReader::new`], consumed with `read_*` methods. Tracks
7/// a byte position and a sub-byte bit offset, plus a recursion depth counter
8/// for safely decoding recursive types.
9///
10/// Sub-byte reads pull individual bits from the current byte. Multi-byte reads
11/// (e.g. [`read_u16`](Self::read_u16)) first align to the next byte boundary,
12/// then interpret the bytes as little-endian.
13pub struct BitReader<'a> {
14    data: &'a [u8],
15    byte_pos: usize,
16    bit_offset: u8,
17    recursion_depth: u32,
18}
19
20impl<'a> BitReader<'a> {
21    /// Create a new `BitReader` over the given byte slice.
22    pub fn new(data: &'a [u8]) -> Self {
23        Self {
24            data,
25            byte_pos: 0,
26            bit_offset: 0,
27            recursion_depth: 0,
28        }
29    }
30
31    /// Read `count` bits LSB-first into a u64.
32    ///
33    /// Fast path: if the requested bits fit entirely within the current byte,
34    /// extract them with a single mask+shift instead of looping.
35    pub fn read_bits(&mut self, count: u8) -> Result<u64, DecodeError> {
36        debug_assert!(count <= 64, "read_bits: count must be <= 64");
37        if count == 0 {
38            return Ok(0);
39        }
40
41        if self.byte_pos >= self.data.len() {
42            return Err(DecodeError::UnexpectedEof);
43        }
44
45        let remaining = 8 - self.bit_offset;
46
47        // Fast path: all requested bits are in the current byte
48        if count <= remaining {
49            let byte = self.data[self.byte_pos];
50            let mask = if count >= 8 {
51                u8::MAX
52            } else {
53                (1u8 << count) - 1
54            };
55            let result = u64::from((byte >> self.bit_offset) & mask);
56            self.bit_offset += count;
57            if self.bit_offset == 8 {
58                self.byte_pos += 1;
59                self.bit_offset = 0;
60            }
61            return Ok(result);
62        }
63
64        // Slow path: bits span byte boundaries
65        let mut result: u64 = 0;
66        for i in 0..count {
67            if self.byte_pos >= self.data.len() {
68                return Err(DecodeError::UnexpectedEof);
69            }
70            let bit = (self.data[self.byte_pos] >> self.bit_offset) & 1;
71            result |= u64::from(bit) << i;
72            self.bit_offset += 1;
73            if self.bit_offset == 8 {
74                self.byte_pos += 1;
75                self.bit_offset = 0;
76            }
77        }
78        Ok(result)
79    }
80
81    /// Read a single bit as bool.
82    pub fn read_bool(&mut self) -> Result<bool, DecodeError> {
83        Ok(self.read_bits(1)? != 0)
84    }
85
86    /// Advance to the next byte boundary, discarding any remaining bits in the current byte.
87    /// Infallible.
88    pub fn flush_to_byte_boundary(&mut self) {
89        if self.bit_offset > 0 {
90            self.byte_pos += 1;
91            self.bit_offset = 0;
92        }
93    }
94
95    /// Remaining bytes from byte_pos.
96    fn remaining(&self) -> usize {
97        self.data.len().saturating_sub(self.byte_pos)
98    }
99
100    /// Read a `u8`, aligning to a byte boundary first.
101    pub fn read_u8(&mut self) -> Result<u8, DecodeError> {
102        self.flush_to_byte_boundary();
103        if self.remaining() < 1 {
104            return Err(DecodeError::UnexpectedEof);
105        }
106        let v = self.data[self.byte_pos];
107        self.byte_pos += 1;
108        Ok(v)
109    }
110
111    /// Read a little-endian `u16`, aligning to a byte boundary first.
112    pub fn read_u16(&mut self) -> Result<u16, DecodeError> {
113        self.flush_to_byte_boundary();
114        if self.remaining() < 2 {
115            return Err(DecodeError::UnexpectedEof);
116        }
117        let bytes: [u8; 2] = self.data[self.byte_pos..self.byte_pos + 2]
118            .try_into()
119            .map_err(|_| DecodeError::UnexpectedEof)?;
120        self.byte_pos += 2;
121        Ok(u16::from_le_bytes(bytes))
122    }
123
124    /// Read a little-endian `u32`, aligning to a byte boundary first.
125    pub fn read_u32(&mut self) -> Result<u32, DecodeError> {
126        self.flush_to_byte_boundary();
127        if self.remaining() < 4 {
128            return Err(DecodeError::UnexpectedEof);
129        }
130        let bytes: [u8; 4] = self.data[self.byte_pos..self.byte_pos + 4]
131            .try_into()
132            .map_err(|_| DecodeError::UnexpectedEof)?;
133        self.byte_pos += 4;
134        Ok(u32::from_le_bytes(bytes))
135    }
136
137    /// Read a little-endian `u64`, aligning to a byte boundary first.
138    pub fn read_u64(&mut self) -> Result<u64, DecodeError> {
139        self.flush_to_byte_boundary();
140        if self.remaining() < 8 {
141            return Err(DecodeError::UnexpectedEof);
142        }
143        let bytes: [u8; 8] = self.data[self.byte_pos..self.byte_pos + 8]
144            .try_into()
145            .map_err(|_| DecodeError::UnexpectedEof)?;
146        self.byte_pos += 8;
147        Ok(u64::from_le_bytes(bytes))
148    }
149
150    /// Read an `i8`, aligning to a byte boundary first.
151    pub fn read_i8(&mut self) -> Result<i8, DecodeError> {
152        self.flush_to_byte_boundary();
153        if self.remaining() < 1 {
154            return Err(DecodeError::UnexpectedEof);
155        }
156        let bytes: [u8; 1] = [self.data[self.byte_pos]];
157        self.byte_pos += 1;
158        Ok(i8::from_le_bytes(bytes))
159    }
160
161    /// Read a little-endian `i16`, aligning to a byte boundary first.
162    pub fn read_i16(&mut self) -> Result<i16, DecodeError> {
163        self.flush_to_byte_boundary();
164        if self.remaining() < 2 {
165            return Err(DecodeError::UnexpectedEof);
166        }
167        let bytes: [u8; 2] = self.data[self.byte_pos..self.byte_pos + 2]
168            .try_into()
169            .map_err(|_| DecodeError::UnexpectedEof)?;
170        self.byte_pos += 2;
171        Ok(i16::from_le_bytes(bytes))
172    }
173
174    /// Read a little-endian `i32`, aligning to a byte boundary first.
175    pub fn read_i32(&mut self) -> Result<i32, DecodeError> {
176        self.flush_to_byte_boundary();
177        if self.remaining() < 4 {
178            return Err(DecodeError::UnexpectedEof);
179        }
180        let bytes: [u8; 4] = self.data[self.byte_pos..self.byte_pos + 4]
181            .try_into()
182            .map_err(|_| DecodeError::UnexpectedEof)?;
183        self.byte_pos += 4;
184        Ok(i32::from_le_bytes(bytes))
185    }
186
187    /// Read a little-endian `i64`, aligning to a byte boundary first.
188    pub fn read_i64(&mut self) -> Result<i64, DecodeError> {
189        self.flush_to_byte_boundary();
190        if self.remaining() < 8 {
191            return Err(DecodeError::UnexpectedEof);
192        }
193        let bytes: [u8; 8] = self.data[self.byte_pos..self.byte_pos + 8]
194            .try_into()
195            .map_err(|_| DecodeError::UnexpectedEof)?;
196        self.byte_pos += 8;
197        Ok(i64::from_le_bytes(bytes))
198    }
199
200    /// Read a little-endian `f32`, aligning to a byte boundary first.
201    pub fn read_f32(&mut self) -> Result<f32, DecodeError> {
202        self.flush_to_byte_boundary();
203        if self.remaining() < 4 {
204            return Err(DecodeError::UnexpectedEof);
205        }
206        let bytes: [u8; 4] = self.data[self.byte_pos..self.byte_pos + 4]
207            .try_into()
208            .map_err(|_| DecodeError::UnexpectedEof)?;
209        self.byte_pos += 4;
210        Ok(f32::from_le_bytes(bytes))
211    }
212
213    /// Read a little-endian `f64`, aligning to a byte boundary first.
214    pub fn read_f64(&mut self) -> Result<f64, DecodeError> {
215        self.flush_to_byte_boundary();
216        if self.remaining() < 8 {
217            return Err(DecodeError::UnexpectedEof);
218        }
219        let bytes: [u8; 8] = self.data[self.byte_pos..self.byte_pos + 8]
220            .try_into()
221            .map_err(|_| DecodeError::UnexpectedEof)?;
222        self.byte_pos += 8;
223        Ok(f64::from_le_bytes(bytes))
224    }
225
226    /// Read a LEB128-encoded u64, consuming at most `max_bytes` bytes.
227    pub fn read_leb128(&mut self, max_bytes: u8) -> Result<u64, DecodeError> {
228        self.flush_to_byte_boundary();
229        let (value, consumed) = crate::leb128::decode(&self.data[self.byte_pos..], max_bytes)?;
230        self.byte_pos += consumed;
231        Ok(value)
232    }
233
234    /// Read a ZigZag + LEB128 encoded signed integer.
235    pub fn read_zigzag(&mut self, _type_bits: u8, max_bytes: u8) -> Result<i64, DecodeError> {
236        let raw = self.read_leb128(max_bytes)?;
237        Ok(crate::zigzag::zigzag_decode(raw))
238    }
239
240    /// Read a length-prefixed UTF-8 string.
241    pub fn read_string(&mut self) -> Result<String, DecodeError> {
242        self.flush_to_byte_boundary();
243        let len = self.read_leb128(crate::MAX_LENGTH_PREFIX_BYTES)?;
244        if len > MAX_BYTES_LENGTH {
245            return Err(DecodeError::LimitExceeded {
246                field: "string",
247                limit: MAX_BYTES_LENGTH,
248                actual: len,
249            });
250        }
251        let len = len as usize;
252        if self.remaining() < len {
253            return Err(DecodeError::UnexpectedEof);
254        }
255        let bytes = self.data[self.byte_pos..self.byte_pos + len].to_vec();
256        self.byte_pos += len;
257        String::from_utf8(bytes).map_err(|_| DecodeError::InvalidUtf8)
258    }
259
260    /// Read a length-prefixed byte vector.
261    pub fn read_bytes(&mut self) -> Result<Vec<u8>, DecodeError> {
262        self.flush_to_byte_boundary();
263        let len = self.read_leb128(crate::MAX_LENGTH_PREFIX_BYTES)?;
264        if len > MAX_BYTES_LENGTH {
265            return Err(DecodeError::LimitExceeded {
266                field: "bytes",
267                limit: MAX_BYTES_LENGTH,
268                actual: len,
269            });
270        }
271        let len = len as usize;
272        if self.remaining() < len {
273            return Err(DecodeError::UnexpectedEof);
274        }
275        let bytes = self.data[self.byte_pos..self.byte_pos + len].to_vec();
276        self.byte_pos += len;
277        Ok(bytes)
278    }
279
280    /// Read exactly `len` raw bytes with no length prefix.
281    pub fn read_raw_bytes(&mut self, len: usize) -> Result<Vec<u8>, DecodeError> {
282        self.flush_to_byte_boundary();
283        if self.remaining() < len {
284            return Err(DecodeError::UnexpectedEof);
285        }
286        let bytes = self.data[self.byte_pos..self.byte_pos + len].to_vec();
287        self.byte_pos += len;
288        Ok(bytes)
289    }
290
291    /// Read `len` bytes as a zero-copy slice, aligning to a byte boundary first.
292    ///
293    /// The returned slice borrows from the original buffer (lifetime `'a`).
294    /// This is useful when you need to reference data without allocating.
295    pub fn read_bytes_ref(&mut self, len: usize) -> Result<&'a [u8], DecodeError> {
296        self.flush_to_byte_boundary();
297        if self.remaining() < len {
298            return Err(DecodeError::UnexpectedEof);
299        }
300        let slice = &self.data[self.byte_pos..self.byte_pos + len];
301        self.byte_pos += len;
302        Ok(slice)
303    }
304
305    /// Read a length-prefixed byte slice without copying.
306    ///
307    /// Reads a LEB128 length prefix, validates against [`MAX_BYTES_LENGTH`](crate::MAX_BYTES_LENGTH),
308    /// and returns a slice backed by the original buffer (lifetime `'a`).
309    ///
310    /// For invalid UTF-8, returns [`DecodeError::InvalidUtf8`].
311    pub fn read_bytes_var_ref(&mut self) -> Result<&'a [u8], DecodeError> {
312        self.flush_to_byte_boundary();
313        let len = self.read_leb128(crate::MAX_LENGTH_PREFIX_BYTES)?;
314        if len > MAX_BYTES_LENGTH {
315            return Err(DecodeError::LimitExceeded {
316                field: "bytes",
317                limit: MAX_BYTES_LENGTH,
318                actual: len,
319            });
320        }
321        let len = len as usize;
322        if self.remaining() < len {
323            return Err(DecodeError::UnexpectedEof);
324        }
325        let slice = &self.data[self.byte_pos..self.byte_pos + len];
326        self.byte_pos += len;
327        Ok(slice)
328    }
329
330    /// Read a length-prefixed UTF-8 string as a zero-copy reference.
331    ///
332    /// Reads a LEB128 length prefix, validates against [`MAX_BYTES_LENGTH`](crate::MAX_BYTES_LENGTH),
333    /// validates UTF-8, and returns a `&str` backed by the original buffer (lifetime `'a`).
334    ///
335    /// For invalid UTF-8, returns [`DecodeError::InvalidUtf8`].
336    pub fn read_string_ref(&mut self) -> Result<&'a str, DecodeError> {
337        self.flush_to_byte_boundary();
338        let len = self.read_leb128(crate::MAX_LENGTH_PREFIX_BYTES)?;
339        if len > MAX_BYTES_LENGTH {
340            return Err(DecodeError::LimitExceeded {
341                field: "string",
342                limit: MAX_BYTES_LENGTH,
343                actual: len,
344            });
345        }
346        let len = len as usize;
347        if self.remaining() < len {
348            return Err(DecodeError::UnexpectedEof);
349        }
350        let bytes = &self.data[self.byte_pos..self.byte_pos + len];
351        let s = std::str::from_utf8(bytes).map_err(|_| DecodeError::InvalidUtf8)?;
352        self.byte_pos += len;
353        Ok(s)
354    }
355
356    /// Read all remaining bytes from the current position to the end.
357    /// Flushes to byte boundary first. Returns an empty Vec if no bytes remain.
358    pub fn read_remaining(&mut self) -> Vec<u8> {
359        self.flush_to_byte_boundary();
360        let remaining = self.data.len().saturating_sub(self.byte_pos);
361        if remaining == 0 {
362            return Vec::new();
363        }
364        let result = self.data[self.byte_pos..].to_vec();
365        self.byte_pos = self.data.len();
366        result
367    }
368
369    /// Increment recursion depth; return error if limit exceeded.
370    pub fn enter_recursive(&mut self) -> Result<(), DecodeError> {
371        self.recursion_depth += 1;
372        if self.recursion_depth > MAX_RECURSION_DEPTH {
373            return Err(DecodeError::RecursionLimitExceeded);
374        }
375        Ok(())
376    }
377
378    /// Decrement recursion depth.
379    pub fn leave_recursive(&mut self) {
380        self.recursion_depth = self.recursion_depth.saturating_sub(1);
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    use crate::BitWriter;
388
389    #[test]
390    fn read_single_bit() {
391        let mut r = BitReader::new(&[0x01]);
392        assert!(r.read_bool().unwrap());
393    }
394
395    #[test]
396    fn round_trip_sub_byte() {
397        let mut w = BitWriter::new();
398        w.write_bits(5, 3);
399        w.write_bits(19, 5);
400        w.write_bits(42, 6);
401        let buf = w.finish();
402        let mut r = BitReader::new(&buf);
403        assert_eq!(r.read_bits(3).unwrap(), 5);
404        assert_eq!(r.read_bits(5).unwrap(), 19);
405        assert_eq!(r.read_bits(6).unwrap(), 42);
406    }
407
408    #[test]
409    fn round_trip_u16() {
410        let mut w = BitWriter::new();
411        w.write_u16(0x1234);
412        let b = w.finish();
413        assert_eq!(BitReader::new(&b).read_u16().unwrap(), 0x1234);
414    }
415
416    #[test]
417    fn round_trip_i32_neg() {
418        let mut w = BitWriter::new();
419        w.write_i32(-42);
420        let b = w.finish();
421        assert_eq!(BitReader::new(&b).read_i32().unwrap(), -42);
422    }
423
424    #[test]
425    fn round_trip_f32() {
426        let mut w = BitWriter::new();
427        w.write_f32(std::f32::consts::PI);
428        let b = w.finish();
429        assert_eq!(BitReader::new(&b).read_f32().unwrap(), std::f32::consts::PI);
430    }
431
432    #[test]
433    fn round_trip_f64_nan() {
434        let mut w = BitWriter::new();
435        w.write_f64(f64::NAN);
436        let b = w.finish();
437        let v = BitReader::new(&b).read_f64().unwrap();
438        assert!(v.is_nan());
439        assert_eq!(v.to_bits(), 0x7FF8000000000000);
440    }
441
442    #[test]
443    fn round_trip_string() {
444        let mut w = BitWriter::new();
445        w.write_string("hello");
446        let b = w.finish();
447        assert_eq!(BitReader::new(&b).read_string().unwrap(), "hello");
448    }
449
450    #[test]
451    fn round_trip_leb128() {
452        let mut w = BitWriter::new();
453        w.write_leb128(300);
454        let b = w.finish();
455        assert_eq!(BitReader::new(&b).read_leb128(4).unwrap(), 300);
456    }
457
458    #[test]
459    fn round_trip_zigzag() {
460        let mut w = BitWriter::new();
461        w.write_zigzag(-42, 64);
462        let b = w.finish();
463        assert_eq!(BitReader::new(&b).read_zigzag(64, 10).unwrap(), -42);
464    }
465
466    #[test]
467    fn unexpected_eof() {
468        assert_eq!(
469            BitReader::new(&[]).read_u8().unwrap_err(),
470            DecodeError::UnexpectedEof
471        );
472    }
473
474    #[test]
475    fn invalid_utf8() {
476        let mut w = BitWriter::new();
477        w.write_leb128(2);
478        w.write_raw_bytes(&[0xFF, 0xFE]);
479        let b = w.finish();
480        assert_eq!(
481            BitReader::new(&b).read_string().unwrap_err(),
482            DecodeError::InvalidUtf8
483        );
484    }
485
486    #[test]
487    fn recursion_depth_limit() {
488        let mut r = BitReader::new(&[]);
489        for _ in 0..64 {
490            r.enter_recursive().unwrap();
491        }
492        assert_eq!(
493            r.enter_recursive().unwrap_err(),
494            DecodeError::RecursionLimitExceeded
495        );
496    }
497
498    #[test]
499    fn recursion_depth_leave() {
500        let mut r = BitReader::new(&[]);
501        for _ in 0..64 {
502            r.enter_recursive().unwrap();
503        }
504        r.leave_recursive();
505        r.enter_recursive().unwrap();
506    }
507
508    #[test]
509    fn trailing_bytes_not_rejected() {
510        // Simulate v2-encoded message read by v1 decoder:
511        // v2 wrote u32(42) + u16(99), v1 only reads u32(42)
512        let data = [0x2a, 0x00, 0x00, 0x00, 0x63, 0x00];
513        let mut r = BitReader::new(&data);
514        let x = r.read_u32().unwrap();
515        assert_eq!(x, 42);
516        r.flush_to_byte_boundary();
517        // Remaining bytes (0x63, 0x00) must not cause error.
518        // BitReader can be dropped with unread data — no panic.
519    }
520
521    #[test]
522    fn read_remaining_after_partial_decode() {
523        let data = [0x2a, 0x00, 0x00, 0x00, 0x63, 0x00];
524        let mut r = BitReader::new(&data);
525        let _x = r.read_u32().unwrap();
526        let remaining = r.read_remaining();
527        assert_eq!(remaining, vec![0x63, 0x00]);
528    }
529
530    #[test]
531    fn read_remaining_when_fully_consumed() {
532        let data = [0x2a, 0x00, 0x00, 0x00];
533        let mut r = BitReader::new(&data);
534        let _x = r.read_u32().unwrap();
535        let remaining = r.read_remaining();
536        assert!(remaining.is_empty());
537    }
538
539    #[test]
540    fn read_remaining_from_start() {
541        let data = [0x01, 0x02, 0x03];
542        let mut r = BitReader::new(&data);
543        let remaining = r.read_remaining();
544        assert_eq!(remaining, vec![0x01, 0x02, 0x03]);
545    }
546
547    #[test]
548    fn read_bytes_ref_basic() {
549        // read_bytes_ref reads raw bytes with no length prefix
550        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0xFF];
551        let mut r = BitReader::new(&data);
552        let slice = r.read_bytes_ref(5).unwrap();
553        assert_eq!(slice, &[0x01, 0x02, 0x03, 0x04, 0x05]);
554        // Verify the slice has the correct lifetime (borrowed from input)
555        assert_eq!(slice.as_ptr(), data[0..5].as_ptr());
556        // Reader positioned after the slice
557        assert_eq!(r.read_u8().unwrap(), 0xFF);
558    }
559
560    #[test]
561    fn read_bytes_ref_eof() {
562        let data = [0x01, 0x02];
563        let mut r = BitReader::new(&data);
564        assert_eq!(r.read_bytes_ref(5).unwrap_err(), DecodeError::UnexpectedEof);
565    }
566
567    #[test]
568    fn read_bytes_var_ref_roundtrip() {
569        let mut w = BitWriter::new();
570        w.write_bytes(&[0x01, 0x02, 0x03, 0x04]);
571        let b = w.finish();
572        let mut r = BitReader::new(&b);
573        let slice = r.read_bytes_var_ref().unwrap();
574        assert_eq!(slice, &[0x01, 0x02, 0x03, 0x04]);
575    }
576
577    #[test]
578    fn read_bytes_var_ref_zero_copy() {
579        let data = [0x04, 0x41, 0x42, 0x43, 0x44]; // LEB128(4) + "ABCD"
580        let mut r = BitReader::new(&data);
581        let slice = r.read_bytes_var_ref().unwrap();
582        // Verify zero-copy: slice points into original buffer
583        assert_eq!(slice.as_ptr(), data[1..5].as_ptr());
584    }
585
586    #[test]
587    fn read_bytes_var_ref_limit_exceeded() {
588        let mut w = BitWriter::new();
589        w.write_leb128(MAX_BYTES_LENGTH + 1);
590        let b = w.finish();
591        let mut r = BitReader::new(&b);
592        assert_eq!(
593            r.read_bytes_var_ref().unwrap_err(),
594            DecodeError::LimitExceeded {
595                field: "bytes",
596                limit: MAX_BYTES_LENGTH,
597                actual: MAX_BYTES_LENGTH + 1,
598            }
599        );
600    }
601
602    #[test]
603    fn read_string_ref_roundtrip() {
604        let mut w = BitWriter::new();
605        w.write_string("hello");
606        let b = w.finish();
607        let mut r = BitReader::new(&b);
608        let s = r.read_string_ref().unwrap();
609        assert_eq!(s, "hello");
610    }
611
612    #[test]
613    fn read_string_ref_zero_copy() {
614        let data = [0x05, b'h', b'e', b'l', b'l', b'o']; // LEB128(5) + "hello"
615        let mut r = BitReader::new(&data);
616        let s = r.read_string_ref().unwrap();
617        // Verify zero-copy: string points into original buffer
618        assert_eq!(s.as_ptr(), data[1..6].as_ptr());
619    }
620
621    #[test]
622    fn read_string_ref_invalid_utf8() {
623        let mut w = BitWriter::new();
624        w.write_leb128(2);
625        w.write_raw_bytes(&[0xFF, 0xFE]);
626        let b = w.finish();
627        let mut r = BitReader::new(&b);
628        assert_eq!(r.read_string_ref().unwrap_err(), DecodeError::InvalidUtf8);
629    }
630
631    #[test]
632    fn read_string_ref_limit_exceeded() {
633        let mut w = BitWriter::new();
634        w.write_leb128(MAX_BYTES_LENGTH + 1);
635        let b = w.finish();
636        let mut r = BitReader::new(&b);
637        assert_eq!(
638            r.read_string_ref().unwrap_err(),
639            DecodeError::LimitExceeded {
640                field: "string",
641                limit: MAX_BYTES_LENGTH,
642                actual: MAX_BYTES_LENGTH + 1,
643            }
644        );
645    }
646
647    #[test]
648    fn read_string_ref_eof_mid_string() {
649        // Length prefix says 10 bytes, but only 3 available
650        let mut w = BitWriter::new();
651        w.write_leb128(10);
652        w.write_raw_bytes(&[0x41, 0x42, 0x43]); // "ABC"
653        let b = w.finish();
654        let mut r = BitReader::new(&b);
655        assert_eq!(r.read_string_ref().unwrap_err(), DecodeError::UnexpectedEof);
656    }
657
658    #[test]
659    fn zero_copy_methods_after_bit_reads() {
660        // Test that zero-copy methods properly flush to byte boundary
661        let mut w = BitWriter::new();
662        w.write_bits(0b101, 3); // 3 bits
663        w.flush_to_byte_boundary();
664        w.write_string("test");
665        let b = w.finish();
666
667        let mut r = BitReader::new(&b);
668        assert_eq!(r.read_bits(3).unwrap(), 0b101);
669        // read_string_ref should flush and read correctly
670        let s = r.read_string_ref().unwrap();
671        assert_eq!(s, "test");
672    }
673
674    #[test]
675    fn read_string_equivalence() {
676        // Ensure read_string and read_string_ref produce equivalent results
677        let mut w = BitWriter::new();
678        w.write_string("vexil rocks 🚀");
679        let b = w.finish();
680
681        let mut r1 = BitReader::new(&b);
682        let mut r2 = BitReader::new(&b);
683
684        let owned = r1.read_string().unwrap();
685        let borrowed = r2.read_string_ref().unwrap();
686
687        assert_eq!(owned, borrowed);
688    }
689
690    #[test]
691    fn read_bytes_equivalence() {
692        // Ensure read_bytes and read_bytes_var_ref produce equivalent results
693        let mut w = BitWriter::new();
694        w.write_bytes(&[0x00, 0x01, 0x02, 0x03]);
695        let b = w.finish();
696
697        let mut r1 = BitReader::new(&b);
698        let mut r2 = BitReader::new(&b);
699
700        let owned = r1.read_bytes().unwrap();
701        let borrowed = r2.read_bytes_var_ref().unwrap();
702
703        assert_eq!(owned, borrowed);
704    }
705}