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 AccumulatedValue
- type Accumulator
- type Decoder
- func (d *Decoder) CheckIntegrity() (seq int, err error)
- func (d *Decoder) Decode() (fit *proto.FIT, err error)
- func (d *Decoder) DecodeWithContext(ctx context.Context) (fit *proto.FIT, err error)
- func (d *Decoder) Discard() error
- func (d *Decoder) Next() bool
- func (d *Decoder) PeekFileHeader() (*proto.FileHeader, error)
- func (d *Decoder) PeekFileId() (fileId *mesgdef.FileId, err 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 ( // Integrity errors ErrNotAFitFile = errorString("not a FIT file") ErrDataSizeZero = errorString("data size zero") ErrCRCChecksumMismatch = errorString("crc checksum mismatch") // Message-field related errors ErrMesgDefMissing = errorString("message definition missing") ErrFieldValueTypeMismatch = errorString("field value type mismatch") ErrInvalidBaseType = errorString("invalid basetype") )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AccumulatedValue ¶
func (*AccumulatedValue) Accumulate ¶
func (a *AccumulatedValue) Accumulate(value uint32, bits byte) uint32
type Accumulator ¶
type Accumulator struct {
AccumulatedValues []AccumulatedValue // use slice over map since len(values) is relatively small
}
func NewAccumulator ¶
func NewAccumulator() *Accumulator
func (*Accumulator) Accumulate ¶
func (*Accumulator) Collect ¶
func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, value uint32)
func (*Accumulator) Reset ¶ added in v0.4.2
func (a *Accumulator) Reset()
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 chained FIT files, use Next() to check if r hasn't reach EOF and next bytes are still a valid FIT sequences.
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 for optimal 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 that contains more than one FIT data, this decode method should be invoked multiple times. It is recommended to wrap it with the Next() method when you are uncertain if it's a chained FIT file.
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.
After this method is invoked, Decode picks up where this left then continue decoding next messages instead of starting from zero. This method is idempotent and can be invoked even after Decode has been invoked.
func (*Decoder) PeekFileId ¶
PeekFileId decodes only up to FileId message without decoding the whole reader. FileId message should be the first message of any FIT file, otherwise return an error.
After this method is invoked, Decode picks up where this left then continue decoding next messages instead of starting from zero. This method is idempotent and can be invoked even after Decode has been invoked.
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 (except messages need to be re-allocated).
type Factory ¶
type Factory interface { // CreateField create 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 interface {
// contains filtered or unexported methods
}
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).
- Encountering a Developer Field without prior Field Description Message.
func WithMesgDefListener ¶
func WithMesgDefListener(listeners ...MesgDefListener) Option
WithMesgDefListener adds listeners to the listener pool, where each listener is broadcasted every message definition. 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 is broadcasted every message. 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 )