Skip to main content

dvb_common/
bits.rs

1//! Big-endian, MSB-first bit reader/writer for dense sub-byte wire fields.
2//!
3//! DVB physical-layer signalling (e.g. the EN 302 755 §7.2 L1-pre / L1-post
4//! tables) packs many fields that are not byte-aligned — 1-bit flags, 3-bit
5//! codes, 18-bit sizes — back-to-back with no padding. [`BitReader`] and
6//! [`BitWriter`] walk such a stream a field at a time, most-significant bit
7//! first within each field and within each byte, which is the bit order used
8//! throughout the DVB/MPEG specifications.
9//!
10//! Both sides are symmetric: bits written by [`BitWriter::write_bits`] read
11//! back identically through [`BitReader::read_bits`]. The writer sets *and*
12//! clears each target bit, so the destination buffer need not be pre-zeroed.
13//!
14//! ```
15//! use dvb_common::bits::{BitReader, BitWriter};
16//!
17//! let mut buf = [0u8; 2];
18//! let mut w = BitWriter::new(&mut buf);
19//! w.write_bits(0b101, 3).unwrap();   // 3-bit field
20//! w.write_bits(0x1FF, 9).unwrap();   // 9-bit field — crosses the byte boundary
21//! assert_eq!(w.bits_written(), 12);
22//!
23//! let mut r = BitReader::new(&buf);
24//! assert_eq!(r.read_bits(3).unwrap(), 0b101);
25//! assert_eq!(r.read_bits(9).unwrap(), 0x1FF);
26//! ```
27
28use core::fmt;
29
30/// Error from a [`BitReader`] / [`BitWriter`] operation.
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[non_exhaustive]
33pub enum BitError {
34    /// Asked to read/write past the end of the backing buffer.
35    OutOfBounds {
36        /// Bits the operation needed.
37        needed_bits: usize,
38        /// Bits actually left in the buffer.
39        remaining_bits: usize,
40    },
41    /// Requested a field wider than the 64-bit carrier.
42    TooManyBits {
43        /// The over-wide width requested.
44        requested: u32,
45    },
46    /// A value passed to [`BitWriter::write_bits`] does not fit in `bits` bits.
47    ValueTooWide {
48        /// The offending value.
49        value: u64,
50        /// The field width it was asked to fit in.
51        bits: u32,
52    },
53}
54
55impl fmt::Display for BitError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            BitError::OutOfBounds {
59                needed_bits,
60                remaining_bits,
61            } => write!(
62                f,
63                "bit buffer out of bounds: need {needed_bits} bit(s), {remaining_bits} remaining"
64            ),
65            BitError::TooManyBits { requested } => {
66                write!(f, "requested {requested} bits exceeds the 64-bit carrier")
67            }
68            BitError::ValueTooWide { value, bits } => {
69                write!(f, "value {value:#x} does not fit in {bits} bit(s)")
70            }
71        }
72    }
73}
74
75impl std::error::Error for BitError {}
76
77/// Reads fields MSB-first from a borrowed byte slice.
78#[derive(Debug, Clone)]
79pub struct BitReader<'a> {
80    data: &'a [u8],
81    /// Absolute bit cursor from the start of `data` (0 = MSB of byte 0).
82    bit_pos: usize,
83}
84
85impl<'a> BitReader<'a> {
86    /// Create a reader positioned at the first bit of `data`.
87    #[must_use]
88    pub fn new(data: &'a [u8]) -> Self {
89        Self { data, bit_pos: 0 }
90    }
91
92    /// Total bits in the backing buffer.
93    #[must_use]
94    pub fn total_bits(&self) -> usize {
95        self.data.len() * 8
96    }
97
98    /// Bits already consumed.
99    #[must_use]
100    pub fn bits_read(&self) -> usize {
101        self.bit_pos
102    }
103
104    /// Bits not yet consumed.
105    #[must_use]
106    pub fn bits_remaining(&self) -> usize {
107        self.total_bits() - self.bit_pos
108    }
109
110    /// `true` if the cursor sits on a byte boundary.
111    #[must_use]
112    pub fn is_byte_aligned(&self) -> bool {
113        self.bit_pos % 8 == 0
114    }
115
116    /// Read `n` bits (`0..=64`) MSB-first into the low bits of a `u64`.
117    ///
118    /// `read_bits(0)` returns `0` and consumes nothing.
119    ///
120    /// # Errors
121    /// [`BitError::TooManyBits`] if `n > 64`; [`BitError::OutOfBounds`] if
122    /// fewer than `n` bits remain.
123    pub fn read_bits(&mut self, n: u32) -> Result<u64, BitError> {
124        if n > 64 {
125            return Err(BitError::TooManyBits { requested: n });
126        }
127        if n == 0 {
128            return Ok(0);
129        }
130        let need = n as usize;
131        let remaining = self.bits_remaining();
132        if need > remaining {
133            return Err(BitError::OutOfBounds {
134                needed_bits: need,
135                remaining_bits: remaining,
136            });
137        }
138        let mut value: u64 = 0;
139        for _ in 0..n {
140            let byte = self.data[self.bit_pos / 8];
141            let bit_index = 7 - (self.bit_pos % 8); // MSB first within the byte
142            let bit = (byte >> bit_index) & 1;
143            value = (value << 1) | u64::from(bit);
144            self.bit_pos += 1;
145        }
146        Ok(value)
147    }
148
149    /// Read a single bit as a `bool` (`1` → `true`).
150    ///
151    /// # Errors
152    /// [`BitError::OutOfBounds`] if no bits remain.
153    pub fn read_bool(&mut self) -> Result<bool, BitError> {
154        Ok(self.read_bits(1)? != 0)
155    }
156
157    /// Skip `n` bits without interpreting them.
158    ///
159    /// # Errors
160    /// [`BitError::OutOfBounds`] if fewer than `n` bits remain.
161    pub fn skip_bits(&mut self, n: usize) -> Result<(), BitError> {
162        let remaining = self.bits_remaining();
163        if n > remaining {
164            return Err(BitError::OutOfBounds {
165                needed_bits: n,
166                remaining_bits: remaining,
167            });
168        }
169        self.bit_pos += n;
170        Ok(())
171    }
172
173    /// Advance to the next byte boundary (no-op if already aligned).
174    pub fn align_to_byte(&mut self) {
175        let rem = self.bit_pos % 8;
176        if rem != 0 {
177            self.bit_pos += 8 - rem;
178        }
179    }
180}
181
182/// Writes fields MSB-first into a borrowed mutable byte slice.
183///
184/// Each bit is explicitly set or cleared, so `buf` need not be zeroed first.
185#[derive(Debug)]
186pub struct BitWriter<'a> {
187    data: &'a mut [u8],
188    bit_pos: usize,
189}
190
191impl<'a> BitWriter<'a> {
192    /// Create a writer positioned at the first bit of `data`.
193    #[must_use]
194    pub fn new(data: &'a mut [u8]) -> Self {
195        Self { data, bit_pos: 0 }
196    }
197
198    /// Total bits the backing buffer can hold.
199    #[must_use]
200    pub fn capacity_bits(&self) -> usize {
201        self.data.len() * 8
202    }
203
204    /// Bits written so far.
205    #[must_use]
206    pub fn bits_written(&self) -> usize {
207        self.bit_pos
208    }
209
210    /// `true` if the cursor sits on a byte boundary.
211    #[must_use]
212    pub fn is_byte_aligned(&self) -> bool {
213        self.bit_pos % 8 == 0
214    }
215
216    /// Write the low `n` bits (`0..=64`) of `value`, MSB-first.
217    ///
218    /// `write_bits(_, 0)` writes nothing.
219    ///
220    /// # Errors
221    /// [`BitError::TooManyBits`] if `n > 64`; [`BitError::ValueTooWide`] if
222    /// `value` has bits set above bit `n-1`; [`BitError::OutOfBounds`] if the
223    /// buffer cannot hold `n` more bits.
224    pub fn write_bits(&mut self, value: u64, n: u32) -> Result<(), BitError> {
225        if n > 64 {
226            return Err(BitError::TooManyBits { requested: n });
227        }
228        if n == 0 {
229            return Ok(());
230        }
231        // Reject values that don't fit — catches caller arithmetic bugs.
232        if n < 64 && value >= (1u64 << n) {
233            return Err(BitError::ValueTooWide { value, bits: n });
234        }
235        let need = n as usize;
236        let remaining = self.capacity_bits() - self.bit_pos;
237        if need > remaining {
238            return Err(BitError::OutOfBounds {
239                needed_bits: need,
240                remaining_bits: remaining,
241            });
242        }
243        for i in (0..n).rev() {
244            let bit = ((value >> i) & 1) as u8;
245            let byte_idx = self.bit_pos / 8;
246            let bit_index = 7 - (self.bit_pos % 8);
247            if bit == 1 {
248                self.data[byte_idx] |= 1 << bit_index;
249            } else {
250                self.data[byte_idx] &= !(1u8 << bit_index);
251            }
252            self.bit_pos += 1;
253        }
254        Ok(())
255    }
256
257    /// Write a single bit from a `bool`.
258    ///
259    /// # Errors
260    /// [`BitError::OutOfBounds`] if the buffer is full.
261    pub fn write_bool(&mut self, value: bool) -> Result<(), BitError> {
262        self.write_bits(u64::from(value), 1)
263    }
264
265    /// Pad with zero bits up to the next byte boundary (no-op if aligned).
266    ///
267    /// # Errors
268    /// [`BitError::OutOfBounds`] if the buffer cannot hold the padding.
269    pub fn align_to_byte(&mut self) -> Result<(), BitError> {
270        let rem = self.bit_pos % 8;
271        if rem != 0 {
272            self.write_bits(0, (8 - rem) as u32)?;
273        }
274        Ok(())
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    // Binary literals here are grouped by *wire field boundaries* (e.g.
281    // `0b1_01_10101` = a 1-bit, 2-bit and 5-bit field) to document the layout,
282    // not by nibbles.
283    #![allow(clippy::unusual_byte_groupings)]
284
285    use super::*;
286
287    #[test]
288    fn single_byte_fields_round_trip() {
289        let mut buf = [0u8; 1];
290        let mut w = BitWriter::new(&mut buf);
291        w.write_bits(0b1, 1).unwrap();
292        w.write_bits(0b01, 2).unwrap();
293        w.write_bits(0b10101, 5).unwrap();
294        assert_eq!(w.bits_written(), 8);
295        // 1 01 10101 = 0b1_01_10101 = 0xB5
296        assert_eq!(buf[0], 0b1_01_10101);
297
298        let mut r = BitReader::new(&buf);
299        assert_eq!(r.read_bits(1).unwrap(), 0b1);
300        assert_eq!(r.read_bits(2).unwrap(), 0b01);
301        assert_eq!(r.read_bits(5).unwrap(), 0b10101);
302        assert_eq!(r.bits_remaining(), 0);
303    }
304
305    #[test]
306    fn field_crossing_byte_boundary_round_trips() {
307        // An 18-bit field like L1_POST_SIZE, offset so it straddles 3 bytes.
308        let mut buf = [0u8; 4];
309        let mut w = BitWriter::new(&mut buf);
310        w.write_bits(0b101, 3).unwrap();
311        w.write_bits(0b10_1010_1010_1010_1011, 18).unwrap();
312        let val18 = 0b10_1010_1010_1010_1011u64;
313
314        let mut r = BitReader::new(&buf);
315        assert_eq!(r.read_bits(3).unwrap(), 0b101);
316        assert_eq!(r.read_bits(18).unwrap(), val18);
317    }
318
319    #[test]
320    fn read_zero_bits_is_noop() {
321        let buf = [0xFFu8];
322        let mut r = BitReader::new(&buf);
323        assert_eq!(r.read_bits(0).unwrap(), 0);
324        assert_eq!(r.bits_read(), 0);
325    }
326
327    #[test]
328    fn full_64_bit_field() {
329        let mut buf = [0u8; 8];
330        let value = 0xDEAD_BEEF_CAFE_F00Du64;
331        let mut w = BitWriter::new(&mut buf);
332        w.write_bits(value, 64).unwrap();
333        let mut r = BitReader::new(&buf);
334        assert_eq!(r.read_bits(64).unwrap(), value);
335    }
336
337    #[test]
338    fn read_past_end_errs() {
339        let buf = [0xFFu8]; // 8 bits
340        let mut r = BitReader::new(&buf);
341        r.read_bits(7).unwrap();
342        let err = r.read_bits(2).unwrap_err();
343        assert_eq!(
344            err,
345            BitError::OutOfBounds {
346                needed_bits: 2,
347                remaining_bits: 1,
348            }
349        );
350    }
351
352    #[test]
353    fn read_too_many_bits_errs() {
354        let buf = [0u8; 16];
355        let mut r = BitReader::new(&buf);
356        assert_eq!(
357            r.read_bits(65).unwrap_err(),
358            BitError::TooManyBits { requested: 65 }
359        );
360    }
361
362    #[test]
363    fn write_value_too_wide_errs() {
364        let mut buf = [0u8; 4];
365        let mut w = BitWriter::new(&mut buf);
366        // 0b100 needs 3 bits; asking for 2 must fail.
367        assert_eq!(
368            w.write_bits(0b100, 2).unwrap_err(),
369            BitError::ValueTooWide {
370                value: 0b100,
371                bits: 2
372            }
373        );
374    }
375
376    #[test]
377    fn write_past_end_errs() {
378        let mut buf = [0u8; 1];
379        let mut w = BitWriter::new(&mut buf);
380        w.write_bits(0, 7).unwrap();
381        assert_eq!(
382            w.write_bits(0b11, 2).unwrap_err(),
383            BitError::OutOfBounds {
384                needed_bits: 2,
385                remaining_bits: 1,
386            }
387        );
388    }
389
390    #[test]
391    fn writer_does_not_require_zeroed_buffer() {
392        // Pre-fill with 0xFF; writer must clear bits it writes as 0.
393        let mut buf = [0xFFu8; 1];
394        let mut w = BitWriter::new(&mut buf);
395        w.write_bits(0b0000_0000, 8).unwrap();
396        assert_eq!(buf[0], 0x00);
397    }
398
399    #[test]
400    fn bool_round_trips() {
401        let mut buf = [0u8; 1];
402        let mut w = BitWriter::new(&mut buf);
403        w.write_bool(true).unwrap();
404        w.write_bool(false).unwrap();
405        w.write_bool(true).unwrap();
406        let mut r = BitReader::new(&buf);
407        assert!(r.read_bool().unwrap());
408        assert!(!r.read_bool().unwrap());
409        assert!(r.read_bool().unwrap());
410    }
411
412    #[test]
413    fn skip_and_align() {
414        let buf = [0b1010_1100u8, 0b1111_0000];
415        let mut r = BitReader::new(&buf);
416        r.read_bits(2).unwrap(); // at bit 2
417        r.skip_bits(3).unwrap(); // at bit 5
418        assert!(!r.is_byte_aligned());
419        r.align_to_byte(); // at bit 8
420        assert!(r.is_byte_aligned());
421        assert_eq!(r.read_bits(4).unwrap(), 0b1111);
422    }
423
424    #[test]
425    fn writer_align_pads_with_zero() {
426        let mut buf = [0xFFu8; 1];
427        let mut w = BitWriter::new(&mut buf);
428        w.write_bits(0b101, 3).unwrap();
429        w.align_to_byte().unwrap();
430        assert_eq!(w.bits_written(), 8);
431        assert_eq!(buf[0], 0b1010_0000); // 3 data bits + 5 zero pad
432    }
433
434    #[test]
435    fn exhaustive_small_width_round_trip() {
436        // Every value of every width 1..=16 reads back identically.
437        for bits in 1u32..=16 {
438            let max = if bits == 64 {
439                u64::MAX
440            } else {
441                (1u64 << bits) - 1
442            };
443            for value in [0u64, 1, max, max / 2] {
444                let mut buf = [0u8; 8];
445                let mut w = BitWriter::new(&mut buf);
446                w.write_bits(value, bits).unwrap();
447                let mut r = BitReader::new(&buf);
448                assert_eq!(
449                    r.read_bits(bits).unwrap(),
450                    value,
451                    "round-trip failed: value={value:#x} bits={bits}"
452                );
453            }
454        }
455    }
456}