Skip to main content

fit/transforms/
bit_stream.rs

1//! LSB-first bit reader for the Components transform.
2//!
3//! When a FIT field's `components` list is non-empty, the field's wire bytes
4//! are interpreted as a packed integer that is unpacked **least-significant
5//! bit first** into multiple synthesised sub-values. For example,
6//! `gear_change_data` is a 32-bit field unpacked into four 8-bit lanes:
7//! `rear_gear`, `rear_gear_num`, `front_gear`, `front_gear_num`.
8//!
9//! Reference: `guide/fit_binary_learning_notes.md` §"BitStream(Components 位域展开)".
10
11/// LSB-first bit reader over a borrowed byte slice.
12pub struct BitStream<'a> {
13    bytes: &'a [u8],
14    /// Index of the next byte to refill from.
15    byte_offset: usize,
16    /// Up to 64 buffered bits; the *low* `bits_in_buffer` bits are valid.
17    buffer: u64,
18    bits_in_buffer: u32,
19}
20
21impl<'a> BitStream<'a> {
22    pub fn new(bytes: &'a [u8]) -> Self {
23        Self {
24            bytes,
25            byte_offset: 0,
26            buffer: 0,
27            bits_in_buffer: 0,
28        }
29    }
30
31    /// Read up to 64 bits LSB-first. If the stream is exhausted, the
32    /// remaining buffered bits are returned right-aligned and any further
33    /// reads yield 0.
34    ///
35    /// Each refill takes the next byte from the slice and shifts it into
36    /// the *high* end of the unread portion of the buffer (so the byte's
37    /// LSB lands at position `bits_in_buffer` and the LSB of the *whole*
38    /// stream is the bit that comes out first).
39    pub fn read_bits(&mut self, n: u32) -> u64 {
40        debug_assert!(n <= 64, "cannot read more than 64 bits at once");
41        if n == 0 {
42            return 0;
43        }
44
45        // Fill the buffer until we have enough bits, or the source is dry.
46        while self.bits_in_buffer < n && self.byte_offset < self.bytes.len() {
47            self.buffer |= (self.bytes[self.byte_offset] as u64) << self.bits_in_buffer;
48            self.bits_in_buffer += 8;
49            self.byte_offset += 1;
50        }
51
52        let take = n.min(self.bits_in_buffer);
53        let mask = if take == 64 {
54            u64::MAX
55        } else {
56            (1u64 << take) - 1
57        };
58        let value = self.buffer & mask;
59
60        // Discard the bits we just consumed.
61        if take == 64 {
62            self.buffer = 0;
63        } else {
64            self.buffer >>= take;
65        }
66        self.bits_in_buffer -= take;
67        value
68    }
69
70    /// Number of bits still available without consuming them.
71    pub fn bits_remaining(&self) -> u64 {
72        self.bits_in_buffer as u64 + (self.bytes.len() - self.byte_offset) as u64 * 8
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn reads_full_byte_lsb_first() {
82        // Single byte 0b10100101: LSB-first reads 1 then 0 then 1 ...
83        let mut bs = BitStream::new(&[0b1010_0101]);
84        let mut bits = Vec::new();
85        for _ in 0..8 {
86            bits.push(bs.read_bits(1) as u8);
87        }
88        assert_eq!(bits, vec![1, 0, 1, 0, 0, 1, 0, 1]);
89    }
90
91    #[test]
92    fn unpacks_gear_change_data() {
93        // 32-bit value packed as: [rearGear:8 | rearGearNum:8 | frontGear:8 | frontGearNum:8]
94        // Bytes (LE): rear=4, rear_num=3, front=2, front_num=1 → [0x04, 0x03, 0x02, 0x01]
95        let mut bs = BitStream::new(&[0x04, 0x03, 0x02, 0x01]);
96        assert_eq!(bs.read_bits(8), 0x04); // rear_gear
97        assert_eq!(bs.read_bits(8), 0x03); // rear_gear_num
98        assert_eq!(bs.read_bits(8), 0x02); // front_gear
99        assert_eq!(bs.read_bits(8), 0x01); // front_gear_num
100        assert_eq!(bs.bits_remaining(), 0);
101    }
102
103    #[test]
104    fn handles_non_byte_aligned_widths() {
105        // Two 12-bit values packed in 3 bytes (LSB-first):
106        //   value0 = 0xABC (12 bits)
107        //   value1 = 0x123 (12 bits)
108        // LSB-first packing: low 8 bits of v0 → byte 0 = 0xBC,
109        //                    high 4 bits of v0 + low 4 bits of v1 → byte 1 = 0x3A
110        //                    high 8 bits of v1 → byte 2 = 0x12
111        let mut bs = BitStream::new(&[0xBC, 0x3A, 0x12]);
112        assert_eq!(bs.read_bits(12), 0xABC);
113        assert_eq!(bs.read_bits(12), 0x123);
114    }
115
116    #[test]
117    fn read_zero_bits_is_noop() {
118        let mut bs = BitStream::new(&[0xFF]);
119        assert_eq!(bs.read_bits(0), 0);
120        assert_eq!(bs.read_bits(8), 0xFF);
121    }
122
123    #[test]
124    fn exhausted_stream_returns_remaining_then_zeros() {
125        let mut bs = BitStream::new(&[0x05]); // 8 bits available
126        assert_eq!(bs.read_bits(4), 0x5); // low nibble
127        assert_eq!(bs.read_bits(8), 0); // only 4 bits left → returns those 4 (= 0)
128        assert_eq!(bs.read_bits(8), 0); // truly empty now
129    }
130}