Documentation
¶
Overview ¶
Package decoder provides a handler to decode FIT files.
Decoder supports decoding chained FIT files for all FIT protocol versions.
Index ¶
- Constants
- type Accumulator
- type Decoder
- func (d *Decoder) CheckIntegrity() (seq int, err error)
- func (d *Decoder) Decode() (*proto.FIT, error)
- func (d *Decoder) DecodeWithContext(ctx context.Context) (*proto.FIT, error)
- func (d *Decoder) Discard() error
- func (d *Decoder) Next() bool
- func (d *Decoder) PeekFileHeader() (*proto.FileHeader, error)
- func (d *Decoder) PeekFileId() (*mesgdef.FileId, error)
- func (d *Decoder) Reset(r io.Reader, opts ...Option)
- type Factory
- type MesgDefListener
- type MesgListener
- type Option
- func WithBroadcastMesgCopy() Option
- func WithBroadcastOnly() Option
- func WithFactory(factory Factory) Option
- func WithIgnoreChecksum() Option
- func WithLogWriter(w io.Writer) Option
- func WithMesgDefListener(listeners ...MesgDefListener) Option
- func WithMesgListener(listeners ...MesgListener) Option
- func WithNoComponentExpansion() Option
- func WithReadBufferSize(size int) Option
- type RawDecoder
- type RawFlag
Constants ¶
const ( // ErrNotFITFile will be returned if the first byte of every FIT sequence does not match // with FIT FileHeader's Size specification (either 12 or 14), byte 8-12 are not ".FIT", // or byte 4-8 are all zero (FileHeader's DataSize == 0). ErrNotFITFile = errorString("not a FIT file") // ErrCRCChecksumMismatch will be returned if the CRC checksum does not match with // the CRC in the file, whether during FileHeader or messages integrity checks. ErrCRCChecksumMismatch = errorString("crc checksum mismatch") // ErrMesgDefMissing will be returned if message definition for the incoming message data is missing. ErrMesgDefMissing = errorString("message definition missing") // NOTE: Kept exported since it's used by RawDecoder )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Accumulator ¶
type Accumulator struct {
// contains filtered or unexported fields
}
Accumulator is value accumulator.
func (*Accumulator) Accumulate ¶
func (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, val uint32, bits byte) uint32
Accumulate calculates the accumulated value and update it accordingly. If targeted value does not exist, it will be collected and the original value will be returned.
func (*Accumulator) Collect ¶
func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, val uint32)
Collect collects value, it will either append the value when not exist or replace existing one.
func (*Accumulator) Reset ¶ added in v0.4.2
func (a *Accumulator) Reset()
Reset resets the accumulator. It retains the underlying storage for use by future use to reduce memory allocs.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is FIT file decoder. See New() for details.
func New ¶
New returns a FIT File Decoder to decode given r.
The FIT protocol allows for multiple FIT files to be chained together in a single FIT file. Each FIT file in the chain must be a properly formatted FIT file (header, data records, CRC).
To decode a chained FIT file containing multiple FIT data, invoke Decode() or DecodeWithContext() method multiple times. For convenience, we can wrap it with the Next() method as follows (optional):
for dec.Next() { fit, err := dec.Decode() }
Note: Decoder already implements efficient io.Reader buffering, so there's no need to wrap 'r' using *bufio.Reader; doing so will only reduce performance.
func (*Decoder) CheckIntegrity ¶ added in v0.7.0
CheckIntegrity checks all FIT sequences of given reader are valid determined by these following checks:
- Has valid FileHeader's size and bytes 8–11 of the FileHeader is “.FIT”
- FileHeader's DataSize > 0
- CRC checksum of messages should match with File's CRC value.
It returns the number of sequences completed and any error encountered. The number of sequences completed can help recovering valid FIT sequences in a chained FIT that contains invalid or corrupted data.
After invoking this method, the underlying reader should be reset afterward as the reader has been fully read. If the underlying reader implements io.Seeker, we can do reader.Seek(0, io.SeekStart).
func (*Decoder) Decode ¶
Decode method decodes `r` into FIT data. One invocation will produce one valid FIT data or an error if it occurs. To decode a chained FIT file containing multiple FIT data, invoke this method multiple times. For convenience, we can wrap it with the Next() method as follows (optional):
for dec.Next() { fit, err := dec.Decode() if err != nil { return err } }
func (*Decoder) DecodeWithContext ¶
DecodeWithContext is similar to Decode but with respect to context propagation.
func (*Decoder) Discard ¶ added in v0.11.0
Discard discards a single FIT file sequence and returns any error encountered. This method directs the Decoder to point to the byte sequence of the next valid FIT file sequence, discarding the current FIT file sequence.
Example: - A chained FIT file consist of Activity, Course, Workout and Settings. And we only want to decode Course.
for dec.Next() { fileId, err := dec.PeekFileId() if err != nil { return err } if fileId.Type != typedef.FileCourse { if err := dec.Discard(); err != nil { return err } continue } fit, err := dec.Decode() if err != nil { return err } }
func (*Decoder) Next ¶
Next checks whether next bytes are still a valid FIT File sequence. Return false when invalid or reach EOF.
func (*Decoder) PeekFileHeader ¶ added in v0.16.0
func (d *Decoder) PeekFileHeader() (*proto.FileHeader, error)
PeekFileHeader decodes only up to FileHeader (first 12-14 bytes) without decoding the whole reader.
If we choose to continue, Decode picks up where this left then continue decoding next messages instead of starting from zero.
func (*Decoder) PeekFileId ¶
PeekFileId decodes only up to FileId message without decoding the whole reader. The FileId is expected to be present as the first message; however, we don't validate this, as it's an edge case that occurs when a FIT file is poorly encoded.
If we choose to continue, Decode picks up where this left then continue decoding next messages instead of starting from zero.
func (*Decoder) Reset ¶ added in v0.9.0
Reset resets the Decoder to read its input from r, clear any error and reset previous options to default options so any options needs to be inputed again. It is similar to New() but it retains the underlying storage for use by future decode to reduce memory allocs.
type Factory ¶
type Factory interface { // CreateField creates new field based on defined messages in the factory. // If not found, it returns new field with "unknown" name. CreateField(mesgNum typedef.MesgNum, num byte) proto.Field }
Factory defines a contract that any Factory containing these method can be used by the Decoder.
type MesgDefListener ¶ added in v0.13.0
type MesgDefListener interface { // OnMesgDef receives message definition from Decoder. OnMesgDef(mesgDef proto.MessageDefinition) }
MesgDefListener is an interface for listening to message definition decoded event.
type MesgListener ¶ added in v0.13.0
type MesgListener interface { // OnMesg receives message from Decoder. The lifecycle of the mesg object is only guaranteed before // OnMesg returns. Any listener that wants to process the mesg concurrently should copy the mesg, // otherwise, the value of the mesg might be changed by the time it is being processed. Except, // the Decoder is directed to copy the mesg before passing the mesg to listener using Option. OnMesg(mesg proto.Message) }
MesgListener is an interface for listening to message decoded events.
type Option ¶
type Option func(o *options)
Option is Decoder's option.
func WithBroadcastMesgCopy ¶ added in v0.15.0
func WithBroadcastMesgCopy() Option
WithBroadcastMesgCopy directs the Decoder to copy the mesg before passing it to listeners (it was the default behavior on version <= v0.14.0).
func WithBroadcastOnly ¶
func WithBroadcastOnly() Option
WithBroadcastOnly directs the Decoder to only broadcast the messages without retaining them, reducing memory usage when it's not going to be used anyway. This option is intended to be used with WithMesgListener and When this option is specified, the Decode will return a FIT with empty messages.
func WithIgnoreChecksum ¶
func WithIgnoreChecksum() Option
WithIgnoreChecksum directs the Decoder to not checking data integrity (CRC Checksum).
func WithLogWriter ¶ added in v0.14.0
WithLogWriter specifies where the log messages will be written to. By default, the Decoder do not write any log if log writer is not specified. The Decoder will only write log messages when it encountered a bad encoded FIT file such as:
- Field Definition's Size (or Developer Field Definition's Size) is zero.
- Field Definition's Size (or Developer Field Definition's Size) is less than basetype's Size. e.g. Size 1 bytes but having basetype uint32 (4 bytes).
- Field Definition's Size is more than basetype's Size but field.Array is false.
- Encountering a Developer Field without prior DeveloperDataId or FieldDescription Message.
func WithMesgDefListener ¶
func WithMesgDefListener(listeners ...MesgDefListener) Option
WithMesgDefListener adds listeners to the listener pool, where each listener will receive every message definition as soon as it is decoded. The listeners will be appended not replaced. If users need to reset use Reset().
func WithMesgListener ¶
func WithMesgListener(listeners ...MesgListener) Option
WithMesgListener adds listeners to the listener pool, where each listener will receive every message as soon as it is decoded. The listeners will be appended not replaced. If users need to reset use Reset().
func WithNoComponentExpansion ¶
func WithNoComponentExpansion() Option
WithNoComponentExpansion directs the Decoder to not expand the components.
func WithReadBufferSize ¶ added in v0.15.0
WithReadBufferSize directs the Decoder to use this buffer size for reading from io.Reader instead of default 4096.
type RawDecoder ¶ added in v0.8.0
type RawDecoder struct { // [MesgDef: 6 + 255 * 3 = 771] < [Mesg: 1 + (255 * 255 * 2) = 130051]. Use bigger capacity. // // This is exported to allow the unused space to be utilized in a tight RAM, for instance, an embedded device. // Using Index >= len(b) is safe on each Decode's callback function call. BytesArray [1 + (255 * 255 * 2)]byte }
RawDecoder is a sequence of FIT bytes decoder. See NewRaw() for details.
func NewRaw ¶ added in v0.8.0
func NewRaw() *RawDecoder
NewRaw creates new RawDecoder which provides low-level building block to work with FIT bytes for the maximum performance gain. RawDecoder will split bytes by its corresponding RawFlag (FileHeader, MessageDefinition, MessageData and CRC) for scoping the operation.
However, this is still considered unsafe operation since we work with bytes directly and the responsibility for validation now placed on the user-space. The only thing that this validates is the reader should be a FIT (FileHeader: has valid Size and bytes 8-12 is ".FIT").
The idea is to allow us to use a minimal viable decoder for performance and memory-critical situations, where every computation or memory usage is constrained. RawDecoder itself is using constant memory < 131 KB and the Decode method has zero heap alloc (except errors) while it may use additional small stack memory. The implementation of the callback function is also expected to have minimal overhead.
For general purpose usage, use Decoder instead.
func (*RawDecoder) Decode ¶ added in v0.8.0
func (d *RawDecoder) Decode(r io.Reader, fn func(flag RawFlag, b []byte) error) (n int64, err error)
Decode decodes r reader into sequence of FIT bytes splitted by its corresponding RawFlag (FileHeader, MessageDefinition, MessageData and CRC) for every FIT sequences in the reader, until it reaches EOF. It returns the number of bytes read and any error encountered. When fn returns an error, Decode will immediately return the error.
For performance, the b is not copied and the underlying array's values will be replaced each fn call. If you need to work with b in its slice form later on, it should be copied.
Note: We encourage wrapping r into a buffered reader such as bufio.NewReader(r), decode process requires byte by byte reading and having frequent read on non-buffered reader might impact performance, especially if it involves syscall such as reading a file.
type RawFlag ¶ added in v0.8.0
type RawFlag byte
RawFlag is the kind of the incomming bytes, the size of the incomming bytes is vary but the the size is guaranteed by the corresponding RawFlag.
const ( // RawFlagFileHeader is guaranteed to have either 12 or 14 bytes (all in little-endian byte order): // Size + ProtocolVersion + ProfileVersion (2 bytes) + DataSize (4 bytes) + DataType (4 bytes) + // (only if Size is 14) CRC (2 bytes) RawFlagFileHeader RawFlag = iota // RawFlagMesgDef is guaranteed to have: // Header + Reserved + Architecture + MesgNum (2 bytes) + n FieldDefinitions + (n FieldDefinitions * 3) + // (only if Header & 0b00100000 == 0b00100000) n DeveloperFieldDefinitions + (n DeveloperFieldDefinitions * 3) RawFlagMesgDef // RawFlagMesgData is guaranteed to have: // Header + Fields' value represented by its Message Definition + (only if it has developer fields) // Developer Fields' value. RawFlagMesgData // RawFlagCRC is guaranteed to have: // 2 bytes (in little-endian byte order) as the checksum of the messages. RawFlagCRC )