1use std::borrow::Cow;
14
15use crate::definition::{LocalDefinitions, MessageDefinition};
16use crate::error::FitError;
17use crate::header::FileHeader;
18use crate::raw_value::{decode_value, RawValue};
19use crate::record_header::RecordHeader;
20use crate::stream::ByteStream;
21
22#[derive(Debug, Clone, PartialEq)]
27pub struct RawMessage<'a> {
28 pub global_mesg_num: u16,
31 pub fields: Vec<RawField>,
33 pub dev_fields: Vec<RawDevField<'a>>,
37 pub starts_new_chain: bool,
42}
43
44impl<'a> RawMessage<'a> {
45 pub fn field(&self, field_def_num: u8) -> Option<&RawField> {
47 self.fields
48 .iter()
49 .find(|f| f.field_def_num == field_def_num)
50 }
51
52 pub fn into_owned(self) -> RawMessage<'static> {
55 RawMessage {
56 global_mesg_num: self.global_mesg_num,
57 fields: self.fields,
58 dev_fields: self
59 .dev_fields
60 .into_iter()
61 .map(RawDevField::into_owned)
62 .collect(),
63 starts_new_chain: self.starts_new_chain,
64 }
65 }
66}
67
68#[derive(Debug, Clone, PartialEq)]
70pub struct RawField {
71 pub field_def_num: u8,
73 pub value: RawValue,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct RawDevField<'a> {
83 pub field_def_num: u8,
85 pub developer_data_index: u8,
87 pub bytes: Cow<'a, [u8]>,
89}
90
91impl<'a> RawDevField<'a> {
92 pub fn into_owned(self) -> RawDevField<'static> {
94 RawDevField {
95 field_def_num: self.field_def_num,
96 developer_data_index: self.developer_data_index,
97 bytes: Cow::Owned(self.bytes.into_owned()),
98 }
99 }
100}
101
102pub struct Decoder<'a> {
110 stream: ByteStream<'a>,
111 local_defs: LocalDefinitions,
112 chain_crc_offset: Option<usize>,
115 needs_header: bool,
117 terminated: bool,
120 last_timestamp: Option<u32>,
123 chain_just_reset: bool,
127}
128
129impl<'a> Decoder<'a> {
130 pub fn new(bytes: &'a [u8]) -> Self {
133 Self {
134 stream: ByteStream::new(bytes),
135 local_defs: LocalDefinitions::new(),
136 chain_crc_offset: None,
137 needs_header: true,
138 terminated: false,
139 last_timestamp: None,
140 chain_just_reset: false,
141 }
142 }
143
144 pub fn read_all(self) -> (Vec<RawMessage<'a>>, Vec<FitError>) {
148 let mut messages = Vec::new();
149 let mut errors = Vec::new();
150 for item in self {
151 match item {
152 Ok(m) => messages.push(m),
153 Err(e) => errors.push(e),
154 }
155 }
156 (messages, errors)
157 }
158
159 fn parse_header_at_cursor(&mut self) -> Result<(), FitError> {
162 let pos = self.stream.position();
163 let bytes = self.stream.as_slice();
164 let header = FileHeader::parse(&bytes[pos..])?;
165 let header_size = header.header_size as usize;
166 let data_size = header.data_size as usize;
167 let total = header_size + data_size + 2;
168 if bytes.len() - pos < total {
169 return Err(FitError::TooShort {
170 expected: total,
171 actual: bytes.len() - pos,
172 });
173 }
174 self.stream.seek(pos + header_size)?;
175 self.chain_crc_offset = Some(pos + header_size + data_size);
176 Ok(())
177 }
178
179 fn decode_compressed_timestamp(&mut self, timestamp_offset: u8) -> u32 {
185 let last_ts = self.last_timestamp.unwrap_or(0);
186 let last_5bits = last_ts & 0x1F;
187 let offset = u32::from(timestamp_offset);
188 let new_ts = if offset >= last_5bits {
189 (last_ts & !0x1F) | offset
190 } else {
191 (last_ts & !0x1F).wrapping_add(0x20) | offset
192 };
193 self.last_timestamp = Some(new_ts);
194 new_ts
195 }
196
197 fn track_timestamp(&mut self, msg: &RawMessage<'_>) {
199 if let Some(f) = msg.field(253) {
200 if let Some(ts) = f.value.as_u32() {
201 self.last_timestamp = Some(ts);
202 }
203 }
204 }
205
206 fn decode_data(&mut self, local_mesg_num: u8) -> Result<RawMessage<'a>, FitError> {
209 let def = self.local_defs.require(local_mesg_num)?.clone();
213
214 let mut out_fields = Vec::with_capacity(def.fields.len());
215 for f in &def.fields {
216 let raw = self.stream.read_bytes(f.size as usize)?;
217 let value = decode_value(f.base_type, raw, def.endian, f.field_def_num)?;
218 out_fields.push(RawField {
219 field_def_num: f.field_def_num,
220 value,
221 });
222 }
223
224 let mut out_dev = Vec::with_capacity(def.dev_fields.len());
225 for d in &def.dev_fields {
226 let raw = self.stream.read_bytes(d.size as usize)?;
227 out_dev.push(RawDevField {
228 field_def_num: d.field_def_num,
229 developer_data_index: d.developer_data_index,
230 bytes: Cow::Borrowed(raw),
231 });
232 }
233
234 let msg = RawMessage {
235 global_mesg_num: def.global_mesg_num,
236 fields: out_fields,
237 dev_fields: out_dev,
238 starts_new_chain: false,
239 };
240 self.track_timestamp(&msg);
241 Ok(msg)
242 }
243
244 fn decode_data_compressed(
248 &mut self,
249 local_mesg_num: u8,
250 timestamp_offset: u8,
251 ) -> Result<RawMessage<'a>, FitError> {
252 let def = self.local_defs.require(local_mesg_num)?.clone();
253 let timestamp = self.decode_compressed_timestamp(timestamp_offset);
254
255 let mut out_fields = Vec::with_capacity(def.fields.len());
256 for f in &def.fields {
257 if f.field_def_num == 253 {
258 out_fields.push(RawField {
259 field_def_num: 253,
260 value: RawValue::U32Scalar(timestamp),
261 });
262 } else {
263 let raw = self.stream.read_bytes(f.size as usize)?;
264 let value = decode_value(f.base_type, raw, def.endian, f.field_def_num)?;
265 out_fields.push(RawField {
266 field_def_num: f.field_def_num,
267 value,
268 });
269 }
270 }
271
272 let mut out_dev = Vec::with_capacity(def.dev_fields.len());
273 for d in &def.dev_fields {
274 let raw = self.stream.read_bytes(d.size as usize)?;
275 out_dev.push(RawDevField {
276 field_def_num: d.field_def_num,
277 developer_data_index: d.developer_data_index,
278 bytes: Cow::Borrowed(raw),
279 });
280 }
281
282 Ok(RawMessage {
283 global_mesg_num: def.global_mesg_num,
284 fields: out_fields,
285 dev_fields: out_dev,
286 starts_new_chain: false,
287 })
288 }
289}
290
291impl<'a> Iterator for Decoder<'a> {
292 type Item = Result<RawMessage<'a>, FitError>;
293
294 fn next(&mut self) -> Option<Self::Item> {
295 if self.terminated {
296 return None;
297 }
298
299 if self.needs_header {
301 self.needs_header = false;
302 if let Err(e) = self.parse_header_at_cursor() {
303 self.terminated = true;
304 return Some(Err(e));
305 }
306 }
307
308 loop {
309 if let Some(crc_off) = self.chain_crc_offset {
311 if self.stream.position() >= crc_off {
312 if self.stream.read_bytes(2).is_err() {
315 self.terminated = true;
316 return None;
317 }
318 if self.stream.is_empty() {
320 self.terminated = true;
321 return None;
322 }
323 self.local_defs.clear();
324 self.last_timestamp = None;
325 self.chain_crc_offset = None;
326 self.chain_just_reset = true;
327 if let Err(e) = self.parse_header_at_cursor() {
328 self.terminated = true;
329 return Some(Err(e));
330 }
331 }
332 }
333
334 let hb = match self.stream.read_u8() {
336 Ok(b) => b,
337 Err(e) => {
338 self.terminated = true;
339 return Some(Err(e));
340 }
341 };
342
343 match RecordHeader::classify(hb) {
344 RecordHeader::Definition {
345 local_mesg_num,
346 has_dev_data,
347 } => match MessageDefinition::parse(&mut self.stream, has_dev_data) {
348 Ok(def) => {
349 self.local_defs.set(local_mesg_num, def);
350 continue;
351 }
352 Err(e) => {
353 self.terminated = true;
354 return Some(Err(e));
355 }
356 },
357 RecordHeader::Data { local_mesg_num } => {
358 let mut result = self.decode_data(local_mesg_num);
359 match &mut result {
360 Ok(msg) => {
361 msg.starts_new_chain = std::mem::take(&mut self.chain_just_reset)
362 }
363 Err(_) => self.terminated = true,
364 }
365 return Some(result);
366 }
367 RecordHeader::CompressedTimestamp {
368 local_mesg_num,
369 timestamp_offset,
370 } => {
371 let mut result = self.decode_data_compressed(local_mesg_num, timestamp_offset);
372 match &mut result {
373 Ok(msg) => {
374 msg.starts_new_chain = std::mem::take(&mut self.chain_just_reset)
375 }
376 Err(_) => self.terminated = true,
377 }
378 return Some(result);
379 }
380 }
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388
389 fn write_fake_fit(records: &[u8]) -> Vec<u8> {
392 let data_size = records.len() as u32;
393 let mut bytes = vec![14u8, 0x20, 0xD0, 0x52];
394 bytes.extend_from_slice(&data_size.to_le_bytes());
395 bytes.extend_from_slice(b".FIT");
396 bytes.extend_from_slice(&[0, 0]); bytes.extend_from_slice(records);
398 bytes.extend_from_slice(&[0, 0]); bytes
400 }
401
402 fn one_u32_record() -> Vec<u8> {
405 let mut records = Vec::new();
406 records.push(0x40);
408 records.extend_from_slice(&[
409 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x06, ]);
414 records.push(0x00);
416 records.extend_from_slice(&[0xF8, 0xEF, 0x59, 0x3B]); records
418 }
419
420 #[test]
421 fn decodes_synthetic_one_record_file() {
422 let fit = write_fake_fit(&one_u32_record());
423 let (msgs, errs) = Decoder::new(&fit).read_all();
424 assert!(errs.is_empty(), "got errors: {errs:?}");
425 assert_eq!(msgs.len(), 1);
426 let m = &msgs[0];
427 assert_eq!(m.global_mesg_num, 0);
428 assert_eq!(m.fields.len(), 1);
429 assert_eq!(m.fields[0].field_def_num, 0);
430 assert_eq!(m.fields[0].value.as_u32(), Some(995749880));
431 }
432
433 #[test]
434 fn data_without_definition_yields_error() {
435 let bad = vec![0x00, 0x01, 0x02, 0x03, 0x04]; let fit = write_fake_fit(&bad);
438 let (_msgs, errs) = Decoder::new(&fit).read_all();
439 assert_eq!(errs.len(), 1);
440 assert!(matches!(errs[0], FitError::UndefinedLocalMesgNum(0)));
441 }
442
443 #[test]
444 fn compressed_timestamp_without_definition_yields_error() {
445 let fit = write_fake_fit(&[0x80]);
447 let (_msgs, errs) = Decoder::new(&fit).read_all();
448 assert_eq!(errs.len(), 1);
449 assert!(matches!(errs[0], FitError::UndefinedLocalMesgNum(_)));
450 }
451
452 #[test]
453 fn malformed_field_definition_is_caught_at_definition() {
454 let mut records = Vec::new();
456 records.push(0x40);
457 records.extend_from_slice(&[
458 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x06, ]);
461 let fit = write_fake_fit(&records);
462 let (_, errs) = Decoder::new(&fit).read_all();
463 assert_eq!(errs.len(), 1);
464 assert!(matches!(
465 errs[0],
466 FitError::MalformedField {
467 field_def_num: 0,
468 size: 3,
469 ..
470 }
471 ));
472 }
473
474 #[test]
475 fn read_all_returns_pair() {
476 let fit = write_fake_fit(&one_u32_record());
477 let (msgs, errs) = Decoder::new(&fit).read_all();
478 assert_eq!(msgs.len(), 1);
479 assert_eq!(errs.len(), 0);
480 }
481
482 #[test]
483 fn endian_round_trip_via_definition() {
484 let mut records = Vec::new();
486 records.push(0x40);
487 records.extend_from_slice(&[
488 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, 0x86, ]);
492 records.push(0x00); records.extend_from_slice(&[0x3B, 0x59, 0xEF, 0xF8]); let fit = write_fake_fit(&records);
495 let (msgs, errs) = Decoder::new(&fit).read_all();
496 assert!(errs.is_empty());
497 assert_eq!(msgs[0].fields[0].value.as_u32(), Some(995749880));
498 }
499
500 #[test]
503 fn multi_fit_chain_resets_local_defs() {
504 let one = write_fake_fit(&one_u32_record());
505 let chained: Vec<u8> = one.iter().chain(one.iter()).copied().collect();
506 let (msgs, errs) = Decoder::new(&chained).read_all();
507 assert!(errs.is_empty(), "got errors: {errs:?}");
508 assert_eq!(msgs.len(), 2, "expected one message per chained file");
509 for m in &msgs {
510 assert_eq!(m.fields[0].value.as_u32(), Some(995749880));
511 }
512 }
513
514 fn record_definition() -> Vec<u8> {
521 let mut r = Vec::new();
522 r.push(0x40);
524 r.extend_from_slice(&[
525 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x01, 0x02, 0xFD, 0x04, 0x86, ]);
531 r
532 }
533
534 fn u32_le(v: u32) -> [u8; 4] {
536 v.to_le_bytes()
537 }
538
539 #[test]
540 fn compressed_timestamp_basic() {
541 let mut records = Vec::new();
548 records.extend_from_slice(&record_definition());
549 records.push(0x00);
551 records.push(120); records.extend_from_slice(&u32_le(1000)); records.push(0x85);
556 records.push(130); let fit = write_fake_fit(&records);
559 let (msgs, errs) = Decoder::new(&fit).read_all();
560 assert!(errs.is_empty(), "got errors: {errs:?}");
561 assert_eq!(msgs.len(), 2);
562
563 assert_eq!(msgs[0].fields[0].value.as_u8(), Some(120));
565 assert_eq!(msgs[0].fields[1].value.as_u32(), Some(1000));
566
567 assert_eq!(msgs[1].fields[0].value.as_u8(), Some(130));
569 assert_eq!(msgs[1].fields[1].value.as_u32(), Some(1029));
571 }
572
573 #[test]
574 fn compressed_timestamp_no_rollover() {
575 let mut records = Vec::new();
578 records.extend_from_slice(&record_definition());
579 records.push(0x00); records.push(120);
581 records.extend_from_slice(&u32_le(1000));
582 records.push(0x8A);
584 records.push(125);
585
586 let fit = write_fake_fit(&records);
587 let (msgs, errs) = Decoder::new(&fit).read_all();
588 assert!(errs.is_empty());
589 assert_eq!(msgs.len(), 2);
590 assert_eq!(msgs[1].fields[1].value.as_u32(), Some(1002));
591 }
592
593 #[test]
594 fn compressed_timestamp_rollover() {
595 let mut records = Vec::new();
598 records.extend_from_slice(&record_definition());
599 records.push(0x00);
600 records.push(120);
601 records.extend_from_slice(&u32_le(1000));
602 records.push(0x83);
604 records.push(125);
605
606 let fit = write_fake_fit(&records);
607 let (msgs, errs) = Decoder::new(&fit).read_all();
608 assert!(errs.is_empty());
609 assert_eq!(msgs[1].fields[1].value.as_u32(), Some(1027));
610 }
611
612 #[test]
613 fn compressed_timestamp_chain_of_records() {
614 let base: u32 = 995749880;
617 let mut records = Vec::new();
618 records.extend_from_slice(&record_definition());
619
620 records.push(0x00);
622 records.push(100);
623 records.extend_from_slice(&u32_le(base));
624
625 for (i, offset) in (25..30).enumerate() {
628 let expected_ts = (base & !0x1F) | offset;
631 records.push(0x80 | offset as u8);
632 records.push(100 + i as u8);
633
634 assert_eq!(expected_ts, base - (base & 0x1F) + offset);
636 }
637
638 let fit = write_fake_fit(&records);
639 let (msgs, errs) = Decoder::new(&fit).read_all();
640 assert!(errs.is_empty(), "got errors: {errs:?}");
641 assert_eq!(msgs.len(), 6);
642
643 assert_eq!(msgs[0].fields[1].value.as_u32(), Some(base));
644 for (i, offset) in (25..30).enumerate() {
645 let expected = (base & !0x1F) | offset;
646 assert_eq!(
647 msgs[i + 1].fields[1].value.as_u32(),
648 Some(expected),
649 "compressed record {i} with offset {offset}"
650 );
651 }
652 }
653
654 #[test]
655 fn compressed_timestamp_rollover_at_boundary() {
656 let ts: u32 = 1023; let mut records = Vec::new();
659 records.extend_from_slice(&record_definition());
660 records.push(0x00);
661 records.push(100);
662 records.extend_from_slice(&u32_le(ts));
663 records.push(0x80);
665 records.push(110);
666
667 let fit = write_fake_fit(&records);
668 let (msgs, errs) = Decoder::new(&fit).read_all();
669 assert!(errs.is_empty());
670 assert_eq!(msgs[1].fields[1].value.as_u32(), Some(1024));
672 }
673
674 #[test]
675 fn compressed_timestamp_cold_start_no_prior_timestamp() {
676 let mut records = Vec::new();
679 records.extend_from_slice(&record_definition());
680 records.push(0x85);
682 records.push(100);
683
684 let fit = write_fake_fit(&records);
685 let (msgs, errs) = Decoder::new(&fit).read_all();
686 assert!(errs.is_empty());
687 assert_eq!(msgs.len(), 1);
688 assert_eq!(msgs[0].fields[1].value.as_u32(), Some(5));
689 }
690
691 #[test]
692 fn multi_chain_resets_compressed_state() {
693 let mut chain1 = Vec::new();
696 chain1.extend_from_slice(&record_definition());
697 chain1.push(0x00);
698 chain1.push(100);
699 chain1.extend_from_slice(&u32_le(1000));
700 chain1.push(0x8A);
702 chain1.push(110);
703
704 let mut chain2 = Vec::new();
705 chain2.extend_from_slice(&record_definition());
706 chain2.push(0x00);
707 chain2.push(100);
708 chain2.extend_from_slice(&u32_le(2000));
709 chain2.push(0x85);
712 chain2.push(110);
713
714 let fit1 = write_fake_fit(&chain1);
715 let fit2 = write_fake_fit(&chain2);
716 let chained: Vec<u8> = fit1.iter().chain(fit2.iter()).copied().collect();
717
718 let (msgs, errs) = Decoder::new(&chained).read_all();
719 assert!(errs.is_empty(), "got errors: {errs:?}");
720 assert_eq!(msgs.len(), 4);
721
722 assert_eq!(msgs[0].fields[1].value.as_u32(), Some(1000));
724 assert_eq!(msgs[1].fields[1].value.as_u32(), Some(1002));
725
726 assert_eq!(msgs[2].fields[1].value.as_u32(), Some(2000));
728 assert_eq!(msgs[3].fields[1].value.as_u32(), Some(2021));
730 }
731}