Skip to main content

boon/io/
bitreader.rs

1use crate::error::{Error, Result};
2
3// Source 2 coordinate encoding constants. A coordinate is encoded as an
4// optional integer part (14 bits) plus an optional fractional part (5 bits).
5const COORD_INTEGER_BITS: usize = 14;
6const COORD_FRACTIONAL_BITS: usize = 5;
7const COORD_DENOMINATOR: f32 = (1 << COORD_FRACTIONAL_BITS) as f32;
8const COORD_RESOLUTION: f32 = 1.0 / COORD_DENOMINATOR;
9
10// Source 2 normal encoding constants. A normal component is a sign bit
11// followed by an 11-bit fractional value in [0, 1].
12const NORMAL_FRACTIONAL_BITS: usize = 11;
13const NORMAL_DENOMINATOR: f32 = ((1 << NORMAL_FRACTIONAL_BITS) - 1) as f32;
14const NORMAL_RESOLUTION: f32 = 1.0 / NORMAL_DENOMINATOR;
15
16/// High-performance bit-level reader over a byte slice.
17///
18/// Uses safe unaligned 64-bit reads via `u64::from_le_bytes` for fast
19/// bit extraction. Every read returns `Result<T>` with overflow checking.
20pub struct BitReader<'a> {
21    data: &'a [u8],
22    position: usize,
23    total_bits: usize,
24}
25
26impl<'a> BitReader<'a> {
27    /// Create a new reader starting at bit 0 of `data`.
28    #[inline]
29    pub fn new(data: &'a [u8]) -> Self {
30        Self {
31            data,
32            position: 0,
33            total_bits: data.len() * 8,
34        }
35    }
36
37    /// Number of bits between the cursor and the end of the buffer.
38    #[inline]
39    pub fn bits_remaining(&self) -> usize {
40        self.total_bits.saturating_sub(self.position)
41    }
42
43    /// Current bit offset from the start of the buffer.
44    #[inline]
45    pub fn position(&self) -> usize {
46        self.position
47    }
48
49    /// Read up to 64 bits. Returns the value right-aligned in a u64.
50    #[inline]
51    pub fn read_bits(&mut self, n: usize) -> Result<u64> {
52        if n == 0 {
53            return Ok(0);
54        }
55        if n > 64 || self.position + n > self.total_bits {
56            return Err(Error::Overflow {
57                needed: n,
58                available: self.bits_remaining(),
59            });
60        }
61
62        let value = self.peek_bits_unchecked(n);
63        self.position += n;
64        Ok(value)
65    }
66
67    /// Peek at up to 64 bits without advancing the position.
68    #[inline]
69    pub fn peek_bits(&self, n: usize) -> Result<u64> {
70        if n == 0 {
71            return Ok(0);
72        }
73        if n > 64 || self.position + n > self.total_bits {
74            return Err(Error::Overflow {
75                needed: n,
76                available: self.bits_remaining(),
77            });
78        }
79        Ok(self.peek_bits_unchecked(n))
80    }
81
82    /// Internal: peek without bounds checking.
83    ///
84    /// Reads a little-endian u64 from the byte at `position / 8`, shifts right by
85    /// the intra-byte bit offset, and masks to `n` bits. The 8-byte window only
86    /// holds `64 - bit_offset` valid bits after the shift, so a read that needs
87    /// more (only possible for `n > 56` at a non-zero offset — e.g. a 64-bit read
88    /// mid-stream) pulls the extra high bits from the following byte; without that
89    /// they would be silently dropped.
90    #[inline(always)]
91    fn peek_bits_unchecked(&self, n: usize) -> u64 {
92        let byte_pos = self.position / 8;
93        let bit_offset = self.position % 8;
94        let remaining_bytes = self.data.len() - byte_pos;
95
96        let mut buf = [0u8; 8];
97        if remaining_bytes >= 8 {
98            buf.copy_from_slice(&self.data[byte_pos..byte_pos + 8]);
99        } else {
100            buf[..remaining_bytes]
101                .copy_from_slice(&self.data[byte_pos..byte_pos + remaining_bytes]);
102        }
103        let low = u64::from_le_bytes(buf) >> bit_offset;
104        if bit_offset + n <= 64 {
105            return low & mask(n);
106        }
107        let taken = 64 - bit_offset;
108        let hi = self.data.get(byte_pos + 8).copied().unwrap_or(0) as u64 & mask(n - taken);
109        (low | (hi << taken)) & mask(n)
110    }
111
112    /// Read a single bit as a boolean.
113    #[inline]
114    pub fn read_bool(&mut self) -> Result<bool> {
115        Ok(self.read_bits(1)? != 0)
116    }
117
118    /// Read 8 bits as a `u8`.
119    #[inline]
120    pub fn read_u8(&mut self) -> Result<u8> {
121        Ok(self.read_bits(8)? as u8)
122    }
123
124    /// Read 16 bits as a little-endian `u16`.
125    #[inline]
126    pub fn read_u16(&mut self) -> Result<u16> {
127        Ok(self.read_bits(16)? as u16)
128    }
129
130    /// Read 32 bits as a little-endian `u32`.
131    #[inline]
132    pub fn read_u32(&mut self) -> Result<u32> {
133        Ok(self.read_bits(32)? as u32)
134    }
135
136    /// Read 64 bits as a little-endian `u64`.
137    #[inline]
138    pub fn read_u64(&mut self) -> Result<u64> {
139        self.read_bits(64)
140    }
141
142    /// Read 32 bits and reinterpret as an IEEE 754 `f32`.
143    #[inline]
144    pub fn read_f32(&mut self) -> Result<f32> {
145        Ok(f32::from_bits(self.read_bits(32)? as u32))
146    }
147
148    /// Read N bytes into the provided buffer.
149    pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {
150        let needed = buf.len() * 8;
151        if self.position + needed > self.total_bits {
152            return Err(Error::Overflow {
153                needed,
154                available: self.bits_remaining(),
155            });
156        }
157
158        // Fast path: byte-aligned — direct memcpy.
159        if self.position.is_multiple_of(8) {
160            let byte_pos = self.position / 8;
161            buf.copy_from_slice(&self.data[byte_pos..byte_pos + buf.len()]);
162            self.position += needed;
163            return Ok(());
164        }
165
166        // Slow path: unaligned — read byte at a time via bit extraction.
167        for byte in buf.iter_mut() {
168            *byte = self.peek_bits_unchecked(8) as u8;
169            self.position += 8;
170        }
171        Ok(())
172    }
173
174    /// Read a specified number of bits into a byte buffer, filling LSB-first.
175    pub fn read_bits_to_bytes(&mut self, buf: &mut [u8], bits: usize) -> Result<()> {
176        let full_bytes = bits / 8;
177        let remaining_bits = bits % 8;
178
179        if remaining_bits == 0 {
180            return self.read_bytes(&mut buf[..full_bytes]);
181        }
182
183        // Has trailing bits — can still fast-path the full bytes.
184        if full_bytes > 0 {
185            self.read_bytes(&mut buf[..full_bytes])?;
186        }
187        buf[full_bytes] = self.read_bits(remaining_bits)? as u8;
188        Ok(())
189    }
190
191    /// Read an unsigned varint (up to 32 bits).
192    pub fn read_uvarint32(&mut self) -> Result<u32> {
193        let mut result: u32 = 0;
194        for i in 0..5 {
195            let byte = self.read_u8()? as u32;
196            result |= (byte & 0x7F) << (7 * i);
197            if byte & 0x80 == 0 {
198                return Ok(result);
199            }
200        }
201        Ok(result)
202    }
203
204    /// Read an unsigned varint (up to 64 bits).
205    pub fn read_uvarint64(&mut self) -> Result<u64> {
206        let mut result: u64 = 0;
207        for i in 0..10 {
208            let byte = self.read_u8()? as u64;
209            result |= (byte & 0x7F) << (7 * i);
210            if byte & 0x80 == 0 {
211                return Ok(result);
212            }
213        }
214        Ok(result)
215    }
216
217    /// Read a signed varint (zigzag encoded, 32-bit).
218    pub fn read_varint32(&mut self) -> Result<i32> {
219        let n = self.read_uvarint32()?;
220        Ok(((n >> 1) as i32) ^ -((n & 1) as i32))
221    }
222
223    /// Read a signed varint (zigzag encoded, 64-bit).
224    pub fn read_varint64(&mut self) -> Result<i64> {
225        let n = self.read_uvarint64()?;
226        Ok(((n >> 1) as i64) ^ -((n & 1) as i64))
227    }
228
229    /// Valve's variable-length unsigned integer encoding.
230    ///
231    /// Reads 6 bits; bits 4-5 select the total width:
232    /// `00` → 6 bits, `01` → 4+4, `10` → 4+8, `11` → 4+28.
233    pub fn read_ubitvar(&mut self) -> Result<u32> {
234        let ret = self.read_bits(6)? as u32;
235        match ret & (16 | 32) {
236            16 => Ok((ret & 15) | (self.read_bits(4)? as u32) << 4),
237            32 => Ok((ret & 15) | (self.read_bits(8)? as u32) << 4),
238            48 => Ok((ret & 15) | (self.read_bits(28)? as u32) << 4),
239            _ => Ok(ret),
240        }
241    }
242
243    /// Field-path variant of ubitvar — cascading 1-bit selectors.
244    ///
245    /// Used exclusively for encoding field path operation indices:
246    /// 2, 4, 10, 17, or 31 bits depending on which prefix bit is set.
247    pub fn read_ubitvarfp(&mut self) -> Result<u32> {
248        if self.read_bool()? {
249            return Ok(self.read_bits(2)? as u32);
250        }
251        if self.read_bool()? {
252            return Ok(self.read_bits(4)? as u32);
253        }
254        if self.read_bool()? {
255            return Ok(self.read_bits(10)? as u32);
256        }
257        if self.read_bool()? {
258            return Ok(self.read_bits(17)? as u32);
259        }
260        Ok(self.read_bits(31)? as u32)
261    }
262
263    /// Read a coordinate value.
264    pub fn read_bitcoord(&mut self) -> Result<f32> {
265        let has_int = self.read_bool()?;
266        let has_frac = self.read_bool()?;
267
268        if !has_int && !has_frac {
269            return Ok(0.0);
270        }
271
272        let negative = self.read_bool()?;
273        let mut value = 0.0f32;
274
275        if has_int {
276            value += self.read_bits(COORD_INTEGER_BITS)? as f32 + 1.0;
277        }
278        if has_frac {
279            value += self.read_bits(COORD_FRACTIONAL_BITS)? as f32 * COORD_RESOLUTION;
280        }
281
282        if negative {
283            value = -value;
284        }
285
286        Ok(value)
287    }
288
289    /// Read a normal component (sign + 11-bit fraction).
290    pub fn read_bitnormal(&mut self) -> Result<f32> {
291        let negative = self.read_bool()?;
292        let frac = self.read_bits(NORMAL_FRACTIONAL_BITS)? as f32;
293        let mut value = frac * NORMAL_RESOLUTION;
294        if negative {
295            value = -value;
296        }
297        Ok(value)
298    }
299
300    /// Read a 3D coordinate vector.
301    pub fn read_bitvec3coord(&mut self) -> Result<[f32; 3]> {
302        let has_x = self.read_bool()?;
303        let has_y = self.read_bool()?;
304        let has_z = self.read_bool()?;
305
306        let x = if has_x { self.read_bitcoord()? } else { 0.0 };
307        let y = if has_y { self.read_bitcoord()? } else { 0.0 };
308        let z = if has_z { self.read_bitcoord()? } else { 0.0 };
309
310        Ok([x, y, z])
311    }
312
313    /// Read a 3D normal vector (2 components + derived Z).
314    pub fn read_bitvec3normal(&mut self) -> Result<[f32; 3]> {
315        let has_x = self.read_bool()?;
316        let has_y = self.read_bool()?;
317
318        let x = if has_x { self.read_bitnormal()? } else { 0.0 };
319        let y = if has_y { self.read_bitnormal()? } else { 0.0 };
320
321        let z_sign = self.read_bool()?;
322        let z_sq = 1.0 - x * x - y * y;
323        let z = if z_sq > 0.0 { z_sq.sqrt() } else { 0.0 };
324        let z = if z_sign { -z } else { z };
325
326        Ok([x, y, z])
327    }
328
329    /// Read an angle encoded as N bits, returning degrees in [0, 360).
330    pub fn read_bitangle(&mut self, n: usize) -> Result<f32> {
331        let raw = self.read_bits(n)? as f32;
332        let shift = (1u64 << n) as f32;
333        Ok(raw * 360.0 / shift)
334    }
335
336    /// Read a null-terminated string.
337    pub fn read_string(&mut self) -> Result<String> {
338        let mut bytes = Vec::new();
339        loop {
340            let b = self.read_u8()?;
341            if b == 0 {
342                break;
343            }
344            bytes.push(b);
345        }
346        Ok(String::from_utf8_lossy(&bytes).into_owned())
347    }
348
349    /// Read a string into the provided buffer, returning bytes written (excluding null).
350    pub fn read_string_into(&mut self, buf: &mut [u8]) -> Result<usize> {
351        let mut i = 0;
352        loop {
353            let b = self.read_u8()?;
354            if b == 0 {
355                break;
356            }
357            if i < buf.len() {
358                buf[i] = b;
359                i += 1;
360            }
361        }
362        Ok(i)
363    }
364
365    /// Read a string as raw bytes into a Vec, returning bytes written (excluding null).
366    pub fn read_string_raw(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
367        let start = buf.len();
368        loop {
369            let b = self.read_u8()?;
370            if b == 0 {
371                break;
372            }
373            buf.push(b);
374        }
375        Ok(buf.len() - start)
376    }
377
378    /// Skip forward by N bits.
379    pub fn skip_bits(&mut self, n: usize) -> Result<()> {
380        if self.position + n > self.total_bits {
381            return Err(Error::Overflow {
382                needed: n,
383                available: self.bits_remaining(),
384            });
385        }
386        self.position += n;
387        Ok(())
388    }
389
390    /// Skip a varint without decoding it.
391    pub fn skip_varint(&mut self) -> Result<()> {
392        for _ in 0..10 {
393            let byte = self.read_u8()?;
394            if byte & 0x80 == 0 {
395                return Ok(());
396            }
397        }
398        Ok(())
399    }
400
401    /// Skip a bitcoord value.
402    pub fn skip_bitcoord(&mut self) -> Result<()> {
403        let has_int = self.read_bool()?;
404        let has_frac = self.read_bool()?;
405
406        if !has_int && !has_frac {
407            return Ok(());
408        }
409
410        self.skip_bits(1)?; // negative flag
411
412        if has_int {
413            self.skip_bits(COORD_INTEGER_BITS)?;
414        }
415        if has_frac {
416            self.skip_bits(COORD_FRACTIONAL_BITS)?;
417        }
418
419        Ok(())
420    }
421
422    /// Skip a bitnormal value.
423    pub fn skip_bitnormal(&mut self) -> Result<()> {
424        self.skip_bits(1 + NORMAL_FRACTIONAL_BITS)
425    }
426
427    /// Skip a 3D coordinate vector.
428    pub fn skip_bitvec3coord(&mut self) -> Result<()> {
429        let has_x = self.read_bool()?;
430        let has_y = self.read_bool()?;
431        let has_z = self.read_bool()?;
432
433        if has_x {
434            self.skip_bitcoord()?;
435        }
436        if has_y {
437            self.skip_bitcoord()?;
438        }
439        if has_z {
440            self.skip_bitcoord()?;
441        }
442
443        Ok(())
444    }
445
446    /// Skip a 3D normal vector.
447    pub fn skip_bitvec3normal(&mut self) -> Result<()> {
448        let has_x = self.read_bool()?;
449        let has_y = self.read_bool()?;
450
451        if has_x {
452            self.skip_bitnormal()?;
453        }
454        if has_y {
455            self.skip_bitnormal()?;
456        }
457
458        self.skip_bits(1)?; // z_sign
459
460        Ok(())
461    }
462
463    /// Skip a null-terminated string.
464    pub fn skip_string(&mut self) -> Result<()> {
465        loop {
466            let b = self.read_u8()?;
467            if b == 0 {
468                return Ok(());
469            }
470        }
471    }
472}
473
474/// Create a bitmask with n bits set.
475#[inline(always)]
476fn mask(n: usize) -> u64 {
477    if n >= 64 { u64::MAX } else { (1u64 << n) - 1 }
478}
479
480#[cfg(test)]
481mod tests {
482    use super::*;
483
484    #[test]
485    fn test_read_bits_basic() {
486        let data = [0b10110100, 0b11001010];
487        let mut br = BitReader::new(&data);
488
489        assert_eq!(br.read_bits(1).unwrap(), 0);
490        assert_eq!(br.read_bits(1).unwrap(), 0);
491        assert_eq!(br.read_bits(1).unwrap(), 1);
492        assert_eq!(br.read_bits(1).unwrap(), 0);
493        assert_eq!(br.read_bits(1).unwrap(), 1);
494        assert_eq!(br.read_bits(1).unwrap(), 1);
495        assert_eq!(br.read_bits(1).unwrap(), 0);
496        assert_eq!(br.read_bits(1).unwrap(), 1);
497    }
498
499    #[test]
500    fn test_read_byte() {
501        let data = [0xAB, 0xCD];
502        let mut br = BitReader::new(&data);
503        assert_eq!(br.read_u8().unwrap(), 0xAB);
504        assert_eq!(br.read_u8().unwrap(), 0xCD);
505    }
506
507    #[test]
508    fn test_read_across_boundary() {
509        let data = [0xFF, 0x00, 0xFF];
510        let mut br = BitReader::new(&data);
511        br.read_bits(4).unwrap();
512        let val = br.read_bits(8).unwrap();
513        assert_eq!(val, 0x0F);
514    }
515
516    #[test]
517    fn test_read_bool() {
518        let data = [0b10000001];
519        let mut br = BitReader::new(&data);
520        assert!(br.read_bool().unwrap());
521        assert!(!br.read_bool().unwrap());
522    }
523
524    #[test]
525    fn test_overflow() {
526        let data = [0xFF];
527        let mut br = BitReader::new(&data);
528        br.read_bits(8).unwrap();
529        assert!(br.read_bits(1).is_err());
530    }
531
532    #[test]
533    fn test_uvarint32() {
534        // Encode 300 as varint: 300 = 0b100101100
535        // byte 0: 10101100 (0xAC), byte 1: 00000010 (0x02)
536        let data = [0xAC, 0x02];
537        let mut br = BitReader::new(&data);
538        assert_eq!(br.read_uvarint32().unwrap(), 300);
539    }
540
541    #[test]
542    fn test_varint32_negative() {
543        // zigzag(-1) = 1
544        let data = [0x01];
545        let mut br = BitReader::new(&data);
546        assert_eq!(br.read_varint32().unwrap(), -1);
547    }
548
549    #[test]
550    fn test_varint32_positive() {
551        // zigzag(1) = 2
552        let data = [0x02];
553        let mut br = BitReader::new(&data);
554        assert_eq!(br.read_varint32().unwrap(), 1);
555    }
556
557    #[test]
558    fn test_read_f32() {
559        let val: f32 = 1.5;
560        let data = val.to_bits().to_le_bytes();
561        let mut br = BitReader::new(&data);
562        let read_val = br.read_f32().unwrap();
563        assert!((read_val - val).abs() < f32::EPSILON);
564    }
565
566    #[test]
567    fn test_read_string() {
568        let data = b"hello\0world";
569        let mut br = BitReader::new(data);
570        assert_eq!(br.read_string().unwrap(), "hello");
571    }
572
573    #[test]
574    fn test_bits_remaining() {
575        let data = [0xFF, 0xFF];
576        let mut br = BitReader::new(&data);
577        assert_eq!(br.bits_remaining(), 16);
578        br.read_bits(5).unwrap();
579        assert_eq!(br.bits_remaining(), 11);
580    }
581
582    #[test]
583    fn test_skip_bits() {
584        let data = [0b11110000, 0b10101010];
585        let mut br = BitReader::new(&data);
586        br.skip_bits(4).unwrap();
587        assert_eq!(br.read_bits(4).unwrap(), 0b1111);
588    }
589
590    #[test]
591    fn test_peek_bits() {
592        let data = [0xAB];
593        let br = BitReader::new(&data);
594        assert_eq!(br.peek_bits(8).unwrap(), 0xAB);
595        assert_eq!(br.position(), 0);
596    }
597
598    #[test]
599    fn test_ubitvar() {
600        // Simple case: value fits in 6 bits, bits 4,5 = 00
601        // Value 5 = 0b000101
602        let data = [0b00000101];
603        let mut br = BitReader::new(&data);
604        assert_eq!(br.read_ubitvar().unwrap(), 5);
605    }
606
607    #[test]
608    fn test_bitangle() {
609        let data = [0x00, 0x00, 0x00, 0x00];
610        let mut br = BitReader::new(&data);
611        assert!((br.read_bitangle(16).unwrap() - 0.0).abs() < f32::EPSILON);
612    }
613}