Skip to content

Implementation Details

Brice edited this page Jan 9, 2021 · 2 revisions

How the parser works

Features

This library is intended to read fit files and convert them into native swift structure (it can be used in objective c as well). It will breakdown the Fit types into 3 categories:

  • number as Double or Double with a unit as String,
  • enumeration, which will be converted to a String based on the fit type definition
  • dates as Date

Each message will be converted to a Swift FitMessage object that contains dictionaries keyed on the field name to the FitFieldValue

Each Fit field corresponding to a type in the Profiles.xlsx file from the sdk will be interpreted as per the definition. Fields for which the type is dependent on a reference field will be also properly interpreted. For example in the file_id message, product will become either garmin_product or tavern_product depending on the value in manufacturer.

Developer fields will be converted into fields with prefix developer_ added to the name of the field in the field definition to avoid collisions with regular fields.

Parsing Methodology

The library has two main mode of parsing FitFiles:

  • fast: This mode parses the files for predefined set of message and fields, it ignores any not known messages or fields. It is fastest because the compile knows in advanced all the fields definition
  • generic: This mode parse the files and interprets dynamically all the messages and fields as they come. For the known messages and fields in Profiles.xlsx the value will be converted as they would be in the fast method. Unknown message will be returned with the number for the message and the field.

Fast Parsing Type

The approach to parse in the fast mode is borrowed from the the Official Fit SDK c implementation.

First into a c-struct with predefined memory layout

The data from the file is mapped onto a c-structure such that each member will be contiguous in memory and read by dereferencing the corresponding field in c. In order to achieve that the fields are ordered by decreasing alignment, first the 4 bytes aligned fields (int32, uint32), then the 2 bytes (int16, uint16) then the 1 bytes (int8, uint8). The strings are included in the group with the largest alignment possible, for example if the size can be divided by 4 the string will go with the 4 bytes aligned, etc.

As the data is read from the file, the code will look up in a table for each message the order of the field and the size, compute the offset and put the data in the right place in the memory of the structure.

This first step is very fast as everything is copying onto memory with easily computed offsets.

Second into higher level swift types

Once a message from the file is mapped onto a c-struct memory, the conversion to a swift type is very simple as it just require a typecasting and a dereference of the member of the struct. Given this is all known at compile time the code is simple and the execution quite fast.

Fit types are interpreted by calling conversion function into the appropriate string.

Generic Parsing Type

Mapped into high level type buffer

The first step is similar to the fast parsing above, but instead of mapping the data of the file into predefined c-structure, it maps the data into 3 different buffers for each high level types of interest: dates, doubles and strings.

Because the generic parsing will not ignore any fields and does not know in advance what fields to expect for each message, the buffer will also contain the field number for processing dynamically in the later stage.

Conversion into higher level swift types

The 3 buffers will be processed dynamically and converted into the same structure as the fast type, but because the fields in the buffer are not known in advance the conversion requires a dynamic check via a function unlike the previous step that was simply a dereference of a memory address.