Skip to main content

wire_codec/
bitfield.rs

1//! Bit-level read and write cursors.
2//!
3//! [`BitReader`] consumes bits from a borrowed byte slice in most-significant-bit
4//! first order, suitable for packed binary protocols where multiple fields share
5//! a single byte. [`BitWriter`] performs the reverse operation against a caller-
6//! supplied output buffer.
7//!
8//! Both cursors support arbitrary widths from 1 to 64 bits per call.
9
10use crate::error::{Error, Result};
11
12/// Maximum bit width per `read_bits` / `write_bits` call.
13pub const MAX_BIT_WIDTH: u32 = 64;
14
15/// Reads packed bits from a borrowed byte slice, most-significant-bit first.
16///
17/// # Example
18///
19/// ```
20/// use wire_codec::BitReader;
21///
22/// // 0b1010_1100 = 0xAC. Read 3 bits, then 5 bits.
23/// let mut r = BitReader::new(&[0xAC]);
24/// assert_eq!(r.read_bits(3).unwrap(), 0b101);
25/// assert_eq!(r.read_bits(5).unwrap(), 0b01100);
26/// ```
27#[derive(Debug, Clone)]
28pub struct BitReader<'a> {
29    bytes: &'a [u8],
30    bit_pos: usize,
31}
32
33impl<'a> BitReader<'a> {
34    /// Wrap `bytes` in a new bit cursor positioned at the first bit.
35    #[inline]
36    pub const fn new(bytes: &'a [u8]) -> Self {
37        Self { bytes, bit_pos: 0 }
38    }
39
40    /// Number of bits already consumed.
41    #[inline]
42    pub const fn bits_consumed(&self) -> usize {
43        self.bit_pos
44    }
45
46    /// Number of bits still available to read.
47    #[inline]
48    pub const fn bits_remaining(&self) -> usize {
49        bit_capacity(self.bytes.len()) - self.bit_pos
50    }
51
52    /// Advance the cursor to the next byte boundary.
53    ///
54    /// Has no effect when the cursor is already byte-aligned. The skipped
55    /// bits are discarded without being inspected.
56    #[inline]
57    pub fn align_to_byte(&mut self) {
58        // `bit_pos <= bit_capacity(..)`, which is a multiple of 8 no larger
59        // than `usize::MAX - 7`, so the addition cannot overflow and the
60        // result never passes the end of the input.
61        self.bit_pos = (self.bit_pos + 7) & !7;
62    }
63
64    /// Read the next `n` bits as the low-order bits of a `u64`.
65    ///
66    /// # Errors
67    ///
68    /// - [`Error::BitOverflow`] if `n` is `0` or greater than [`MAX_BIT_WIDTH`].
69    /// - [`Error::UnexpectedEof`] if fewer than `n` bits remain in the input.
70    pub fn read_bits(&mut self, n: u32) -> Result<u64> {
71        if n == 0 || n > MAX_BIT_WIDTH {
72            return Err(Error::BitOverflow);
73        }
74        if (n as usize) > self.bits_remaining() {
75            return Err(Error::UnexpectedEof);
76        }
77        let mut value: u64 = 0;
78        let mut bits_left = n;
79        while bits_left > 0 {
80            let byte_idx = self.bit_pos / 8;
81            let bit_off = (self.bit_pos % 8) as u32;
82            let avail = 8 - bit_off;
83            let take = if bits_left < avail { bits_left } else { avail };
84            let shift = avail - take;
85            let mask: u32 = (1u32 << take) - 1;
86            let chunk = (u32::from(self.bytes[byte_idx]) >> shift) & mask;
87            value = (value << take) | u64::from(chunk);
88            self.bit_pos += take as usize;
89            bits_left -= take;
90        }
91        Ok(value)
92    }
93
94    /// Read one bit as a `bool`.
95    ///
96    /// # Errors
97    ///
98    /// Returns [`Error::UnexpectedEof`] if no bits remain.
99    ///
100    /// # Example
101    ///
102    /// ```
103    /// use wire_codec::BitReader;
104    ///
105    /// let mut r = BitReader::new(&[0b1000_0000]);
106    /// assert!(r.read_bool().unwrap());
107    /// assert!(!r.read_bool().unwrap());
108    /// ```
109    #[inline]
110    pub fn read_bool(&mut self) -> Result<bool> {
111        self.read_bits(1).map(|bit| bit != 0)
112    }
113
114    /// Read the next `n` bits as a two's-complement signed value, sign-extended
115    /// to `i64`.
116    ///
117    /// # Errors
118    ///
119    /// Same contract as [`BitReader::read_bits`].
120    ///
121    /// # Example
122    ///
123    /// ```
124    /// use wire_codec::BitReader;
125    ///
126    /// // 0b101 in a 3-bit signed field is -3.
127    /// let mut r = BitReader::new(&[0b1010_0000]);
128    /// assert_eq!(r.read_signed_bits(3).unwrap(), -3);
129    /// ```
130    #[inline]
131    pub fn read_signed_bits(&mut self, n: u32) -> Result<i64> {
132        self.read_bits(n).map(|raw| sign_extend(raw, n))
133    }
134}
135
136/// Interpret the low `n` bits of `raw` (1 <= n <= 64) as two's complement.
137#[inline]
138pub(crate) const fn sign_extend(raw: u64, n: u32) -> i64 {
139    let shift = 64 - n;
140    ((raw << shift) as i64) >> shift
141}
142
143/// Encode `value` as an `n`-bit two's-complement field, or `None` if it does
144/// not fit.
145#[inline]
146pub(crate) fn signed_to_bits(value: i64, n: u32) -> Option<u64> {
147    if n == 0 || n > MAX_BIT_WIDTH {
148        return None;
149    }
150    let shift = 64 - n;
151    // The value fits iff truncating to `n` bits and sign-extending back
152    // reproduces it.
153    let raw = (value as u64) & (u64::MAX >> shift);
154    (sign_extend(raw, n) == value).then_some(raw)
155}
156
157/// Writes packed bits to a caller-supplied byte slice, most-significant-bit first.
158///
159/// Every bit inside the prefix reported by [`BitWriter::finish`] is written
160/// explicitly: field bits by [`BitWriter::write_bits`], padding bits as zero
161/// by [`BitWriter::align_to_byte`] and [`BitWriter::finish`]. A reused buffer
162/// holding stale data therefore produces the same output as a zeroed one.
163/// Bytes past that prefix are left untouched.
164///
165/// # Example
166///
167/// ```
168/// use wire_codec::BitWriter;
169///
170/// let mut storage = [0u8; 1];
171/// let mut w = BitWriter::new(&mut storage);
172/// w.write_bits(0b101, 3).unwrap();
173/// w.write_bits(0b01100, 5).unwrap();
174/// assert_eq!(w.finish(), 1);
175/// assert_eq!(storage[0], 0xAC);
176/// ```
177#[derive(Debug)]
178pub struct BitWriter<'a> {
179    bytes: &'a mut [u8],
180    bit_pos: usize,
181}
182
183impl<'a> BitWriter<'a> {
184    /// Wrap `bytes` in a new bit cursor. Existing contents of `bytes` are
185    /// overwritten as bits are written.
186    #[inline]
187    pub fn new(bytes: &'a mut [u8]) -> Self {
188        Self { bytes, bit_pos: 0 }
189    }
190
191    /// Number of bits written so far.
192    #[inline]
193    pub fn bits_written(&self) -> usize {
194        self.bit_pos
195    }
196
197    /// Pad the cursor with zero bits up to the next byte boundary.
198    ///
199    /// Has no effect when the cursor is already byte-aligned.
200    ///
201    /// # Errors
202    ///
203    /// Never fails in practice: a partially written byte always lies inside
204    /// the backing slice, so rounding up to its end cannot run out of room.
205    /// The `Result` return type is kept for API stability.
206    pub fn align_to_byte(&mut self) -> Result<()> {
207        self.zero_trailing_bits();
208        // `bit_pos <= bit_capacity(..)`, a multiple of 8 no larger than
209        // `usize::MAX - 7`: the addition cannot overflow and the rounded
210        // position cannot pass the end of the slice.
211        self.bit_pos = (self.bit_pos + 7) & !7;
212        Ok(())
213    }
214
215    /// Clear the unwritten low-order bits of a partially written byte.
216    #[inline]
217    fn zero_trailing_bits(&mut self) {
218        let bit_off = self.bit_pos % 8;
219        if bit_off != 0 {
220            let keep = 0xFFu8 << (8 - bit_off);
221            self.bytes[self.bit_pos / 8] &= keep;
222        }
223    }
224
225    /// Write the low `n` bits of `value` into the buffer.
226    ///
227    /// # Errors
228    ///
229    /// - [`Error::BitOverflow`] if `n` is `0`, greater than [`MAX_BIT_WIDTH`],
230    ///   or `value` has bits set above position `n`.
231    /// - [`Error::BufferFull`] if fewer than `n` bits remain in the buffer.
232    pub fn write_bits(&mut self, value: u64, n: u32) -> Result<()> {
233        if n == 0 || n > MAX_BIT_WIDTH {
234            return Err(Error::BitOverflow);
235        }
236        if n < 64 && (value >> n) != 0 {
237            return Err(Error::BitOverflow);
238        }
239        if (n as usize) > bit_capacity(self.bytes.len()) - self.bit_pos {
240            return Err(Error::BufferFull);
241        }
242        let mut bits_left = n;
243        while bits_left > 0 {
244            let byte_idx = self.bit_pos / 8;
245            let bit_off = (self.bit_pos % 8) as u32;
246            let avail = 8 - bit_off;
247            let take = if bits_left < avail { bits_left } else { avail };
248            let shift = avail - take;
249            let chunk = ((value >> (bits_left - take)) & ((1u64 << take) - 1)) as u8;
250            // Clear the destination bits, then OR in the new ones. This lets
251            // callers pass a freshly-allocated zeroed buffer or a reused one
252            // and get well-defined output either way.
253            let mask = (((1u32 << take) - 1) as u8) << shift;
254            self.bytes[byte_idx] = (self.bytes[byte_idx] & !mask) | (chunk << shift);
255            self.bit_pos += take as usize;
256            bits_left -= take;
257        }
258        Ok(())
259    }
260
261    /// Write one bit: `1` for `true`, `0` for `false`.
262    ///
263    /// # Errors
264    ///
265    /// Returns [`Error::BufferFull`] if no bits remain in the buffer.
266    ///
267    /// # Example
268    ///
269    /// ```
270    /// use wire_codec::BitWriter;
271    ///
272    /// let mut storage = [0u8; 1];
273    /// let mut w = BitWriter::new(&mut storage);
274    /// w.write_bool(true).unwrap();
275    /// w.write_bool(false).unwrap();
276    /// w.write_bool(true).unwrap();
277    /// assert_eq!(w.finish(), 1);
278    /// assert_eq!(storage[0], 0b1010_0000);
279    /// ```
280    #[inline]
281    pub fn write_bool(&mut self, value: bool) -> Result<()> {
282        self.write_bits(u64::from(value), 1)
283    }
284
285    /// Write `value` as an `n`-bit two's-complement field.
286    ///
287    /// # Errors
288    ///
289    /// - [`Error::BitOverflow`] if `n` is `0`, greater than [`MAX_BIT_WIDTH`],
290    ///   or `value` is outside `-(2^(n-1))..2^(n-1)`.
291    /// - [`Error::BufferFull`] if fewer than `n` bits remain in the buffer.
292    ///
293    /// # Example
294    ///
295    /// ```
296    /// use wire_codec::{BitReader, BitWriter, Error};
297    ///
298    /// let mut storage = [0u8; 1];
299    /// let mut w = BitWriter::new(&mut storage);
300    /// w.write_signed_bits(-3, 3).unwrap();
301    /// assert_eq!(w.write_signed_bits(4, 3), Err(Error::BitOverflow));
302    /// assert_eq!(w.finish(), 1);
303    /// assert_eq!(BitReader::new(&storage).read_signed_bits(3).unwrap(), -3);
304    /// ```
305    #[inline]
306    pub fn write_signed_bits(&mut self, value: i64, n: u32) -> Result<()> {
307        let raw = signed_to_bits(value, n).ok_or(Error::BitOverflow)?;
308        self.write_bits(raw, n)
309    }
310
311    /// Finalize writes and return the number of whole bytes consumed.
312    ///
313    /// A partially-filled trailing byte counts as one full byte, and its
314    /// remaining low-order bits are cleared to zero.
315    #[inline]
316    pub fn finish(mut self) -> usize {
317        self.zero_trailing_bits();
318        self.bit_pos.div_ceil(8)
319    }
320}
321
322/// Number of addressable bits in a slice of `len` bytes.
323///
324/// Saturates so that `len * 8` cannot overflow on 32-bit targets: slices
325/// larger than `usize::MAX / 8` bytes expose only their first
326/// `usize::MAX / 8` bytes to the bit cursors. The result is always a
327/// multiple of 8 and at most `usize::MAX - 7`.
328#[inline]
329const fn bit_capacity(len: usize) -> usize {
330    let max_bytes = usize::MAX / 8;
331    let bytes = if len < max_bytes { len } else { max_bytes };
332    bytes * 8
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338
339    #[test]
340    fn pack_unpack_round_trip() {
341        let mut storage = [0u8; 4];
342        let mut w = BitWriter::new(&mut storage);
343        w.write_bits(0b1011, 4).unwrap();
344        w.write_bits(0b1100_1010, 8).unwrap();
345        w.write_bits(0b1111_0000, 8).unwrap();
346        let n = w.finish();
347        assert_eq!(n, 3);
348
349        let mut r = BitReader::new(&storage[..n]);
350        assert_eq!(r.read_bits(4).unwrap(), 0b1011);
351        assert_eq!(r.read_bits(8).unwrap(), 0b1100_1010);
352        assert_eq!(r.read_bits(8).unwrap(), 0b1111_0000);
353    }
354
355    #[test]
356    fn write_rejects_value_overflow() {
357        let mut storage = [0u8; 1];
358        let mut w = BitWriter::new(&mut storage);
359        // 0b1_0000 needs 5 bits but only 4 were requested.
360        assert_eq!(w.write_bits(0b1_0000, 4), Err(Error::BitOverflow));
361    }
362
363    #[test]
364    fn padding_bits_are_zeroed_on_reused_buffer() {
365        // Regression test: align and finish must clear stale bits.
366        let mut storage = [0xFFu8; 2];
367        let mut w = BitWriter::new(&mut storage);
368        w.write_bits(0b1, 1).unwrap();
369        w.align_to_byte().unwrap();
370        w.write_bits(0b0, 1).unwrap();
371        assert_eq!(w.finish(), 2);
372        assert_eq!(storage, [0b1000_0000, 0b0000_0000]);
373    }
374
375    #[test]
376    fn finish_leaves_bytes_past_the_prefix_untouched() {
377        let mut storage = [0xAAu8; 3];
378        let mut w = BitWriter::new(&mut storage);
379        w.write_bits(0b11, 2).unwrap();
380        assert_eq!(w.finish(), 1);
381        assert_eq!(storage, [0b1100_0000, 0xAA, 0xAA]);
382    }
383
384    #[test]
385    fn align_when_already_aligned_is_a_no_op() {
386        let mut storage = [0xFFu8; 1];
387        let mut w = BitWriter::new(&mut storage);
388        w.align_to_byte().unwrap();
389        assert_eq!(w.bits_written(), 0);
390        w.write_bits(0xA5, 8).unwrap();
391        w.align_to_byte().unwrap();
392        assert_eq!(w.bits_written(), 8);
393        assert_eq!(w.finish(), 1);
394        assert_eq!(storage, [0xA5]);
395    }
396
397    #[test]
398    fn reader_align_to_byte() {
399        let mut r = BitReader::new(&[0b1010_0000, 0xCD]);
400        r.align_to_byte();
401        assert_eq!(r.bits_consumed(), 0, "aligned cursor does not move");
402        assert_eq!(r.read_bits(3).unwrap(), 0b101);
403        r.align_to_byte();
404        assert_eq!(r.bits_consumed(), 8);
405        assert_eq!(r.bits_remaining(), 8);
406        assert_eq!(r.read_bits(8).unwrap(), 0xCD);
407        r.align_to_byte();
408        assert_eq!(r.bits_consumed(), 16, "align at end of input stays put");
409        assert_eq!(r.read_bits(1), Err(Error::UnexpectedEof));
410    }
411
412    #[test]
413    fn width_validation() {
414        let mut r = BitReader::new(&[0xFF; 16]);
415        assert_eq!(r.read_bits(0), Err(Error::BitOverflow));
416        assert_eq!(r.read_bits(65), Err(Error::BitOverflow));
417        assert_eq!(r.read_signed_bits(0), Err(Error::BitOverflow));
418        assert_eq!(r.bits_consumed(), 0);
419
420        let mut storage = [0u8; 16];
421        let mut w = BitWriter::new(&mut storage);
422        assert_eq!(w.write_bits(0, 0), Err(Error::BitOverflow));
423        assert_eq!(w.write_bits(0, 65), Err(Error::BitOverflow));
424        assert_eq!(w.write_signed_bits(0, 0), Err(Error::BitOverflow));
425        assert_eq!(w.write_signed_bits(0, 65), Err(Error::BitOverflow));
426        assert_eq!(w.bits_written(), 0);
427    }
428
429    #[test]
430    fn full_width_fields_at_unaligned_offsets() {
431        for offset in 0..8u32 {
432            let mut storage = [0u8; 10];
433            let mut w = BitWriter::new(&mut storage);
434            if offset > 0 {
435                w.write_bits(0, offset).unwrap();
436            }
437            w.write_bits(0xDEAD_BEEF_0123_4567, 64).unwrap();
438            assert_eq!(w.write_bits(u64::MAX, 64), Err(Error::BufferFull));
439            let n = w.finish();
440            let mut r = BitReader::new(&storage[..n]);
441            if offset > 0 {
442                assert_eq!(r.read_bits(offset).unwrap(), 0);
443            }
444            assert_eq!(r.read_bits(64).unwrap(), 0xDEAD_BEEF_0123_4567);
445        }
446    }
447
448    #[test]
449    fn reads_and_writes_spanning_several_bytes() {
450        let mut storage = [0u8; 4];
451        let mut w = BitWriter::new(&mut storage);
452        w.write_bits(0b1, 1).unwrap();
453        w.write_bits(0x1_2345, 17).unwrap();
454        w.write_bits(0x3FFF, 14).unwrap();
455        assert_eq!(w.bits_written(), 32);
456        assert_eq!(w.write_bool(true), Err(Error::BufferFull));
457        assert_eq!(w.finish(), 4);
458        let mut r = BitReader::new(&storage);
459        assert_eq!(r.read_bits(1).unwrap(), 1);
460        assert_eq!(r.read_bits(17).unwrap(), 0x1_2345);
461        assert_eq!(r.read_bits(14).unwrap(), 0x3FFF);
462        assert_eq!(r.read_bool(), Err(Error::UnexpectedEof));
463    }
464
465    #[test]
466    fn signed_fields() {
467        for n in [1u32, 2, 7, 8, 13, 63, 64] {
468            let min = if n == 64 {
469                i64::MIN
470            } else {
471                -(1i64 << (n - 1))
472            };
473            let max = if n == 64 {
474                i64::MAX
475            } else {
476                (1i64 << (n - 1)) - 1
477            };
478            for v in [min, max, 0, -1] {
479                let mut storage = [0u8; 8];
480                let mut w = BitWriter::new(&mut storage);
481                w.write_signed_bits(v, n).unwrap();
482                let bytes = w.finish();
483                let mut r = BitReader::new(&storage[..bytes]);
484                assert_eq!(r.read_signed_bits(n).unwrap(), v, "n={n} v={v}");
485            }
486            if n < 64 {
487                let mut storage = [0u8; 8];
488                let mut w = BitWriter::new(&mut storage);
489                assert_eq!(w.write_signed_bits(max + 1, n), Err(Error::BitOverflow));
490                assert_eq!(w.write_signed_bits(min - 1, n), Err(Error::BitOverflow));
491            }
492        }
493        assert_eq!(sign_extend(0b1, 1), -1);
494        assert_eq!(signed_to_bits(-1, 1), Some(1));
495        assert_eq!(signed_to_bits(1, 1), None);
496    }
497
498    #[test]
499    fn bit_capacity_saturates() {
500        assert_eq!(bit_capacity(0), 0);
501        assert_eq!(bit_capacity(3), 24);
502        assert_eq!(bit_capacity(usize::MAX), (usize::MAX / 8) * 8);
503        assert_eq!(bit_capacity(usize::MAX) % 8, 0);
504    }
505
506    #[test]
507    fn align_rounds_up_to_byte() {
508        let mut storage = [0u8; 2];
509        let mut w = BitWriter::new(&mut storage);
510        w.write_bits(0b101, 3).unwrap();
511        w.align_to_byte().unwrap();
512        w.write_bits(0xFF, 8).unwrap();
513        assert_eq!(w.finish(), 2);
514        assert_eq!(storage, [0b1010_0000, 0xFF]);
515    }
516}