1use crate::base_type::BaseType;
21use crate::error::FitError;
22use crate::stream::Endian;
23
24#[derive(Debug, Clone, PartialEq)]
26pub enum RawValue {
27 Invalid,
29
30 EnumScalar(u8),
32 U8Scalar(u8),
33 U8zScalar(u8),
34 I8Scalar(i8),
35 U16Scalar(u16),
36 U16zScalar(u16),
37 I16Scalar(i16),
38 U32Scalar(u32),
39 U32zScalar(u32),
40 I32Scalar(i32),
41 U64Scalar(u64),
42 U64zScalar(u64),
43 I64Scalar(i64),
44 F32Scalar(f32),
45 F64Scalar(f64),
46
47 EnumArray(Box<[u8]>),
49 U8Array(Box<[u8]>),
50 U8zArray(Box<[u8]>),
51 I8Array(Box<[i8]>),
52 U16Array(Box<[u16]>),
53 U16zArray(Box<[u16]>),
54 I16Array(Box<[i16]>),
55 U32Array(Box<[u32]>),
56 U32zArray(Box<[u32]>),
57 I32Array(Box<[i32]>),
58 U64Array(Box<[u64]>),
59 U64zArray(Box<[u64]>),
60 I64Array(Box<[i64]>),
61 F32Array(Box<[f32]>),
62 F64Array(Box<[f64]>),
63
64 String(Box<str>),
66 Byte(Box<[u8]>),
70}
71
72impl RawValue {
73 #[inline]
75 pub fn is_invalid(&self) -> bool {
76 matches!(self, RawValue::Invalid)
77 }
78
79 pub fn as_str(&self) -> Option<&str> {
81 match self {
82 RawValue::String(s) => Some(s.as_ref()),
83 _ => None,
84 }
85 }
86
87 pub fn as_u8(&self) -> Option<u8> {
89 match *self {
90 RawValue::U8Scalar(v) | RawValue::U8zScalar(v) | RawValue::EnumScalar(v) => Some(v),
91 RawValue::Byte(ref b) if b.len() == 1 => Some(b[0]),
92 _ => None,
93 }
94 }
95
96 pub fn as_u16(&self) -> Option<u16> {
98 match *self {
99 RawValue::U16Scalar(v) | RawValue::U16zScalar(v) => Some(v),
100 RawValue::U8Scalar(v) | RawValue::U8zScalar(v) | RawValue::EnumScalar(v) => {
101 Some(v as u16)
102 }
103 _ => None,
104 }
105 }
106
107 pub fn as_u32(&self) -> Option<u32> {
109 match *self {
110 RawValue::U32Scalar(v) | RawValue::U32zScalar(v) => Some(v),
111 _ => None,
112 }
113 }
114
115 pub fn scalar_u64(&self) -> Option<u64> {
119 match *self {
120 RawValue::EnumScalar(v) | RawValue::U8Scalar(v) | RawValue::U8zScalar(v) => {
121 Some(v as u64)
122 }
123 RawValue::U16Scalar(v) | RawValue::U16zScalar(v) => Some(v as u64),
124 RawValue::U32Scalar(v) | RawValue::U32zScalar(v) => Some(v as u64),
125 RawValue::U64Scalar(v) | RawValue::U64zScalar(v) => Some(v),
126 RawValue::I8Scalar(v) => Some(v as u8 as u64),
127 RawValue::I16Scalar(v) => Some(v as u16 as u64),
128 RawValue::I32Scalar(v) => Some(v as u32 as u64),
129 RawValue::I64Scalar(v) => Some(v as u64),
130 RawValue::Byte(ref b) if b.len() == 1 => Some(b[0] as u64),
131 _ => None,
132 }
133 }
134
135 pub fn scalar_f64(&self) -> Option<f64> {
137 if let Some(u) = self.scalar_u64() {
138 return Some(u as f64);
139 }
140 match *self {
141 RawValue::F32Scalar(v) => Some(v as f64),
142 RawValue::F64Scalar(v) => Some(v),
143 _ => None,
144 }
145 }
146
147 pub fn to_f64s(&self) -> Option<Vec<f64>> {
150 use RawValue::*;
151 Some(match self {
152 EnumScalar(v) | U8Scalar(v) | U8zScalar(v) => vec![*v as f64],
153 U16Scalar(v) | U16zScalar(v) => vec![*v as f64],
154 U32Scalar(v) | U32zScalar(v) => vec![*v as f64],
155 U64Scalar(v) | U64zScalar(v) => vec![*v as f64],
156 I8Scalar(v) => vec![*v as f64],
157 I16Scalar(v) => vec![*v as f64],
158 I32Scalar(v) => vec![*v as f64],
159 I64Scalar(v) => vec![*v as f64],
160 F32Scalar(v) => vec![*v as f64],
161 F64Scalar(v) => vec![*v],
162 EnumArray(a) | U8Array(a) | U8zArray(a) | Byte(a) => {
163 a.iter().map(|&x| x as f64).collect()
164 }
165 U16Array(a) | U16zArray(a) => a.iter().map(|&x| x as f64).collect(),
166 U32Array(a) | U32zArray(a) => a.iter().map(|&x| x as f64).collect(),
167 U64Array(a) | U64zArray(a) => a.iter().map(|&x| x as f64).collect(),
168 I8Array(a) => a.iter().map(|&x| x as f64).collect(),
169 I16Array(a) => a.iter().map(|&x| x as f64).collect(),
170 I32Array(a) => a.iter().map(|&x| x as f64).collect(),
171 I64Array(a) => a.iter().map(|&x| x as f64).collect(),
172 F32Array(a) => a.iter().map(|&x| x as f64).collect(),
173 F64Array(a) => a.to_vec(),
174 _ => return None,
175 })
176 }
177}
178
179pub(crate) fn decode_value(
189 base_type: BaseType,
190 raw: &[u8],
191 endian: Endian,
192 field_def_num: u8,
193) -> Result<RawValue, FitError> {
194 let stride = base_type.element_size();
195 if !base_type.is_string() && !base_type.is_byte() && (raw.is_empty() || raw.len() % stride != 0)
196 {
197 return Err(FitError::MalformedField {
198 field_def_num,
199 size: raw.len() as u8,
200 element_size: stride,
201 });
202 }
203
204 Ok(match base_type {
205 BaseType::Enum => collapse(
206 decode_u8_iter(raw),
207 |&v| v == 0xFF,
208 RawValue::EnumScalar,
209 RawValue::EnumArray,
210 ),
211 BaseType::UInt8 => collapse(
212 decode_u8_iter(raw),
213 |&v| v == 0xFF,
214 RawValue::U8Scalar,
215 RawValue::U8Array,
216 ),
217 BaseType::UInt8z => collapse(
218 decode_u8_iter(raw),
219 |&v| v == 0x00,
220 RawValue::U8zScalar,
221 RawValue::U8zArray,
222 ),
223 BaseType::SInt8 => collapse(
224 raw.iter().map(|&b| b as i8),
225 |&v| v == i8::MAX,
226 RawValue::I8Scalar,
227 RawValue::I8Array,
228 ),
229 BaseType::Byte => decode_byte(raw),
230 BaseType::String => decode_string(raw),
231
232 BaseType::UInt16 => collapse(
233 decode_u16_iter(raw, endian),
234 |&v| v == u16::MAX,
235 RawValue::U16Scalar,
236 RawValue::U16Array,
237 ),
238 BaseType::UInt16z => collapse(
239 decode_u16_iter(raw, endian),
240 |&v| v == 0,
241 RawValue::U16zScalar,
242 RawValue::U16zArray,
243 ),
244 BaseType::SInt16 => collapse(
245 decode_i16_iter(raw, endian),
246 |&v| v == i16::MAX,
247 RawValue::I16Scalar,
248 RawValue::I16Array,
249 ),
250
251 BaseType::UInt32 => collapse(
252 decode_u32_iter(raw, endian),
253 |&v| v == u32::MAX,
254 RawValue::U32Scalar,
255 RawValue::U32Array,
256 ),
257 BaseType::UInt32z => collapse(
258 decode_u32_iter(raw, endian),
259 |&v| v == 0,
260 RawValue::U32zScalar,
261 RawValue::U32zArray,
262 ),
263 BaseType::SInt32 => collapse(
264 decode_i32_iter(raw, endian),
265 |&v| v == i32::MAX,
266 RawValue::I32Scalar,
267 RawValue::I32Array,
268 ),
269
270 BaseType::UInt64 => collapse(
271 decode_u64_iter(raw, endian),
272 |&v| v == u64::MAX,
273 RawValue::U64Scalar,
274 RawValue::U64Array,
275 ),
276 BaseType::UInt64z => collapse(
277 decode_u64_iter(raw, endian),
278 |&v| v == 0,
279 RawValue::U64zScalar,
280 RawValue::U64zArray,
281 ),
282 BaseType::SInt64 => collapse(
283 decode_i64_iter(raw, endian),
284 |&v| v == i64::MAX,
285 RawValue::I64Scalar,
286 RawValue::I64Array,
287 ),
288
289 BaseType::Float32 => collapse(
290 decode_f32_iter(raw, endian),
291 |v| v.to_bits() == 0xFFFF_FFFF,
292 RawValue::F32Scalar,
293 RawValue::F32Array,
294 ),
295 BaseType::Float64 => collapse(
296 decode_f64_iter(raw, endian),
297 |v| v.to_bits() == 0xFFFF_FFFF_FFFF_FFFF,
298 RawValue::F64Scalar,
299 RawValue::F64Array,
300 ),
301 })
302}
303
304fn decode_u8_iter(raw: &[u8]) -> impl Iterator<Item = u8> + '_ {
310 raw.iter().copied()
311}
312
313fn decode_u16_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = u16> + '_ {
314 raw.chunks_exact(2).map(move |c| {
315 let arr = [c[0], c[1]];
316 match endian {
317 Endian::Little => u16::from_le_bytes(arr),
318 Endian::Big => u16::from_be_bytes(arr),
319 }
320 })
321}
322
323fn decode_i16_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = i16> + '_ {
324 raw.chunks_exact(2).map(move |c| {
325 let arr = [c[0], c[1]];
326 match endian {
327 Endian::Little => i16::from_le_bytes(arr),
328 Endian::Big => i16::from_be_bytes(arr),
329 }
330 })
331}
332
333fn decode_u32_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = u32> + '_ {
334 raw.chunks_exact(4).map(move |c| {
335 let arr = [c[0], c[1], c[2], c[3]];
336 match endian {
337 Endian::Little => u32::from_le_bytes(arr),
338 Endian::Big => u32::from_be_bytes(arr),
339 }
340 })
341}
342
343fn decode_i32_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = i32> + '_ {
344 raw.chunks_exact(4).map(move |c| {
345 let arr = [c[0], c[1], c[2], c[3]];
346 match endian {
347 Endian::Little => i32::from_le_bytes(arr),
348 Endian::Big => i32::from_be_bytes(arr),
349 }
350 })
351}
352
353fn decode_u64_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = u64> + '_ {
354 raw.chunks_exact(8).map(move |c| {
355 let arr = [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]];
356 match endian {
357 Endian::Little => u64::from_le_bytes(arr),
358 Endian::Big => u64::from_be_bytes(arr),
359 }
360 })
361}
362
363fn decode_i64_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = i64> + '_ {
364 raw.chunks_exact(8).map(move |c| {
365 let arr = [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]];
366 match endian {
367 Endian::Little => i64::from_le_bytes(arr),
368 Endian::Big => i64::from_be_bytes(arr),
369 }
370 })
371}
372
373fn decode_f32_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = f32> + '_ {
374 raw.chunks_exact(4).map(move |c| {
375 let arr = [c[0], c[1], c[2], c[3]];
376 match endian {
377 Endian::Little => f32::from_le_bytes(arr),
378 Endian::Big => f32::from_be_bytes(arr),
379 }
380 })
381}
382
383fn decode_f64_iter(raw: &[u8], endian: Endian) -> impl Iterator<Item = f64> + '_ {
384 raw.chunks_exact(8).map(move |c| {
385 let arr = [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]];
386 match endian {
387 Endian::Little => f64::from_le_bytes(arr),
388 Endian::Big => f64::from_be_bytes(arr),
389 }
390 })
391}
392
393fn collapse<T, I, F, S, A>(iter: I, is_invalid: F, scalar: S, array: A) -> RawValue
398where
399 T: Copy,
400 I: Iterator<Item = T>,
401 F: Fn(&T) -> bool,
402 S: FnOnce(T) -> RawValue,
403 A: FnOnce(Box<[T]>) -> RawValue,
404{
405 let collected: Vec<T> = iter.collect();
406 if collected.is_empty() || collected.iter().all(is_invalid) {
407 return RawValue::Invalid;
408 }
409 if collected.len() == 1 {
410 scalar(collected[0])
411 } else {
412 array(collected.into_boxed_slice())
413 }
414}
415
416fn decode_byte(raw: &[u8]) -> RawValue {
417 if !raw.is_empty() && raw.iter().all(|&b| b == 0xFF) {
418 RawValue::Invalid
419 } else {
420 RawValue::Byte(raw.to_vec().into_boxed_slice())
421 }
422}
423
424fn decode_string(raw: &[u8]) -> RawValue {
425 let end = raw.iter().position(|&b| b == 0).unwrap_or(raw.len());
426 if end == 0 {
427 return RawValue::Invalid;
428 }
429 let s = String::from_utf8_lossy(&raw[..end]).into_owned();
430 RawValue::String(s.into_boxed_str())
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 fn dec(bt: BaseType, raw: &[u8], endian: Endian) -> RawValue {
438 decode_value(bt, raw, endian, 0).unwrap()
439 }
440
441 #[test]
442 fn enum_invalid_when_all_ff() {
443 assert_eq!(
444 dec(BaseType::Enum, &[0xFF], Endian::Little),
445 RawValue::Invalid
446 );
447 assert_eq!(
448 dec(BaseType::Enum, &[0xFF, 0xFF], Endian::Little),
449 RawValue::Invalid
450 );
451 }
452
453 #[test]
454 fn enum_valid_keeps_all_elements() {
455 assert_eq!(
456 dec(BaseType::Enum, &[1, 0xFF, 4], Endian::Little),
457 RawValue::EnumArray(vec![1u8, 0xFF, 4].into_boxed_slice())
458 );
459 }
460
461 #[test]
462 fn uint8z_invalid_is_zero_not_ff() {
463 assert_eq!(
464 dec(BaseType::UInt8z, &[0], Endian::Little),
465 RawValue::Invalid
466 );
467 assert_eq!(
468 dec(BaseType::UInt8z, &[0xFF], Endian::Little),
469 RawValue::U8zScalar(0xFF)
470 );
471 }
472
473 #[test]
474 fn byte_invalid_only_when_all_ff() {
475 assert_eq!(
476 dec(BaseType::Byte, &[0xFF], Endian::Little),
477 RawValue::Invalid
478 );
479 assert_eq!(
480 dec(BaseType::Byte, &[0xFF, 0x01, 0xFF], Endian::Little),
481 RawValue::Byte(vec![0xFFu8, 0x01, 0xFF].into_boxed_slice())
482 );
483 assert_eq!(
484 dec(BaseType::Byte, &[0xFF, 0xFF, 0xFF], Endian::Little),
485 RawValue::Invalid
486 );
487 }
488
489 #[test]
490 fn uint16_endianness_and_invalid() {
491 assert_eq!(
492 dec(BaseType::UInt16, &[0x34, 0x12], Endian::Little),
493 RawValue::U16Scalar(0x1234)
494 );
495 assert_eq!(
496 dec(BaseType::UInt16, &[0x12, 0x34], Endian::Big),
497 RawValue::U16Scalar(0x1234)
498 );
499 assert_eq!(
500 dec(BaseType::UInt16, &[0xFF, 0xFF], Endian::Little),
501 RawValue::Invalid
502 );
503 }
504
505 #[test]
506 fn uint32_le_decodes_known_timestamp() {
507 assert_eq!(
508 dec(BaseType::UInt32, &[0xF8, 0xEF, 0x59, 0x3B], Endian::Little),
509 RawValue::U32Scalar(995749880)
510 );
511 }
512
513 #[test]
514 fn float32_invalid_is_all_ones_bit_pattern() {
515 assert_eq!(
516 dec(BaseType::Float32, &[0xFF, 0xFF, 0xFF, 0xFF], Endian::Little),
517 RawValue::Invalid
518 );
519 let v = dec(BaseType::Float32, &[0x00, 0x00, 0x80, 0x3F], Endian::Little);
520 match v {
521 RawValue::F32Scalar(x) => assert_eq!(x, 1.0),
522 _ => panic!("expected F32Scalar"),
523 }
524 }
525
526 #[test]
527 fn string_strips_null_and_lossy_decodes() {
528 assert_eq!(
529 dec(BaseType::String, b"FIT Cookbook\0\0\0", Endian::Little),
530 RawValue::String("FIT Cookbook".into())
531 );
532 assert_eq!(
533 dec(BaseType::String, b"\0", Endian::Little),
534 RawValue::Invalid
535 );
536 assert_eq!(
537 dec(BaseType::String, b"", Endian::Little),
538 RawValue::Invalid
539 );
540 }
541
542 #[test]
543 fn malformed_size_returns_error() {
544 let err = decode_value(BaseType::UInt32, &[0, 0, 0], Endian::Little, 42).unwrap_err();
545 assert!(matches!(
546 err,
547 FitError::MalformedField {
548 field_def_num: 42,
549 ..
550 }
551 ));
552 }
553
554 #[test]
555 fn helpers_extract_scalars() {
556 let v = RawValue::U32Scalar(995749880);
557 assert_eq!(v.as_u32(), Some(995749880));
558 assert!(!v.is_invalid());
559
560 let v = RawValue::EnumScalar(4);
561 assert_eq!(v.as_u8(), Some(4));
562 assert_eq!(v.as_u16(), Some(4));
563
564 assert!(RawValue::Invalid.is_invalid());
565 assert_eq!(RawValue::Invalid.as_u32(), None);
566 }
567
568 #[test]
569 fn sint8_invalid_at_max() {
570 assert_eq!(
571 dec(BaseType::SInt8, &[0x7F], Endian::Little),
572 RawValue::Invalid
573 );
574 assert_eq!(
575 dec(BaseType::SInt8, &[0x7E], Endian::Little),
576 RawValue::I8Scalar(126)
577 );
578 }
579
580 #[test]
581 fn sint16_array_partial_invalid_keeps_field() {
582 assert_eq!(
583 dec(BaseType::SInt16, &[0xFF, 0x7F, 0x05, 0x00], Endian::Little),
584 RawValue::I16Array(vec![i16::MAX, 5].into_boxed_slice())
585 );
586 }
587}