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}