decoder

package
v0.19.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 28, 2024 License: BSD-3-Clause Imports: 19 Imported by: 0

Documentation

Overview

Package decoder provides a handler to decode FIT files.

Decoder supports decoding chained FIT files for all FIT protocol versions.

Index

Constants

View Source
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

type AccumulatedValue struct {
	MesgNum      typedef.MesgNum
	DestFieldNum byte
	Last         uint32
	Value        uint32
}

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 (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, value uint32, bits byte) uint32

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

func New(r io.Reader, opts ...Option) *Decoder

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

func (d *Decoder) CheckIntegrity() (seq int, err error)

CheckIntegrity checks all FIT sequences of given reader are valid determined by these following checks:

  1. Has valid FileHeader's size and bytes 8–11 of the FileHeader is “.FIT”
  2. FileHeader's DataSize > 0
  3. 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

func (d *Decoder) Decode() (*proto.FIT, error)

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, however, the invocation must be wrapped with Next() method as follows:

for dec.Next() {
     fit, err := dec.Decode()
     if err != nil {
         return err
     }
}

func (*Decoder) DecodeWithContext

func (d *Decoder) DecodeWithContext(ctx context.Context) (*proto.FIT, error)

DecodeWithContext is similar to Decode but with respect to context propagation.

func (*Decoder) Discard added in v0.11.0

func (d *Decoder) Discard() error

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

func (d *Decoder) Next() bool

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

func (d *Decoder) PeekFileId() (*mesgdef.FileId, error)

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

func (d *Decoder) Reset(r io.Reader, opts ...Option)

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 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 WithFactory

func WithFactory(factory Factory) Option

WithFactory sets custom factory.

func WithIgnoreChecksum

func WithIgnoreChecksum() Option

WithIgnoreChecksum directs the Decoder to not checking data integrity (CRC Checksum).

func WithLogWriter added in v0.14.0

func WithLogWriter(w io.Writer) Option

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

func WithReadBufferSize(size int) Option

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
)

func (RawFlag) String added in v0.8.0

func (f RawFlag) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL