#![warn(missing_docs)]
mod lru;
mod validator;
use crate::{
crc16::Crc16,
encoder::lru::Lru,
encoder::validator::MessageValidator,
profile::{PROFILE_VERSION, typedef::DateTime},
proto::*,
};
use alloc::vec::Vec;
use embedded_io::{Seek, SeekFrom, Write};
pub use validator::{FieldValidationError, MessageValidationError};
#[derive(Debug)]
pub enum Error<E> {
Io {
err: E,
},
EmptyMessages,
MessageValidation {
mesg_index: usize,
err: MessageValidationError,
},
}
impl<E> core::fmt::Display for Error<E>
where
E: core::fmt::Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match &self {
Self::Io { err } => write!(f, "io error: {}", err),
Self::EmptyMessages => write!(f, "messages is empty"),
Self::MessageValidation { mesg_index, err } => {
write!(f, "message validation: mesg_index {}: {}", mesg_index, err)
}
}
}
}
impl<E> From<E> for Error<E> {
fn from(err: E) -> Self {
Self::Io { err }
}
}
impl<E> core::error::Error for Error<E> where E: core::fmt::Debug + core::fmt::Display {}
#[derive(Clone, Copy)]
pub enum HeaderOption {
Normal(u8),
Compressed(u8),
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Endianness {
LittleEndian = 0,
BigEndian = 1,
}
#[derive(Clone, Copy)]
struct Options {
protocol_version: ProtocolVersion,
endianness: Endianness,
header_option: HeaderOption,
}
pub struct Encoder<W> {
writer: W,
n: i64,
last_file_header_pos: i64,
data_size: u32,
crc16: Crc16,
lru: Lru,
buf: Vec<u8>,
timestamp_reference: u32,
options: Options,
message_validator: MessageValidator,
}
impl<W: Write + Seek> Encoder<W> {
pub fn new(writer: W) -> Encoder<W> {
Builder::new().build(writer)
}
pub fn encode(&mut self, fit: &mut FIT) -> Result<(), Error<W::Error>> {
self.select_protocol_version(&mut fit.file_header);
self.validate(fit)?;
self.encode_file_header(&mut fit.file_header)?;
for mesg in &mut fit.messages {
self.encode_message(mesg)?;
}
fit.crc = self.crc16.sum16();
self.encode_crc()?;
self.update_file_header(&mut fit.file_header)?;
self.reset();
Ok(())
}
fn select_protocol_version(&mut self, file_header: &mut FileHeader) {
if self.options.protocol_version.0 != 0 {
file_header.protocol_version = self.options.protocol_version;
} else if file_header.protocol_version.0 == 0 {
file_header.protocol_version = ProtocolVersion::V1
}
}
fn validate(&mut self, fit: &mut FIT) -> Result<(), Error<W::Error>> {
if fit.messages.is_empty() {
return Err(Error::EmptyMessages);
}
let protocol_version = fit.file_header.protocol_version;
for (i, mesg) in fit.messages.iter_mut().enumerate() {
if let Err(err) = self
.message_validator
.validate_message(mesg, protocol_version)
{
return Err(Error::MessageValidation { mesg_index: i, err });
}
}
Ok(())
}
fn encode_file_header(&mut self, file_header: &mut FileHeader) -> Result<(), Error<W::Error>> {
self.last_file_header_pos = self.n;
if file_header.size != 12 {
file_header.size = 14;
}
if file_header.profile_version == 0 {
file_header.profile_version = PROFILE_VERSION;
}
file_header.data_type = FileHeader::DATA_TYPE;
self.buf.clear();
write_file_header_to_vec(&mut self.buf, file_header);
self.writer.write_all(&self.buf)?;
self.n += self.buf.len() as i64;
Ok(())
}
fn update_file_header(&mut self, file_header: &mut FileHeader) -> Result<(), Error<W::Error>> {
file_header.data_size = self.data_size;
self.buf.clear();
write_file_header_to_vec(&mut self.buf, file_header);
if file_header.size == 14 {
self.crc16.write(&self.buf[..12]);
file_header.crc = self.crc16.sum16();
self.buf[12..14].copy_from_slice(&self.crc16.sum16().to_le_bytes());
self.crc16.reset();
}
let size = self.n - self.last_file_header_pos;
self.writer.seek(SeekFrom::Current(-size))?;
self.writer.write_all(&self.buf)?;
let n = self.buf.len() as i64;
self.writer.seek(SeekFrom::Current(size - n))?;
Ok(())
}
fn encode_message(&mut self, mesg: &mut Message) -> Result<(), Error<W::Error>> {
mesg.header = Message::NORMAL_HEADER_MASK;
if let HeaderOption::Compressed(_) = self.options.header_option {
self.compress_timestamp_into_header(mesg);
}
self.buf.clear();
write_message_definition_to_vec(&mut self.buf, mesg, self.options.endianness as u8);
let (local_mesg_num, is_new_mesg_def) = self.lru.put(&self.buf);
self.buf[0] |= local_mesg_num;
if mesg.header & Message::COMPRESSED_HEADER_MASK == Message::COMPRESSED_HEADER_MASK {
mesg.header |= local_mesg_num << Message::COMPRESSED_BIT_SHIFT;
} else {
mesg.header |= local_mesg_num;
}
if is_new_mesg_def {
self.writer.write_all(&self.buf)?;
self.crc16.write(&self.buf);
self.n += self.buf.len() as i64;
self.data_size += self.buf.len() as u32;
}
self.write_message_checksum(mesg, self.options.endianness as u8)?;
Ok(())
}
fn compress_timestamp_into_header(&mut self, mesg: &mut Message) {
let mut timestamp = u32::MAX;
if let Some(field) = mesg.fields.iter().find(|f| f.num == Field::TIMESTAMP)
&& let Value::Uint32(v) = field.value
{
timestamp = v;
}
if timestamp == u32::MAX || timestamp < DateTime::MIN.0 {
return;
}
if timestamp.wrapping_sub(self.timestamp_reference) as u8 > Message::COMPRESSED_TIME_MASK {
self.timestamp_reference = timestamp;
return;
}
let time_offset = (timestamp & Message::COMPRESSED_TIME_MASK as u32) as u8;
mesg.header = Message::COMPRESSED_HEADER_MASK | time_offset;
mesg.fields.retain(|field| field.num != Field::TIMESTAMP);
}
fn write_message_checksum(&mut self, mesg: &Message, arch: u8) -> Result<(), Error<W::Error>> {
self.writer.write_all(&[mesg.header])?;
self.crc16.write(&[mesg.header]);
self.n += 1;
self.data_size += 1;
for field in &mesg.fields {
self.buf.clear();
write_value_to_vec(&mut self.buf, &field.value, arch);
self.writer.write_all(&self.buf)?;
self.crc16.write(&self.buf);
self.n += self.buf.len() as i64;
self.data_size += self.buf.len() as u32;
}
for dev_field in &mesg.developer_fields {
self.buf.clear();
write_value_to_vec(&mut self.buf, &dev_field.value, arch);
self.writer.write_all(&self.buf)?;
self.crc16.write(&self.buf);
self.n += self.buf.len() as i64;
self.data_size += self.buf.len() as u32;
}
Ok(())
}
fn encode_crc(&mut self) -> Result<(), Error<W::Error>> {
let crc = self.crc16.sum16();
self.writer.write_all(&crc.to_le_bytes())?;
self.n += 2;
self.crc16.reset();
Ok(())
}
fn reset(&mut self) {
self.timestamp_reference = 0;
self.data_size = 0;
self.lru.reset();
self.message_validator.reset();
}
}
fn write_file_header_to_vec(buf: &mut Vec<u8>, file_header: &FileHeader) {
buf.push(file_header.size);
buf.push(file_header.protocol_version.0);
buf.extend_from_slice(&file_header.profile_version.to_le_bytes());
buf.extend_from_slice(&file_header.data_size.to_le_bytes());
buf.extend_from_slice(FileHeader::DATA_TYPE.as_bytes());
if file_header.size == 14 {
buf.extend_from_slice(&file_header.crc.to_le_bytes());
}
}
fn write_message_definition_to_vec(buf: &mut Vec<u8>, mesg: &Message, arch: u8) {
buf.extend_from_slice(&[
Message::DEFINITION_MASK, 0, arch, ]);
buf.extend_from_slice(&match arch {
0 => mesg.num.0.to_le_bytes(),
_ => mesg.num.0.to_be_bytes(),
});
buf.push(mesg.fields.len() as u8);
for field in &mesg.fields {
buf.push(field.num);
buf.push(field.value.size() as u8);
buf.push(field.profile_type.base_type().0);
}
if mesg.developer_fields.is_empty() {
return;
}
buf[0] |= Message::DEV_DATA_MASK;
buf.push(mesg.developer_fields.len() as u8);
for developer_field in &mesg.developer_fields {
buf.push(developer_field.num);
buf.push(developer_field.value.size() as u8);
buf.push(developer_field.developer_data_index);
}
}
fn write_value_to_vec(w: &mut Vec<u8>, value: &Value, arch: u8) {
match value {
Value::Int8(v) => w.push(*v as u8),
Value::Uint8(v) => w.push(*v),
Value::Int16(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Uint16(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Int32(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Uint32(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::String(v) => {
let b = v.as_bytes();
w.extend_from_slice(b);
if b.is_empty() || b[b.len() - 1] != 0 {
w.push(0);
}
}
Value::Float32(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Float64(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Int64(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::Uint64(v) => w.extend_from_slice(&match arch {
0 => v.to_le_bytes(),
_ => v.to_be_bytes(),
}),
Value::VecInt8(v) => w.extend(v.iter().map(|&x| x as u8)),
Value::VecUint8(v) => w.extend(v.iter()),
Value::VecInt16(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecUint16(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecInt32(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecUint32(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecString(v) => {
for x in v {
let b = x.as_bytes();
w.extend_from_slice(b);
if b.is_empty() || b[b.len() - 1] != 0 {
w.push(0);
}
}
}
Value::VecFloat32(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecFloat64(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecInt64(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
Value::VecUint64(v) => match arch {
0 => w.extend(v.iter().flat_map(|x| x.to_le_bytes())),
_ => w.extend(v.iter().flat_map(|x| x.to_be_bytes())),
},
_ => {} };
}
impl Encoder<()> {
pub const fn builder() -> Builder {
Builder::new()
}
}
pub struct Builder {
options: Options,
}
impl Builder {
pub const fn new() -> Builder {
Self {
options: Options {
protocol_version: ProtocolVersion(0),
endianness: Endianness::LittleEndian,
header_option: HeaderOption::Normal(0),
},
}
}
pub const fn protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
self.options.protocol_version = protocol_version;
self
}
pub const fn endianness(mut self, endianness: Endianness) -> Self {
self.options.endianness = endianness;
self
}
pub const fn header_option(mut self, header_option: HeaderOption) -> Self {
self.options.header_option = header_option;
self
}
pub fn build<W: Write + Seek>(&self, writer: W) -> Encoder<W> {
Encoder {
writer,
n: 0,
last_file_header_pos: 0,
data_size: 0,
crc16: Crc16::new(),
lru: Lru::new(
match self.options.header_option {
HeaderOption::Normal(interleave) => interleave.min(15) as usize,
HeaderOption::Compressed(interleave) => interleave.min(3) as usize,
} + 1,
),
buf: Vec::with_capacity(1537),
timestamp_reference: 0,
options: self.options,
message_validator: MessageValidator::new(),
}
}
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::{
Encoder,
encoder::write_value_to_vec,
profile::{mesgdef, typedef},
proto::{Message, Value},
};
use alloc::{borrow::ToOwned, vec, vec::Vec};
use embedded_io::{ErrorKind, ErrorType, Seek, Write};
struct Sink;
impl ErrorType for Sink {
type Error = ErrorKind;
}
impl Write for Sink {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Seek for Sink {
fn seek(&mut self, _: embedded_io::SeekFrom) -> Result<u64, Self::Error> {
Ok(0)
}
}
#[test]
fn compress_timestamp_into_header() {
let meter: u32 = 100;
let mut mesgs = vec![
{
let mut file_id = mesgdef::FileId::new();
file_id.manufacturer = typedef::Manufacturer::DEVELOPMENT;
Message::from(file_id)
},
{
let mut rec = mesgdef::Record::new();
rec.timestamp = typedef::DateTime(0);
rec.distance = 100 * meter;
Message::from(rec)
},
{
let mut rec = mesgdef::Record::new();
rec.timestamp = typedef::DateTime(1062594924);
rec.distance = 200 * meter;
Message::from(rec)
},
{
let mut rec = mesgdef::Record::new();
rec.timestamp = typedef::DateTime(1062594925);
rec.distance = 300 * meter;
Message::from(rec)
},
];
let expected = vec![
{
let mut file_id = mesgdef::FileId::new();
file_id.manufacturer = typedef::Manufacturer::DEVELOPMENT;
Message::from(file_id) },
{
let mut rec = mesgdef::Record::new();
rec.timestamp = typedef::DateTime(0); rec.distance = 100 * meter;
Message::from(rec)
},
{
let mut rec = mesgdef::Record::new();
rec.timestamp = typedef::DateTime(1062594924); rec.distance = 200 * meter;
Message::from(rec)
},
{
let mut rec = mesgdef::Record::new();
rec.distance = 300 * meter;
let mut mesg = Message::from(rec);
mesg.header |= Message::COMPRESSED_HEADER_MASK
| (1062594925 & Message::COMPRESSED_TIME_MASK as u32) as u8;
mesg
},
];
let mut enc = Encoder::new(Sink {});
for mesg in mesgs.iter_mut() {
enc.compress_timestamp_into_header(mesg);
}
assert_eq!(mesgs, expected);
}
#[test]
fn test_write_value_to_vec() {
struct Case {
value: Value,
expected: Vec<u8>,
arch: u8,
}
let tt = [
Case {
value: Value::Int8(1),
expected: 1i8.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Uint8(2),
expected: 2u8.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Int16(3),
expected: 3i16.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Uint16(4),
expected: 4i16.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Int32(5),
expected: 5i32.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Uint32(6),
expected: 6u32.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Int64(7),
expected: 7i64.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Uint64(8),
expected: 8u64.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::String("FIT".to_owned()),
expected: "FIT\x00".as_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::String("".to_owned()),
expected: "\x00".as_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Float32(9.0),
expected: 9.0f32.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::Float64(10.0),
expected: 10.0f64.to_le_bytes().to_vec(),
arch: 0,
},
Case {
value: Value::VecInt8(vec![1, 1]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(1u8.to_le_bytes());
v.extend(1i8.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecUint8(vec![2, 2]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(2u8.to_le_bytes());
v.extend(2u8.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecInt16(vec![3, 3]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(3i16.to_le_bytes());
v.extend(3i16.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecUint16(vec![4, 4]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(4u16.to_le_bytes());
v.extend(4u16.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecInt32(vec![5, 5]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(5i32.to_le_bytes());
v.extend(5i32.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecUint32(vec![6, 6]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(6u32.to_le_bytes());
v.extend(6u32.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecInt64(vec![7, 7]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(7i64.to_le_bytes());
v.extend(7i64.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecUint64(vec![8, 8]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(8u64.to_le_bytes());
v.extend(8u64.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecString(vec!["FIT".to_owned(), "SDK".to_owned()]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend("FIT\x00".as_bytes());
v.extend("SDK\x00".as_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecString(vec!["".to_owned(), "SDK\x00".to_owned()]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend("\x00".as_bytes());
v.extend("SDK\x00".as_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecFloat32(vec![9.0, 9.0]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(9.0f32.to_le_bytes());
v.extend(9.0f32.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::VecFloat64(vec![10.0, 10.0]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(10.0f64.to_le_bytes());
v.extend(10.0f64.to_le_bytes());
v
},
arch: 0,
},
Case {
value: Value::Int8(1),
expected: 1i8.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Uint8(2),
expected: 2u8.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Int16(3),
expected: 3i16.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Uint16(4),
expected: 4i16.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Int32(5),
expected: 5i32.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Uint32(6),
expected: 6u32.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Int64(7),
expected: 7i64.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Uint64(8),
expected: 8u64.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Float32(9.0),
expected: 9.0f32.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::Float64(10.0),
expected: 10.0f64.to_be_bytes().to_vec(),
arch: 1,
},
Case {
value: Value::VecInt8(vec![1, 1]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(1u8.to_be_bytes());
v.extend(1i8.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecUint8(vec![2, 2]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(2u8.to_be_bytes());
v.extend(2u8.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecInt16(vec![3, 3]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(3i16.to_be_bytes());
v.extend(3i16.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecUint16(vec![4, 4]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(4u16.to_be_bytes());
v.extend(4u16.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecInt32(vec![5, 5]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(5i32.to_be_bytes());
v.extend(5i32.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecUint32(vec![6, 6]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(6u32.to_be_bytes());
v.extend(6u32.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecInt64(vec![7, 7]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(7i64.to_be_bytes());
v.extend(7i64.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecUint64(vec![8, 8]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(8u64.to_be_bytes());
v.extend(8u64.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecFloat32(vec![9.0, 9.0]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(9.0f32.to_be_bytes());
v.extend(9.0f32.to_be_bytes());
v
},
arch: 1,
},
Case {
value: Value::VecFloat64(vec![10.0, 10.0]),
expected: {
let mut v: Vec<u8> = Vec::new();
v.extend(10.0f64.to_be_bytes());
v.extend(10.0f64.to_be_bytes());
v
},
arch: 1,
},
];
let mut buf: Vec<u8> = Vec::new();
for tc in tt {
buf.clear();
write_value_to_vec(&mut buf, &tc.value, tc.arch);
assert_eq!(tc.expected, buf, "input: {:?}", tc.value)
}
}
}