1use crate::error::FitError;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Endian {
13 Little,
14 Big,
15}
16
17#[derive(Debug, Clone)]
19pub struct ByteStream<'a> {
20 bytes: &'a [u8],
21 pos: usize,
22}
23
24impl<'a> ByteStream<'a> {
25 #[inline]
27 pub fn new(bytes: &'a [u8]) -> Self {
28 Self { bytes, pos: 0 }
29 }
30
31 #[inline]
33 pub fn as_slice(&self) -> &'a [u8] {
34 self.bytes
35 }
36
37 #[inline]
39 pub fn position(&self) -> usize {
40 self.pos
41 }
42
43 pub fn seek(&mut self, offset: usize) -> Result<(), FitError> {
45 if offset > self.bytes.len() {
46 return Err(FitError::UnexpectedEof { offset });
47 }
48 self.pos = offset;
49 Ok(())
50 }
51
52 #[inline]
54 pub fn remaining(&self) -> usize {
55 self.bytes.len().saturating_sub(self.pos)
56 }
57
58 #[inline]
60 pub fn is_empty(&self) -> bool {
61 self.pos >= self.bytes.len()
62 }
63
64 pub fn peek_u8(&self) -> Result<u8, FitError> {
66 self.bytes
67 .get(self.pos)
68 .copied()
69 .ok_or(FitError::UnexpectedEof { offset: self.pos })
70 }
71
72 pub fn read_u8(&mut self) -> Result<u8, FitError> {
74 let v = self.peek_u8()?;
75 self.pos += 1;
76 Ok(v)
77 }
78
79 pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], FitError> {
81 if self.remaining() < n {
82 return Err(FitError::UnexpectedEof { offset: self.pos });
83 }
84 let slice = &self.bytes[self.pos..self.pos + n];
85 self.pos += n;
86 Ok(slice)
87 }
88
89 pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N], FitError> {
91 let slice = self.read_bytes(N)?;
92 let mut arr = [0u8; N];
94 arr.copy_from_slice(slice);
95 Ok(arr)
96 }
97
98 pub fn read_u16(&mut self, endian: Endian) -> Result<u16, FitError> {
100 let bytes = self.read_array::<2>()?;
101 Ok(match endian {
102 Endian::Little => u16::from_le_bytes(bytes),
103 Endian::Big => u16::from_be_bytes(bytes),
104 })
105 }
106
107 pub fn read_u32(&mut self, endian: Endian) -> Result<u32, FitError> {
109 let bytes = self.read_array::<4>()?;
110 Ok(match endian {
111 Endian::Little => u32::from_le_bytes(bytes),
112 Endian::Big => u32::from_be_bytes(bytes),
113 })
114 }
115
116 #[inline]
118 pub fn read_u16_le(&mut self) -> Result<u16, FitError> {
119 self.read_u16(Endian::Little)
120 }
121
122 #[inline]
124 pub fn read_u32_le(&mut self) -> Result<u32, FitError> {
125 self.read_u32(Endian::Little)
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn position_and_remaining() {
135 let mut s = ByteStream::new(&[1, 2, 3, 4, 5]);
136 assert_eq!(s.position(), 0);
137 assert_eq!(s.remaining(), 5);
138 let _ = s.read_u8().unwrap();
139 assert_eq!(s.position(), 1);
140 assert_eq!(s.remaining(), 4);
141 }
142
143 #[test]
144 fn peek_does_not_advance() {
145 let mut s = ByteStream::new(&[42]);
146 assert_eq!(s.peek_u8().unwrap(), 42);
147 assert_eq!(s.position(), 0);
148 assert_eq!(s.read_u8().unwrap(), 42);
149 assert_eq!(s.position(), 1);
150 }
151
152 #[test]
153 fn read_past_end_errors_with_correct_offset() {
154 let mut s = ByteStream::new(&[1, 2]);
155 let _ = s.read_u8().unwrap();
156 let _ = s.read_u8().unwrap();
157 assert_eq!(s.read_u8(), Err(FitError::UnexpectedEof { offset: 2 }),);
158 }
159
160 #[test]
161 fn endianness_is_correct() {
162 let mut s = ByteStream::new(&[0x12, 0x34, 0x56, 0x78]);
163 assert_eq!(s.read_u16(Endian::Little).unwrap(), 0x3412);
164 s.seek(0).unwrap();
165 assert_eq!(s.read_u16(Endian::Big).unwrap(), 0x1234);
166 s.seek(0).unwrap();
167 assert_eq!(s.read_u32(Endian::Little).unwrap(), 0x7856_3412);
168 s.seek(0).unwrap();
169 assert_eq!(s.read_u32(Endian::Big).unwrap(), 0x1234_5678);
170 }
171
172 #[test]
173 fn read_bytes_borrows_correct_range() {
174 let data = [10, 20, 30, 40, 50];
175 let mut s = ByteStream::new(&data);
176 assert_eq!(s.read_bytes(2).unwrap(), &[10, 20]);
177 assert_eq!(s.read_bytes(3).unwrap(), &[30, 40, 50]);
178 assert!(s.is_empty());
179 }
180
181 #[test]
182 fn seek_within_and_at_end_ok() {
183 let mut s = ByteStream::new(&[1, 2, 3]);
184 s.seek(3).unwrap(); assert!(s.is_empty());
186 assert_eq!(s.seek(4), Err(FitError::UnexpectedEof { offset: 4 }));
187 }
188}