use std::array;
use smallvec::SmallVec;
use crate::base_type::BaseType;
use crate::error::FitError;
use crate::stream::{ByteStream, Endian};
pub const FIELDS_INLINE: usize = 48;
pub const DEV_FIELDS_INLINE: usize = 8;
pub const LOCAL_DEFINITION_SLOTS: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldDefinition {
pub field_def_num: u8,
pub size: u8,
pub base_type: BaseType,
pub base_type_byte: u8,
}
impl FieldDefinition {
pub fn element_count(&self) -> usize {
let stride = self.base_type.element_size();
if stride == 0 || (self.size as usize) % stride != 0 {
0
} else {
(self.size as usize) / stride
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeveloperFieldDefinition {
pub field_def_num: u8,
pub size: u8,
pub developer_data_index: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MessageDefinition {
pub global_mesg_num: u16,
pub endian: Endian,
pub fields: SmallVec<[FieldDefinition; FIELDS_INLINE]>,
pub dev_fields: SmallVec<[DeveloperFieldDefinition; DEV_FIELDS_INLINE]>,
pub reserved: u8,
}
impl MessageDefinition {
pub fn parse(stream: &mut ByteStream<'_>, has_dev_data: bool) -> Result<Self, FitError> {
let reserved = stream.read_u8()?;
let architecture = stream.read_u8()?;
let endian = if architecture == 0 {
Endian::Little
} else {
Endian::Big
};
let global_mesg_num = stream.read_u16(endian)?;
let field_count = stream.read_u8()? as usize;
let mut fields: SmallVec<[FieldDefinition; FIELDS_INLINE]> =
SmallVec::with_capacity(field_count);
for _ in 0..field_count {
let field_def_num = stream.read_u8()?;
let size = stream.read_u8()?;
let base_type_byte = stream.read_u8()?;
let base_type = BaseType::from_byte(base_type_byte)?;
let stride = base_type.element_size();
if !base_type.is_string()
&& !base_type.is_byte()
&& (size == 0 || (size as usize) % stride != 0)
{
return Err(FitError::MalformedField {
field_def_num,
size,
element_size: stride,
});
}
fields.push(FieldDefinition {
field_def_num,
size,
base_type,
base_type_byte,
});
}
let mut dev_fields: SmallVec<[DeveloperFieldDefinition; DEV_FIELDS_INLINE]> =
SmallVec::new();
if has_dev_data {
let dev_count = stream.read_u8()? as usize;
dev_fields.reserve_exact(dev_count);
for _ in 0..dev_count {
let field_def_num = stream.read_u8()?;
let size = stream.read_u8()?;
let developer_data_index = stream.read_u8()?;
dev_fields.push(DeveloperFieldDefinition {
field_def_num,
size,
developer_data_index,
});
}
}
Ok(Self {
global_mesg_num,
endian,
fields,
dev_fields,
reserved,
})
}
pub fn data_size(&self) -> usize {
let core: usize = self.fields.iter().map(|f| f.size as usize).sum();
let dev: usize = self.dev_fields.iter().map(|f| f.size as usize).sum();
core + dev
}
pub fn field(&self, field_def_num: u8) -> Option<&FieldDefinition> {
self.fields
.iter()
.find(|f| f.field_def_num == field_def_num)
}
}
#[derive(Debug, Default)]
pub struct LocalDefinitions {
slots: [Option<MessageDefinition>; LOCAL_DEFINITION_SLOTS],
}
impl LocalDefinitions {
pub fn new() -> Self {
Self {
slots: array::from_fn(|_| None),
}
}
pub fn get(&self, local_mesg_num: u8) -> Option<&MessageDefinition> {
self.slots
.get(local_mesg_num as usize)
.and_then(Option::as_ref)
}
pub fn require(&self, local_mesg_num: u8) -> Result<&MessageDefinition, FitError> {
self.get(local_mesg_num)
.ok_or(FitError::UndefinedLocalMesgNum(local_mesg_num))
}
pub fn set(&mut self, local_mesg_num: u8, def: MessageDefinition) {
if let Some(slot) = self.slots.get_mut(local_mesg_num as usize) {
*slot = Some(def);
}
}
pub fn clear(&mut self) {
for slot in self.slots.iter_mut() {
*slot = None;
}
}
pub fn occupied(&self) -> usize {
self.slots.iter().filter(|s| s.is_some()).count()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_le_definition_bytes() -> Vec<u8> {
vec![
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x02, 0x84, 0x02, 0x02, 0x84, 0x03, 0x04, 0x86, 0x04, 0x04, 0x8C, ]
}
#[test]
fn parses_canonical_le_definition() {
let bytes = sample_le_definition_bytes();
let mut stream = ByteStream::new(&bytes);
let def = MessageDefinition::parse(&mut stream, false).unwrap();
assert_eq!(def.global_mesg_num, 0);
assert_eq!(def.endian, Endian::Little);
assert_eq!(def.reserved, 0);
assert_eq!(def.fields.len(), 5);
assert!(def.dev_fields.is_empty());
assert_eq!(def.fields[0].field_def_num, 0);
assert_eq!(def.fields[0].size, 1);
assert_eq!(def.fields[0].base_type, BaseType::Enum);
assert_eq!(def.fields[1].base_type, BaseType::UInt16);
assert!(BaseType::endian_flag_set(def.fields[1].base_type_byte));
assert_eq!(def.fields[4].base_type, BaseType::UInt32z);
assert_eq!(def.fields[4].element_count(), 1);
assert_eq!(def.data_size(), 1 + 2 + 2 + 4 + 4);
}
#[test]
fn parses_big_endian_definition() {
let bytes = vec![
0x00, 0x01, 0x00, 0x14, 0x01, 253, 0x04, 0x86, ];
let mut stream = ByteStream::new(&bytes);
let def = MessageDefinition::parse(&mut stream, false).unwrap();
assert_eq!(def.endian, Endian::Big);
assert_eq!(def.global_mesg_num, 20);
assert_eq!(def.fields.len(), 1);
assert_eq!(def.fields[0].field_def_num, 253);
assert_eq!(def.fields[0].base_type, BaseType::UInt32);
}
#[test]
fn parses_definition_with_dev_data() {
let mut bytes = sample_le_definition_bytes();
bytes.extend_from_slice(&[
0x02, 0x00, 0x04, 0x00, 0x05, 0x02, 0x01, ]);
let mut stream = ByteStream::new(&bytes);
let def = MessageDefinition::parse(&mut stream, true).unwrap();
assert_eq!(def.dev_fields.len(), 2);
assert_eq!(def.dev_fields[0].field_def_num, 0);
assert_eq!(def.dev_fields[0].developer_data_index, 0);
assert_eq!(def.dev_fields[1].field_def_num, 5);
assert_eq!(def.dev_fields[1].developer_data_index, 1);
assert_eq!(def.data_size(), (1 + 2 + 2 + 4 + 4) + (4 + 2));
}
#[test]
fn rejects_unknown_base_type() {
let bytes = vec![
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x1A, ];
let mut stream = ByteStream::new(&bytes);
let err = MessageDefinition::parse(&mut stream, false).unwrap_err();
assert!(matches!(err, FitError::UnknownBaseType(_, _)));
}
#[test]
fn local_definitions_set_get_clear() {
let mut tbl = LocalDefinitions::new();
assert_eq!(tbl.occupied(), 0);
assert!(tbl.get(0).is_none());
let bytes = sample_le_definition_bytes();
let mut stream = ByteStream::new(&bytes);
let def = MessageDefinition::parse(&mut stream, false).unwrap();
tbl.set(3, def.clone());
assert_eq!(tbl.occupied(), 1);
assert_eq!(tbl.get(3).unwrap().global_mesg_num, 0);
tbl.set(3, def);
assert_eq!(tbl.occupied(), 1);
tbl.set(
99,
MessageDefinition {
global_mesg_num: 0,
endian: Endian::Little,
fields: SmallVec::new(),
dev_fields: SmallVec::new(),
reserved: 0,
},
);
assert_eq!(tbl.occupied(), 1);
tbl.clear();
assert_eq!(tbl.occupied(), 0);
}
#[test]
fn require_returns_useful_error() {
let tbl = LocalDefinitions::new();
let err = tbl.require(7).unwrap_err();
assert_eq!(err, FitError::UndefinedLocalMesgNum(7));
}
#[test]
fn element_count_handles_arrays_and_misalignment() {
let single = FieldDefinition {
field_def_num: 0,
size: 4,
base_type: BaseType::UInt32,
base_type_byte: 0x06,
};
assert_eq!(single.element_count(), 1);
let array = FieldDefinition {
field_def_num: 0,
size: 8,
base_type: BaseType::UInt16,
base_type_byte: 0x04,
};
assert_eq!(array.element_count(), 4);
let bad = FieldDefinition {
field_def_num: 0,
size: 3,
base_type: BaseType::UInt32,
base_type_byte: 0x06,
};
assert_eq!(bad.element_count(), 0);
}
}