From 8ad4e1f02886b3335d63d3aa5c03f417aa8952f3 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Tue, 10 Sep 2024 23:51:18 +0700 Subject: [PATCH 01/22] proto: remove CreateMessageDefinition, CreateMessageDefinitionTo and WithMessages --- decoder/decoder_test.go | 38 +++--- decoder/raw_test.go | 46 +++---- encoder/encoder.go | 38 +++++- internal/cmd/benchfit/go.mod | 4 +- internal/cmd/benchfit/go.sum | 8 +- internal/cmd/testgen/main.go | 4 +- proto/proto.go | 129 +++++++++--------- proto/proto_internal_test.go | 206 ++++++++++++++++++++++++++++ proto/proto_marshal.go | 46 ------- proto/proto_marshal_test.go | 92 +++++-------- proto/proto_test.go | 252 +++++++---------------------------- proto/value_marshal_test.go | 6 +- 12 files changed, 445 insertions(+), 424 deletions(-) diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index 12ff6b8..bcca0db 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -489,7 +489,7 @@ func TestCheckIntegrity(t *testing.T) { DataSize: 0, DataType: proto.DataTypeFIT, } - b, _ := h.MarshalBinary() + b, _ := h.MarshalAppend(nil) crc := crc16.New(nil) crc.Write(b[:12]) binary.LittleEndian.PutUint16(b[12:14], crc.Sum16()) @@ -568,7 +568,7 @@ func TestCheckIntegrity(t *testing.T) { // Chained FIT File but with next sequence header is b := append(b[:0:0], b...) h := headerForTest() - nextb, _ := h.MarshalBinary() + nextb, _ := h.MarshalAppend(nil) nextb[0] = 100 // alter FileHeader's Size b = append(b, nextb...) return bytes.NewReader(b) @@ -653,19 +653,19 @@ func createFitForTest() (proto.FIT, []byte) { } bytesbuffer := new(bytes.Buffer) - b, _ := fit.FileHeader.MarshalBinary() + b, _ := fit.FileHeader.MarshalAppend(nil) bytesbuffer.Write(b) // Marshal and calculate data size and crc checksum crc16checker := crc16.New(nil) for i := range fit.Messages { mesg := fit.Messages[i] - mesgDef := proto.CreateMessageDefinition(&mesg) - b, _ := mesgDef.MarshalBinary() + mesgDef, _ := proto.NewMessageDefinition(&mesg) + b, _ := mesgDef.MarshalAppend(nil) bytesbuffer.Write(b) crc16checker.Write(b) - b, err := mesg.MarshalBinary() + b, err := mesg.MarshalAppend(nil) if err != nil { panic(err) } @@ -851,7 +851,7 @@ func TestNext(t *testing.T) { // New header of the next chained FIT sequences. header := headerForTest() - b, _ := header.MarshalBinary() + b, _ := header.MarshalAppend(nil) buf = append(buf, b...) r := func() io.Reader { @@ -1274,7 +1274,7 @@ func TestDecodeMessageDefinition(t *testing.T) { r io.Reader opts []Option header byte - mesgDef proto.MessageDefinition + mesgDef *proto.MessageDefinition err error }{ { @@ -1297,8 +1297,11 @@ func TestDecodeMessageDefinition(t *testing.T) { opts: []Option{ WithMesgDefListener(fnMesgDefListener(func(mesgDef proto.MessageDefinition) {})), }, - header: proto.MesgDefinitionMask, - mesgDef: proto.CreateMessageDefinition(&fit.Messages[0]), // file_id + header: proto.MesgDefinitionMask, + mesgDef: func() *proto.MessageDefinition { + mesgDef, _ := proto.NewMessageDefinition(&fit.Messages[0]) // file_id + return mesgDef + }(), }, { name: "decode read return io.EOF when retrieving init data", @@ -1406,7 +1409,7 @@ func TestDecodeMessageDefinition(t *testing.T) { if err != nil { return } - mesgDef := *dec.localMessageDefinitions[proto.MesgDefinitionMask&proto.LocalMesgNumMask] + mesgDef := dec.localMessageDefinitions[proto.MesgDefinitionMask&proto.LocalMesgNumMask] if len(mesgDef.DeveloperFieldDefinitions) == 0 { mesgDef.DeveloperFieldDefinitions = nil } @@ -1709,7 +1712,7 @@ func TestDecodeFields(t *testing.T) { }, }, } - mesgb, _ := mesg.MarshalBinary() + mesgb, _ := mesg.MarshalAppend(nil) mesgb = mesgb[1:] // splice mesg header cur := 0 return fnReader(func(b []byte) (n int, err error) { @@ -1752,7 +1755,7 @@ func TestDecodeFields(t *testing.T) { }, }, } - mesgb, _ := mesg.MarshalBinary() + mesgb, _ := mesg.MarshalAppend(nil) mesgb = mesgb[1:] // splice mesg header cur := 0 return fnReader(func(b []byte) (n int, err error) { @@ -2645,8 +2648,11 @@ func BenchmarkDecodeMessageData(b *testing.B) { factory.CreateField(mesgnum.Record, fieldnum.RecordTemperature).WithValue(int8(32)), }, } - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgb, err := mesg.MarshalBinary() + mesgDef, err := proto.NewMessageDefinition(&mesg) + if err != nil { + b.Fatal(err) + } + mesgb, err := mesg.MarshalAppend(nil) if err != nil { b.Fatalf("marshal binary: %v", err) } @@ -2662,7 +2668,7 @@ func BenchmarkDecodeMessageData(b *testing.B) { }) dec := New(r, WithIgnoreChecksum(), WithNoComponentExpansion(), WithBroadcastOnly()) - dec.localMessageDefinitions[0] = &mesgDef + dec.localMessageDefinitions[0] = mesgDef b.StartTimer() for i := 0; i < b.N; i++ { diff --git a/decoder/raw_test.go b/decoder/raw_test.go index 744c261..4ea6931 100644 --- a/decoder/raw_test.go +++ b/decoder/raw_test.go @@ -201,9 +201,9 @@ func TestRawDecoderDecode(t *testing.T) { factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) cur := 0 @@ -231,9 +231,9 @@ func TestRawDecoderDecode(t *testing.T) { }, ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) cur := 0 @@ -261,9 +261,9 @@ func TestRawDecoderDecode(t *testing.T) { }, ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) cur := 0 @@ -291,9 +291,9 @@ func TestRawDecoderDecode(t *testing.T) { }, ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) cur := 0 @@ -320,8 +320,8 @@ func TestRawDecoderDecode(t *testing.T) { factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgb, _ := mesg.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgb, _ := mesg.MarshalAppend(nil) buf = append(buf, mesgb...) cur := 0 @@ -343,9 +343,9 @@ func TestRawDecoderDecode(t *testing.T) { factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) buf = append(buf, mesgDefb[0]&proto.LocalMesgNumMask) @@ -388,11 +388,11 @@ func TestRawDecoderDecode(t *testing.T) { factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), ) h := headerForTest() - buf, _ := h.MarshalBinary() - mesgDef := proto.CreateMessageDefinition(&mesg) - mesgDefb, _ := mesgDef.MarshalBinary() + buf, _ := h.MarshalAppend(nil) + mesgDef, _ := proto.NewMessageDefinition(&mesg) + mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) - mesgb, _ := mesg.MarshalBinary() + mesgb, _ := mesg.MarshalAppend(nil) buf = append(buf, mesgb...) binary.LittleEndian.PutUint32(buf[4:8], uint32(len(buf)-14)) @@ -469,13 +469,13 @@ func BenchmarkRawDecoderDecode(b *testing.B) { // But since it's big, it's should be good to benchmark. f, err := os.Open("../testdata/big_activity.fit") if err != nil { - panic(err) + b.Fatal(err) } defer f.Close() all, err := io.ReadAll(f) if err != nil { - panic(err) + b.Fatal(err) } buf := bytes.NewBuffer(all) diff --git a/encoder/encoder.go b/encoder/encoder.go index 59acb0a..42b2e94 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -463,12 +463,12 @@ func (e *Encoder) encodeMessage(mesg *proto.Message) (err error) { } } - proto.CreateMessageDefinitionTo(&e.mesgDef, mesg) - if err := e.protocolValidator.ValidateMessageDefinition(&e.mesgDef); err != nil { + mesgDef := e.createMessageDefinition(mesg) + if err := e.protocolValidator.ValidateMessageDefinition(mesgDef); err != nil { return err } - b, _ := e.mesgDef.MarshalAppend(e.buf[:0]) + b, _ := mesgDef.MarshalAppend(e.buf[:0]) localMesgNum, isNewMesgDef := e.localMesgNumLRU.Put(b) // This might alloc memory since we need to copy the item. if e.options.headerOption == headerOptionNormal { b[0] = (b[0] &^ proto.LocalMesgNumMask) | localMesgNum // Update the message definition header. @@ -530,6 +530,38 @@ func (e *Encoder) compressTimestampIntoHeader(mesg *proto.Message) { mesg.RemoveFieldByNum(proto.FieldNumTimestamp) } +func (e *Encoder) createMessageDefinition(mesg *proto.Message) *proto.MessageDefinition { + e.mesgDef.Header = proto.MesgDefinitionMask + e.mesgDef.Reserved = mesg.Reserved + e.mesgDef.Architecture = mesg.Architecture + e.mesgDef.MesgNum = mesg.Num + + e.mesgDef.FieldDefinitions = e.mesgDef.FieldDefinitions[:0] + for i := range mesg.Fields { + e.mesgDef.FieldDefinitions = append(e.mesgDef.FieldDefinitions, proto.FieldDefinition{ + Num: mesg.Fields[i].Num, + Size: byte(proto.Sizeof(mesg.Fields[i].Value)), + BaseType: mesg.Fields[i].BaseType, + }) + } + + if len(mesg.DeveloperFields) == 0 { + return &e.mesgDef + } + + e.mesgDef.Header |= proto.DevDataMask + e.mesgDef.DeveloperFieldDefinitions = e.mesgDef.DeveloperFieldDefinitions[:0] + for i := range mesg.DeveloperFields { + e.mesgDef.DeveloperFieldDefinitions = append(e.mesgDef.DeveloperFieldDefinitions, proto.DeveloperFieldDefinition{ + Num: mesg.DeveloperFields[i].Num, + Size: byte(proto.Sizeof(mesg.DeveloperFields[i].Value)), + DeveloperDataIndex: mesg.DeveloperFields[i].DeveloperDataIndex, + }) + } + + return &e.mesgDef +} + func (e *Encoder) encodeCRC() error { b := e.buf[:2] binary.LittleEndian.PutUint16(b, e.crc16.Sum16()) diff --git a/internal/cmd/benchfit/go.mod b/internal/cmd/benchfit/go.mod index 8bfc7de..d1da9d2 100644 --- a/internal/cmd/benchfit/go.mod +++ b/internal/cmd/benchfit/go.mod @@ -17,9 +17,9 @@ require ( golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.7.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect honnef.co/go/tools v0.4.2 // indirect mvdan.cc/gofumpt v0.4.0 // indirect diff --git a/internal/cmd/benchfit/go.sum b/internal/cmd/benchfit/go.sum index 9dfc5f1..0ea21db 100644 --- a/internal/cmd/benchfit/go.sum +++ b/internal/cmd/benchfit/go.sum @@ -69,8 +69,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -97,8 +97,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= diff --git a/internal/cmd/testgen/main.go b/internal/cmd/testgen/main.go index 6ba4b28..d87c646 100644 --- a/internal/cmd/testgen/main.go +++ b/internal/cmd/testgen/main.go @@ -61,7 +61,7 @@ func createValidFitOnlyContainFileId(ctx context.Context) error { type Uint16 uint16 now := datetime.ToTime(uint32(1062766519)) - fit := new(proto.FIT).WithMessages( + fit := &proto.FIT{Messages: []proto.Message{ factory.CreateMesgOnly(typedef.MesgNumFileId).WithFields( factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("something ss"), @@ -80,7 +80,7 @@ func createValidFitOnlyContainFileId(ctx context.Context) error { // fieldnum.RecordAltitude: uint16((150 + 500) * 5), // input scaled value // fieldnum.RecordAltitude: "something", // input scaled value }), - ) + }} enc := encoder.New(f) if err := enc.EncodeWithContext(ctx, fit); err != nil { diff --git a/proto/proto.go b/proto/proto.go index b48e1e4..bcb2eea 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -5,6 +5,8 @@ package proto import ( + "fmt" + "github.com/muktihari/fit/profile" "github.com/muktihari/fit/profile/basetype" "github.com/muktihari/fit/profile/typedef" @@ -44,6 +46,33 @@ const ( // header is 1 byte -> 0bxxxxxxxx FieldNumTimestamp = 253 ) +// FIT represents a structure for FIT Files. +type FIT struct { + FileHeader FileHeader // File Header contains either 12 or 14 bytes + Messages []Message // Messages. + CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the messages. +} + +// FileHeader is a FIT's FileHeader with either 12 bytes size without CRC or a 14 bytes size with CRC, while 14 bytes size is the preferred size. +type FileHeader struct { + Size byte // File header size either 12 (legacy) or 14. + ProtocolVersion byte // The FIT Protocol version which is being used to encode the FIT file. + ProfileVersion uint16 // The FIT Profile Version (associated with data defined in Global FIT Profile). + DataSize uint32 // The size of the messages in bytes (this field will be automatically updated by the encoder) + DataType string // ".FIT" (a string constant) + CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the file header. (this field will be automatically updated by the encoder) +} + +// MessageDefinition is the definition of the upcoming data messages. +type MessageDefinition struct { + Header byte // The message definition header with mask 0b01000000. + Reserved byte // Currently undetermined; the default value is 0. + Architecture byte // The Byte Order to be used to decode the values of both this message definition and the upcoming message. (0: Little-Endian, 1: Big-Endian) + MesgNum typedef.MesgNum // Global Message Number defined by factory (retrieved from Profile.xslx). (endianness of this 2 Byte value is defined in the Architecture byte) + FieldDefinitions []FieldDefinition // List of the field definition + DeveloperFieldDefinitions []DeveloperFieldDefinition // List of the developer field definition (only if Developer Data Flag is set in Header) +} + // LocalMesgNum extracts LocalMesgNum from message header. func LocalMesgNum(header byte) byte { if (header & MesgCompressedHeaderMask) == MesgCompressedHeaderMask { @@ -52,86 +81,62 @@ func LocalMesgNum(header byte) byte { return header & LocalMesgNumMask } -// CreateMessageDefinition creates new MessageDefinition base on given Message. -// It will panic if mesg is nil. And mesg must be validated first, for instance -// if field.Value's size is more than 255 bytes, overflow will occurs. -func CreateMessageDefinition(mesg *Message) (mesgDef MessageDefinition) { - CreateMessageDefinitionTo(&mesgDef, mesg) - return -} +const ( + errNilMesg = errorString("mesg is nil") + errValueSizeExceed255 = errorString("value's size exceed 255") +) + +// NewMessageDefinition returns a new MessageDefinition based on the given Message or an error if one occurs. +// +// This serves as a testing helper and is for documentation purposes only. +func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { + if mesg == nil { + return nil, errNilMesg + } + + const maxValueSize = 255 -// CreateMessageDefinitionTo create MessageDefinition base on given Message and put it at target object to avoid allocation. -// It will panic if either target or mesg is nil. And mesg must be validated first, for instance -// if field.Value's size is more than 255 bytes, overflow will occurs. -func CreateMessageDefinitionTo(target *MessageDefinition, mesg *Message) { - target.Header = MesgDefinitionMask - target.Reserved = mesg.Reserved - target.Architecture = mesg.Architecture - target.MesgNum = mesg.Num - - target.FieldDefinitions = target.FieldDefinitions[:0] - if cap(target.FieldDefinitions) < len(mesg.Fields) { - target.FieldDefinitions = make([]FieldDefinition, 0, len(mesg.Fields)) + mesgDef := &MessageDefinition{ + Header: MesgDefinitionMask, + Reserved: mesg.Reserved, + Architecture: mesg.Architecture, + MesgNum: mesg.Num, + FieldDefinitions: make([]FieldDefinition, 0, len(mesg.Fields)), } for i := range mesg.Fields { - target.FieldDefinitions = append(target.FieldDefinitions, FieldDefinition{ + size := Sizeof(mesg.Fields[i].Value) + if size > maxValueSize { + return nil, fmt.Errorf("Fields[%d].Value's size should be < %d: %w", + i, maxValueSize, errValueSizeExceed255) + } + mesgDef.FieldDefinitions = append(mesgDef.FieldDefinitions, FieldDefinition{ Num: mesg.Fields[i].Num, - Size: byte(Sizeof(mesg.Fields[i].Value)), + Size: byte(size), BaseType: mesg.Fields[i].BaseType, }) } if len(mesg.DeveloperFields) == 0 { - return + return mesgDef, nil } - target.Header |= DevDataMask - - target.DeveloperFieldDefinitions = target.DeveloperFieldDefinitions[:0] - if cap(target.DeveloperFieldDefinitions) < len(mesg.DeveloperFields) { - target.DeveloperFieldDefinitions = make([]DeveloperFieldDefinition, 0, len(mesg.DeveloperFields)) - } + mesgDef.DeveloperFieldDefinitions = make([]DeveloperFieldDefinition, 0, len(mesg.DeveloperFields)) + mesgDef.Header |= DevDataMask for i := range mesg.DeveloperFields { - target.DeveloperFieldDefinitions = append(target.DeveloperFieldDefinitions, DeveloperFieldDefinition{ + size := Sizeof(mesg.DeveloperFields[i].Value) + if size > maxValueSize { + return nil, fmt.Errorf("Fields[%d].Value's size should be < %d: %w", + i, maxValueSize, errValueSizeExceed255) + } + mesgDef.DeveloperFieldDefinitions = append(mesgDef.DeveloperFieldDefinitions, DeveloperFieldDefinition{ Num: mesg.DeveloperFields[i].Num, - Size: byte(Sizeof(mesg.DeveloperFields[i].Value)), + Size: byte(size), DeveloperDataIndex: mesg.DeveloperFields[i].DeveloperDataIndex, }) } -} -// FIT represents a structure for FIT Files. -type FIT struct { - FileHeader FileHeader // File Header contains either 12 or 14 bytes - Messages []Message // Messages. - CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the messages. -} - -// WithMessages set Messages and return the pointer to the FIT. -func (f *FIT) WithMessages(messages ...Message) *FIT { - f.Messages = messages - return f -} - -// FileHeader is a FIT's FileHeader with either 12 bytes size without CRC or a 14 bytes size with CRC, while 14 bytes size is the preferred size. -type FileHeader struct { - Size byte // File header size either 12 (legacy) or 14. - ProtocolVersion byte // The FIT Protocol version which is being used to encode the FIT file. - ProfileVersion uint16 // The FIT Profile Version (associated with data defined in Global FIT Profile). - DataSize uint32 // The size of the messages in bytes (this field will be automatically updated by the encoder) - DataType string // ".FIT" (a string constant) - CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the file header. (this field will be automatically updated by the encoder) -} - -// MessageDefinition is the definition of the upcoming data messages. -type MessageDefinition struct { - Header byte // The message definition header with mask 0b01000000. - Reserved byte // Currently undetermined; the default value is 0. - Architecture byte // The Byte Order to be used to decode the values of both this message definition and the upcoming message. (0: Little-Endian, 1: Big-Endian) - MesgNum typedef.MesgNum // Global Message Number defined by factory (retrieved from Profile.xslx). (endianness of this 2 Byte value is defined in the Architecture byte) - FieldDefinitions []FieldDefinition // List of the field definition - DeveloperFieldDefinitions []DeveloperFieldDefinition // List of the developer field definition (only if Developer Data Flag is set in Header) + return mesgDef, nil } // Clone clones MessageDefinition diff --git a/proto/proto_internal_test.go b/proto/proto_internal_test.go index ab1db00..c3edf0f 100644 --- a/proto/proto_internal_test.go +++ b/proto/proto_internal_test.go @@ -5,10 +5,216 @@ package proto import ( + "errors" "fmt" "testing" + + "github.com/google/go-cmp/cmp" + "github.com/muktihari/fit/profile/basetype" + "github.com/muktihari/fit/profile/typedef" + "github.com/muktihari/fit/profile/untyped/fieldnum" + "github.com/muktihari/fit/profile/untyped/mesgnum" ) +func TestNewMessageDefinition(t *testing.T) { + tt := []struct { + name string + mesg *Message + mesgDef *MessageDefinition + err error + }{ + {name: "nil mesg", err: errNilMesg}, + { + name: "field value exceed max 255", + mesg: &Message{Num: mesgnum.FileId, Fields: []Field{ + { + FieldBase: &FieldBase{Num: fieldnum.FileIdProductName, BaseType: basetype.String}, + Value: String(string(make([]byte, 256))), + }, + }}, + err: errValueSizeExceed255, + }, + { + name: "developerField value exceed max 255", + mesg: &Message{Num: mesgnum.FileId, Fields: []Field{}, + DeveloperFields: []DeveloperField{ + {Value: String(string(make([]byte, 256)))}, + }, + }, + err: errValueSizeExceed255, + }, + { + name: "fields only with non-array values", + mesg: &Message{Num: mesgnum.FileId, Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: Uint8(typedef.FileActivity.Byte())}, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask, + MesgNum: mesgnum.FileId, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.FileIdType, + Size: 1, + BaseType: basetype.Enum, + }, + }, + }, + }, + { + name: "fields only with mesg architecture big-endian", + mesg: func() *Message { + mesg := &Message{Num: mesgnum.FileId, Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: Uint8(typedef.FileActivity.Byte())}, + }} + mesg.Architecture = 1 // big-endian + return mesg + }(), + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask, + Architecture: 1, // big-endian + MesgNum: mesgnum.FileId, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.FileIdType, + Size: 1, + BaseType: basetype.Enum, + }, + }, + }, + }, + { + name: "fields only with string value", + mesg: &Message{Num: mesgnum.FileId, Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.FileIdProductName, BaseType: basetype.String}, Value: String("FIT SDK Go")}, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask, + MesgNum: mesgnum.FileId, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.FileIdProductName, + Size: 1 * 11, // len("FIT SDK Go") == 10 + '0x00' + BaseType: basetype.String, + }, + }, + }, + }, + { + name: "fields only with array of byte", + mesg: &Message{Num: mesgnum.UserProfile, Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: SliceUint8([]byte{2, 9})}, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + }, + }, + + { + name: "developer fields", + mesg: &Message{Num: mesgnum.UserProfile, + Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []DeveloperField{ + {Num: 0, DeveloperDataIndex: 0, Value: Uint8(1)}, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask | DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []DeveloperFieldDefinition{ + { + Num: 0, Size: 1, DeveloperDataIndex: 0, + }, + }, + }, + }, + { + name: "developer fields with string value \"FIT SDK Go\", size should be 11", + mesg: &Message{Num: mesgnum.UserProfile, + Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []DeveloperField{ + { + Num: 0, DeveloperDataIndex: 0, Value: String("FIT SDK Go"), + }, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask | DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []DeveloperFieldDefinition{ + { + Num: 0, Size: 11, DeveloperDataIndex: 0, + }, + }, + }, + }, + { + name: "developer fields with value []uint16{1,2,3}, size should be 3*2 = 6", + mesg: &Message{Num: mesgnum.UserProfile, + Fields: []Field{ + {FieldBase: &FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []DeveloperField{ + {Num: 0, DeveloperDataIndex: 0, Value: SliceUint16([]uint16{1, 2, 3})}, + }}, + mesgDef: &MessageDefinition{ + Header: MesgDefinitionMask | DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []DeveloperFieldDefinition{ + { + Num: 0, Size: 6, DeveloperDataIndex: 0, + }, + }, + }, + }, + } + + for i, tc := range tt { + t.Run(fmt.Sprintf("[%d] %s", i, tc.name), func(t *testing.T) { + mesgDef, err := NewMessageDefinition(tc.mesg) + if !errors.Is(err, tc.err) { + t.Fatalf("expected error: %v, got: %v", tc.err, err) + } + if err != nil { + return + } + if diff := cmp.Diff(mesgDef, tc.mesgDef); diff != "" { + t.Fatal(diff) + } + }) + } +} + func TestIsValueEqualTo(t *testing.T) { tt := []struct { field Field diff --git a/proto/proto_marshal.go b/proto/proto_marshal.go index cf70eda..7e3d1ee 100644 --- a/proto/proto_marshal.go +++ b/proto/proto_marshal.go @@ -5,10 +5,8 @@ package proto import ( - "encoding" "encoding/binary" "fmt" - "sync" ) const littleEndian = 0 @@ -21,25 +19,6 @@ const MaxBytesPerMessage = 1 + (255*255)*2 // Header + Reserved + Architecture + MesgNum (2 bytes) + n Fields + (Max n Fields * 3) + n DevFields + (Max n DevFields * 3). const MaxBytesPerMessageDefinition = 5 + 1 + (255 * 3) + 1 + (255 * 3) -var pool = sync.Pool{New: func() any { return new([MaxBytesPerMessage]byte) }} - -var ( - _ encoding.BinaryMarshaler = (*FileHeader)(nil) - _ encoding.BinaryMarshaler = (*MessageDefinition)(nil) - _ encoding.BinaryMarshaler = (*Message)(nil) -) - -// MarshalBinary returns the FIT format encoding of FileHeader and nil error. -func (h FileHeader) MarshalBinary() ([]byte, error) { - arr := pool.Get().(*[MaxBytesPerMessage]byte) - defer pool.Put(arr) - b := arr[:0] - - b, _ = h.MarshalAppend(b) - - return append([]byte{}, b...), nil -} - // MarshalAppend appends the FIT format encoding of FileHeader to b, returning the result. func (h FileHeader) MarshalAppend(b []byte) ([]byte, error) { b = append(b, h.Size, h.ProtocolVersion) @@ -52,17 +31,6 @@ func (h FileHeader) MarshalAppend(b []byte) ([]byte, error) { return b, nil } -// MarshalBinary returns the FIT format encoding of MessageDefinition and nil error. -func (m MessageDefinition) MarshalBinary() ([]byte, error) { - arr := pool.Get().(*[MaxBytesPerMessage]byte) - defer pool.Put(arr) - b := arr[:0] - - b, _ = m.MarshalAppend(b) - - return append([]byte{}, b...), nil -} - // MarshalAppend appends the FIT format encoding of MessageDefinition to b, returning the result. func (m MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { b = append(b, m.Header) @@ -98,20 +66,6 @@ func (m MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { return b, nil } -// MarshalBinary returns the FIT format encoding of Message and any error encountered during marshal. -func (m Message) MarshalBinary() ([]byte, error) { - arr := pool.Get().(*[MaxBytesPerMessage]byte) - defer pool.Put(arr) - b := arr[:0] - - b, err := m.MarshalAppend(b) - if err != nil { - return nil, err - } - - return append([]byte{}, b...), nil -} - // MarshalAppend appends the FIT format encoding of Message to b, returning the result. func (m Message) MarshalAppend(b []byte) ([]byte, error) { b = append(b, m.Header) diff --git a/proto/proto_marshal_test.go b/proto/proto_marshal_test.go index 3fc7dc5..a8a7792 100644 --- a/proto/proto_marshal_test.go +++ b/proto/proto_marshal_test.go @@ -64,7 +64,7 @@ func TestHeaderMarshaler(t *testing.T) { } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - b, err := tc.fileHeader.MarshalBinary() + b, err := tc.fileHeader.MarshalAppend(nil) if !errors.Is(err, tc.err) { t.Fatalf("expected err: %v, got: %v", tc.err, err) } @@ -148,7 +148,7 @@ func TestMessageDefinitionMarshaler(t *testing.T) { for i, tc := range tt { t.Run(fmt.Sprintf("[%d] %s", i, tc.name), func(t *testing.T) { - b, _ := tc.mesgdef.MarshalBinary() + b, _ := tc.mesgdef.MarshalAppend(nil) if diff := cmp.Diff(b, tc.b); diff != "" { t.Fatal(diff) } @@ -243,7 +243,7 @@ func TestMessageMarshaler(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - b, err := tc.mesg.MarshalBinary() + b, err := tc.mesg.MarshalAppend(nil) if !errors.Is(err, tc.err) { t.Fatalf("expected err: %v, got: %v", tc.err, err) } @@ -254,23 +254,6 @@ func TestMessageMarshaler(t *testing.T) { } } -func BenchmarkHeaderMarshalBinary(b *testing.B) { - b.StopTimer() - header := proto.FileHeader{ - Size: 14, - ProtocolVersion: 32, - ProfileVersion: 2132, - DataSize: 642262, - DataType: ".FIT", - CRC: 12856, - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, _ = header.MarshalBinary() - } -} - func BenchmarkHeaderMarshalAppend(b *testing.B) { b.StopTimer() header := proto.FileHeader{ @@ -281,29 +264,29 @@ func BenchmarkHeaderMarshalAppend(b *testing.B) { DataType: ".FIT", CRC: 12856, } - arr := [proto.MaxBytesPerMessageDefinition]byte{} b.StartTimer() for i := 0; i < b.N; i++ { - _, _ = header.MarshalAppend(arr[:0]) - } -} - -func BenchmarkMessageDefinitionMarshalBinary(b *testing.B) { - b.StopTimer() - mesg := factory.CreateMesg(mesgnum.Record) - mesgDef := proto.CreateMessageDefinition(&mesg) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, _ = mesgDef.MarshalBinary() + _, _ = header.MarshalAppend(make([]byte, 0, 14)) } } func BenchmarkMessageDefinitionMarshalAppend(b *testing.B) { b.StopTimer() - mesg := factory.CreateMesg(mesgnum.Record) - mesgDef := proto.CreateMessageDefinition(&mesg) + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(uint32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLat).WithValue(int32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLong).WithValue(int32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(70)), + factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16(300*5 - 500)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPower).WithValue(uint16(300)), + }} + mesgDef, err := proto.NewMessageDefinition(&mesg) + if err != nil { + b.Fatal(err) + } arr := [proto.MaxBytesPerMessageDefinition]byte{} b.StartTimer() @@ -312,31 +295,30 @@ func BenchmarkMessageDefinitionMarshalAppend(b *testing.B) { } } -func BenchmarkMessageMarshalBinary(b *testing.B) { +func BenchmarkMessageMarshalAppend(b *testing.B) { b.StopTimer() - mesg := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordPositionLat: proto.Int32(1000), - fieldnum.RecordPositionLong: proto.Int32(1000), - fieldnum.RecordSpeed: proto.Uint16(1000), - }) - b.StartTimer() + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(uint32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLat).WithValue(int32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLong).WithValue(int32(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(70)), + factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16(300*5 - 500)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPower).WithValue(uint16(300)), + }} - for i := 0; i < b.N; i++ { - _, _ = mesg.MarshalBinary() + var size = 1 + for i := range mesg.Fields { + size += proto.Sizeof(mesg.Fields[i].Value) } -} - -func BenchmarkMessageMarshalAppend(b *testing.B) { - b.StopTimer() - mesg := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordPositionLat: proto.Int32(1000), - fieldnum.RecordPositionLong: proto.Int32(1000), - fieldnum.RecordSpeed: proto.Uint16(1000), - }) - arr := [proto.MaxBytesPerMessage]byte{} + buf := make([]byte, size) b.StartTimer() for i := 0; i < b.N; i++ { - _, _ = mesg.MarshalAppend(arr[:0]) + _, err := mesg.MarshalAppend(buf[:0]) + if err != nil { + b.Fatal(err) + } } } diff --git a/proto/proto_test.go b/proto/proto_test.go index 7c77826..c9ddf69 100644 --- a/proto/proto_test.go +++ b/proto/proto_test.go @@ -50,23 +50,23 @@ func TestFitWithMessages(t *testing.T) { { name: "withMessages", messages: []proto.Message{ - factory.CreateMesg(mesgnum.Record).WithFields( + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed), factory.CreateField(mesgnum.Record, fieldnum.RecordCadence), factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate), - ), - factory.CreateMesg(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed), factory.CreateField(mesgnum.Record, fieldnum.RecordCadence), factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate), - ), + }}, }, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - fit := new(proto.FIT).WithMessages(tc.messages...) + fit := &proto.FIT{Messages: tc.messages} if diff := cmp.Diff(fit.Messages, tc.messages, cmp.Transformer("Value", func(v proto.Value) any { return v.Any() @@ -138,17 +138,17 @@ func TestMessageFieldByNum(t *testing.T) { }{ { name: "FieldByNum found", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ sharedField, - ), + }}, fieldNum: fieldnum.EventEventType, field: &sharedField, }, { name: "FieldByNum not found", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ sharedField, - ), + }}, fieldNum: fieldnum.EventData, field: nil, }, @@ -177,17 +177,17 @@ func TestMessageFieldValueByNum(t *testing.T) { }{ { name: "FieldValueByNum found", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart), - ), + }}, fieldNum: fieldnum.EventEventType, value: proto.Uint8(uint8(typedef.EventTypeStart)), }, { name: "FieldValueByNum not found", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart), - ), + }}, fieldNum: fieldnum.EventData, value: proto.Value{}, }, @@ -213,18 +213,18 @@ func TestMessageRemoveFieldByNum(t *testing.T) { }{ { name: "remove existing field", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart), - ), + }}, fieldNum: fieldnum.EventEventType, field: nil, size: 0, }, { name: "remove field that is not exist", - mesg: proto.Message{Num: mesgnum.Event}.WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart), - ), + }}, fieldNum: fieldnum.EventData, field: nil, size: 1, @@ -246,17 +246,18 @@ func TestMessageRemoveFieldByNum(t *testing.T) { } func TestMessageClone(t *testing.T) { - mesg := factory.CreateMesg(mesgnum.Session).WithFieldValues(map[byte]any{ - fieldnum.SessionAvgAltitude: proto.Uint16(1000), - fieldnum.SessionAvgSpeed: proto.Uint16(1000), - }).WithDeveloperFields( - proto.DeveloperField{ - Num: 0, - DeveloperDataIndex: 0, - Value: proto.Uint8(1), - }, - proto.DeveloperField{}, - ) + mesg := proto.Message{Num: mesgnum.Session, Fields: []proto.Field{ + factory.CreateField(mesgnum.Session, fieldnum.SessionAvgAltitude).WithValue(uint16(1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(1000)), + }, + DeveloperFields: []proto.DeveloperField{ + { + Num: 0, + DeveloperDataIndex: 0, + Value: proto.Uint8(1), + }, + {}, + }} cloned := mesg.Clone() cloned.Fields[0].Num = 100 @@ -281,26 +282,26 @@ func TestFieldSubFieldSubtitution(t *testing.T) { }{ { name: "SubFieldSubtitution ok, main field can be interpreted.", - mesg: factory.CreateMesg(mesgnum.Event).WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(10)), - ), + }}, field: factory.CreateField(mesgnum.Event, fieldnum.EventData), subfieldName: "course_point_index", ok: true, }, { name: "SubFieldSubtitution not ok, can't interpret main field.", - mesg: factory.CreateMesg(mesgnum.Event).WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(100)), - ), + }}, field: factory.CreateField(mesgnum.Event, fieldnum.EventData), ok: false, }, { name: "SubFieldSubtitution field reference not found", - mesg: factory.CreateMesg(mesgnum.Event).WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventActivityType).WithValue(uint8(10)), - ), + }}, field: factory.CreateField(mesgnum.Event, fieldnum.EventData), ok: false, }, @@ -336,187 +337,26 @@ func TestFieldClone(t *testing.T) { t.Fatalf("expected deep cloned, but some data still being referenced.") } - field = proto.Field{} + field = factory.CreateField(mesgnum.Session, fieldnum.SessionTotalCycles) cloned = field.Clone() + field.SubFields[0].Name = "FIT SDK for Go" if diff := cmp.Diff(field, cloned, cmp.Transformer("Value", func(v proto.Value) any { return v.Any() }), - ); diff != "" { + ); diff == "" { t.Fatalf("should not changed") } -} - -func TestCreateMessageDefinition(t *testing.T) { - tt := []struct { - name string - mesg proto.Message - mesgDef proto.MessageDefinition - }{ - { - name: "fields only with non-array values", - mesg: proto.Message{Num: mesgnum.FileId}.WithFields( - factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask, - MesgNum: mesgnum.FileId, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.FileIdType, - Size: 1, - BaseType: basetype.Enum, - }, - }, - }, - }, - { - name: "fields only with mesg architecture big-endian", - mesg: func() proto.Message { - mesg := proto.Message{Num: mesgnum.FileId}.WithFields( - factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), - ) - mesg.Architecture = 1 // big-endian - return mesg - }(), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask, - Architecture: 1, // big-endian - MesgNum: mesgnum.FileId, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.FileIdType, - Size: 1, - BaseType: basetype.Enum, - }, - }, - }, - }, - { - name: "fields only with string value", - mesg: proto.Message{Num: mesgnum.FileId}.WithFields( - factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("FIT SDK Go"), - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask, - MesgNum: mesgnum.FileId, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.FileIdProductName, - Size: 1 * 11, // len("FIT SDK Go") == 10 + '0x00' - BaseType: basetype.String, - }, - }, - }, - }, - { - name: "fields only with array of byte", - mesg: proto.Message{Num: mesgnum.UserProfile}.WithFields( - factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileGlobalId).WithValue([]byte{2, 9}), - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask, - MesgNum: mesgnum.UserProfile, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.UserProfileGlobalId, - Size: 2, - BaseType: basetype.Byte, - }, - }, - }, - }, - { - name: "developer fields", - mesg: proto.Message{Num: mesgnum.UserProfile}. - WithFields( - factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileGlobalId).WithValue([]byte{byte(2), byte(9)})). - WithDeveloperFields( - proto.DeveloperField{ - Num: 0, DeveloperDataIndex: 0, Value: proto.Uint8(1), - }, - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask | proto.DevDataMask, - MesgNum: mesgnum.UserProfile, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.UserProfileGlobalId, - Size: 2, - BaseType: basetype.Byte, - }, - }, - DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ - { - Num: 0, Size: 1, DeveloperDataIndex: 0, - }, - }, - }, - }, - { - name: "developer fields with string value \"FIT SDK Go\", size should be 11", - mesg: proto.Message{Num: mesgnum.UserProfile}. - WithFields( - factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileGlobalId).WithValue([]byte{byte(2), byte(9)})). - WithDeveloperFields( - proto.DeveloperField{ - Num: 0, DeveloperDataIndex: 0, Value: proto.String("FIT SDK Go"), - }, - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask | proto.DevDataMask, - MesgNum: mesgnum.UserProfile, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.UserProfileGlobalId, - Size: 2, - BaseType: basetype.Byte, - }, - }, - DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ - { - Num: 0, Size: 11, DeveloperDataIndex: 0, - }, - }, - }, - }, - { - name: "developer fields with value []uint16{1,2,3}, size should be 3*2 = 6", - mesg: proto.Message{Num: mesgnum.UserProfile}. - WithFields( - factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileGlobalId).WithValue([]byte{byte(2), byte(9)})). - WithDeveloperFields( - proto.DeveloperField{ - Num: 0, DeveloperDataIndex: 0, Value: proto.SliceUint16([]uint16{1, 2, 3}), - }, - ), - mesgDef: proto.MessageDefinition{ - Header: proto.MesgDefinitionMask | proto.DevDataMask, - MesgNum: mesgnum.UserProfile, - FieldDefinitions: []proto.FieldDefinition{ - { - Num: fieldnum.UserProfileGlobalId, - Size: 2, - BaseType: basetype.Byte, - }, - }, - DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ - { - Num: 0, Size: 6, DeveloperDataIndex: 0, - }, - }, - }, - }, - } + field = proto.Field{} + cloned = field.Clone() - for i, tc := range tt { - t.Run(fmt.Sprintf("[%d] %s", i, tc.name), func(t *testing.T) { - mesgDef := proto.CreateMessageDefinition(&tc.mesg) - if diff := cmp.Diff(mesgDef, tc.mesgDef); diff != "" { - t.Fatal(diff) - } - }) + if diff := cmp.Diff(field, cloned, + cmp.Transformer("Value", func(v proto.Value) any { + return v.Any() + }), + ); diff != "" { + t.Fatalf("empty field base, field should be returned as is: %v", diff) } } diff --git a/proto/value_marshal_test.go b/proto/value_marshal_test.go index 4b2fa9e..bdabc08 100644 --- a/proto/value_marshal_test.go +++ b/proto/value_marshal_test.go @@ -72,12 +72,8 @@ func TestValueMarshalAppend(t *testing.T) { for i, tc := range tt { for arch := byte(0); arch <= 1; arch++ { t.Run(fmt.Sprintf("[%d] %T(%v))", i, tc.value.Any(), tc.value.Any()), func(t *testing.T) { - arr := pool.Get().(*[MaxBytesPerMessage]byte) - defer pool.Put(arr) - b := arr[:0] - var err error - *tc.b, err = tc.value.MarshalAppend(b, arch) + *tc.b, err = tc.value.MarshalAppend(nil, arch) if !errors.Is(err, tc.err) { t.Fatalf("expected err: %v, got: %v", tc.err, err) } From d727a775e06a8256d4e59402079eb0759874d079 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 00:23:21 +0700 Subject: [PATCH 02/22] proto: remove Message's WithFields and WithDeveloperFields method --- decoder/decoder_test.go | 107 +++++++++++------------ decoder/raw_test.go | 51 +++++------ encoder/encoder_bench_test.go | 8 +- encoder/encoder_test.go | 44 +++++----- encoder/stream_test.go | 16 ++-- encoder/validator_test.go | 38 ++++---- internal/cmd/testgen/main.go | 8 +- profile/filedef/activity_summary_test.go | 32 +++---- profile/filedef/activity_test.go | 96 ++++++++++---------- profile/filedef/blood_pressure_test.go | 32 +++---- profile/filedef/course_test.go | 52 +++++------ profile/filedef/device_test.go | 40 ++++----- profile/filedef/filedef_test.go | 40 ++++----- profile/filedef/goals_test.go | 24 ++--- profile/filedef/listener_test.go | 12 +-- profile/filedef/monitoring_ab_test.go | 36 ++++---- profile/filedef/monitoring_daily_test.go | 32 +++---- profile/filedef/schedules_test.go | 24 ++--- profile/filedef/segment_list_test.go | 28 +++--- profile/filedef/segment_test.go | 36 ++++---- profile/filedef/settings_test.go | 40 ++++----- profile/filedef/sport_test.go | 48 +++++----- profile/filedef/totals_test.go | 24 ++--- profile/filedef/weight_test.go | 32 +++---- profile/filedef/workout_test.go | 32 +++---- proto/proto.go | 12 --- 26 files changed, 465 insertions(+), 479 deletions(-) diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index bcca0db..d254f41 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -25,7 +25,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/muktihari/fit/factory" - "github.com/muktihari/fit/internal/kit" "github.com/muktihari/fit/kit/datetime" "github.com/muktihari/fit/kit/hash" "github.com/muktihari/fit/kit/hash/crc16" @@ -610,45 +609,43 @@ func createFitForTest() (proto.FIT, []byte) { fit := proto.FIT{ FileHeader: headerForTest(), Messages: []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileActivity)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue([]string{"Heart Rate"}), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - ), - factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ), - factory.CreateMesgOnly(mesgnum.Record). - WithFields( - factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(0)), - factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(77)), - ). - WithDeveloperFields( - proto.DeveloperField{ - DeveloperDataIndex: 0, - Num: 0, - Value: proto.Uint8(100), - }, - ), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(0)), + factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(77)), + }, DeveloperFields: []proto.DeveloperField{ + { + DeveloperDataIndex: 0, + Num: 0, + Value: proto.Uint8(100), + }, + }}, }, } for i := 0; i < 100; i++ { fit.Messages = append(fit.Messages, - factory.CreateMesgOnly(mesgnum.Record).WithFields( - factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32((i+1)*1000)), + proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32((i + 1) * 1000)), factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ), + }}, ) } @@ -1820,18 +1817,18 @@ func TestExpandComponents(t *testing.T) { }{ { name: "expand components single happy flow", - mesg: factory.CreateMesgOnly(mesgnum.Record).WithFields( + mesg: proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), - ), + }}, containingField: factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), components: factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).Components, nFieldAfterExpansion: 2, // 1 for speed, +1 expand field enhanced_speed }, { name: "expand components multiple happy flow", - mesg: factory.CreateMesgOnly(mesgnum.Event).WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventFrontGearChange)), - ), + }}, containingField: factory.CreateField(mesgnum.Event, fieldnum.EventData).WithValue(uint32(0x27010E08)), components: func() []proto.Component { subfields := factory.CreateField(mesgnum.Event, fieldnum.EventData).SubFields @@ -1846,9 +1843,9 @@ func TestExpandComponents(t *testing.T) { }, { name: "expand components run out bits for the last component", - mesg: factory.CreateMesgOnly(mesgnum.Event).WithFields( + mesg: proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventFrontGearChange)), - ), + }}, containingField: factory.CreateField(mesgnum.Event, fieldnum.EventData).WithValue(uint32(0x00010E08)), components: func() []proto.Component { subfields := factory.CreateField(mesgnum.Event, fieldnum.EventData).SubFields @@ -1863,27 +1860,27 @@ func TestExpandComponents(t *testing.T) { }, { name: "expand components containing field value mismatch", - mesg: factory.CreateMesgOnly(mesgnum.Record).WithFields( + mesg: proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue("invalid value"), - ), + }}, containingField: factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue("invalid value"), components: factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).Components, nFieldAfterExpansion: 1, }, { name: "expand components accumulate", - mesg: factory.CreateMesgOnly(mesgnum.Hr).WithFields( + mesg: proto.Message{Num: mesgnum.Hr, Fields: []proto.Field{ factory.CreateField(mesgnum.Hr, fieldnum.HrEventTimestamp).WithValue(uint8(10)), - ), + }}, containingField: factory.CreateField(mesgnum.Hr, fieldnum.HrEventTimestamp12).WithValue(uint8(10)), components: factory.CreateField(mesgnum.Hr, fieldnum.HrEventTimestamp12).Components, nFieldAfterExpansion: 2, }, { name: "expand components do not expand when containing field's value is invalid", - mesg: factory.CreateMesgOnly(mesgnum.Session).WithFields( + mesg: proto.Message{Num: mesgnum.Session, Fields: []proto.Field{ factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(basetype.Uint16Invalid)), - ), + }}, containingField: factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(basetype.Uint16Invalid)), components: factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).Components, nFieldAfterExpansion: 1, @@ -1906,7 +1903,7 @@ func TestExpandMutipleComponents(t *testing.T) { compressedSepeedDistanceField := factory.CreateField(mesgnum.Record, fieldnum.RecordCompressedSpeedDistance). WithValue([]byte{0, 4, 1}) - mesg := factory.CreateMesgOnly(mesgnum.Record).WithFields(compressedSepeedDistanceField) + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{compressedSepeedDistanceField}} dec := New(nil) dec.expandComponents(&mesg, &compressedSepeedDistanceField, compressedSepeedDistanceField.Components) @@ -2007,10 +2004,10 @@ func TestExpandMutipleComponentsDynamicField(t *testing.T) { }, ) - mesg := fac.CreateMesgOnly(customMesgNum).WithFields( + mesg := proto.Message{Num: customMesgNum, Fields: []proto.Field{ fac.CreateField(customMesgNum, 0).WithValue(uint8(10)), // event fac.CreateField(customMesgNum, 2).WithValue(uint32(10)), // compressed_data - ) + }} dec := New(nil, WithFactory(fac)) fieldToExpand := mesg.FieldByNum(2) @@ -2047,14 +2044,14 @@ func TestDecodeDeveloperFields(t *testing.T) { 0, }, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2073,14 +2070,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer fields missing fieldDescription with developer data index 1", r: fnReaderOK, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2099,14 +2096,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer fields missing field description number", r: fnReaderOK, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2125,14 +2122,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer fields missing field description number but unable to read acquired bytes", r: fnReaderErr, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2152,14 +2149,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer fields got io.EOF", r: fnReaderErr, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2179,14 +2176,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer field, devField def's size is zero, skip", r: fnReaderOK, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2211,14 +2208,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer field, devField def's size 1 < 4 size of uint32 ", r: fnReaderOK, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint32)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, @@ -2248,14 +2245,14 @@ func TestDecodeDeveloperFields(t *testing.T) { name: "decode developer fields field description has invalid basetype", r: fnReaderOK, fieldDescription: mesgdef.NewFieldDescription( - kit.Ptr(factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + &proto.Message{Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(255)), - )), + }}, ), mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, diff --git a/decoder/raw_test.go b/decoder/raw_test.go index 4ea6931..58275a4 100644 --- a/decoder/raw_test.go +++ b/decoder/raw_test.go @@ -197,9 +197,9 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesgDef fields return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) @@ -221,15 +221,16 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesgDef n developer fields return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( - factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ).WithDeveloperFields( - proto.DeveloperField{ - DeveloperDataIndex: 0, - Num: 0, - Value: proto.Uint8(100), - }, - ) + mesg := proto.Message{Num: mesgnum.Record, + Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), + }, DeveloperFields: []proto.DeveloperField{ + { + DeveloperDataIndex: 0, + Num: 0, + Value: proto.Uint8(100), + }, + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) @@ -251,15 +252,15 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesgDef developer fields return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ).WithDeveloperFields( - proto.DeveloperField{ + }, DeveloperFields: []proto.DeveloperField{ + { DeveloperDataIndex: 0, Num: 0, Value: proto.Uint8(100), }, - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) @@ -281,15 +282,15 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesgDef fn return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ).WithDeveloperFields( - proto.DeveloperField{ + }, DeveloperFields: []proto.DeveloperField{ + { DeveloperDataIndex: 0, Num: 0, Value: proto.Uint8(100), }, - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) @@ -316,9 +317,9 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesg, mesgDef not found", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgb, _ := mesg.MarshalAppend(nil) @@ -339,9 +340,9 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode mesg, read return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) @@ -384,9 +385,9 @@ func TestRawDecoderDecode(t *testing.T) { { name: "decode crc return io.EOF", r: func() io.Reader { - mesg := factory.CreateMesg(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), - ) + }} h := headerForTest() buf, _ := h.MarshalAppend(nil) mesgDef, _ := proto.NewMessageDefinition(&mesg) diff --git a/encoder/encoder_bench_test.go b/encoder/encoder_bench_test.go index 7eec46d..5ff00d7 100644 --- a/encoder/encoder_bench_test.go +++ b/encoder/encoder_bench_test.go @@ -81,17 +81,17 @@ func createFitForBenchmark(recodSize int) *proto.FIT { for i := 0; i < recodSize-len(fit.Messages); i++ { now = now.Add(time.Second) // only time is moving forward if i%100 == 0 { // add event every 100 message - fit.Messages = append(fit.Messages, factory.CreateMesgOnly(mesgnum.Event).WithFields( + fit.Messages = append(fit.Messages, proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(now)), factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventActivity)), factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(uint8(typedef.EventTypeStop)), - )) + }}) now = now.Add(10 * time.Second) // gap - fit.Messages = append(fit.Messages, factory.CreateMesgOnly(mesgnum.Event).WithFields( + fit.Messages = append(fit.Messages, proto.Message{Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(now)), factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventActivity)), factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(uint8(typedef.EventTypeStart)), - )) + }}) now = now.Add(time.Second) // gap } diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index 08faeaf..f13054a 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -43,26 +43,26 @@ func TestEncodeRealFiles(t *testing.T) { now := time.Date(2023, 9, 15, 6, 0, 0, 0, time.UTC) fit := &proto.FIT{ Messages: []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("Bryton Active App"), - ), - factory.CreateMesgOnly(mesgnum.Activity).WithFields( + }}, + {Num: mesgnum.Activity, Fields: []proto.Field{ factory.CreateField(mesgnum.Activity, fieldnum.ActivityType).WithValue(typedef.ActivityTypeCycling), factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), factory.CreateField(mesgnum.Activity, fieldnum.ActivityNumSessions).WithValue(uint16(1)), - ), - factory.CreateMesgOnly(mesgnum.Session).WithFields( + }}, + {Num: mesgnum.Session, Fields: []proto.Field{ factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(1000)), factory.CreateField(mesgnum.Session, fieldnum.SessionAvgCadence).WithValue(uint8(78)), factory.CreateField(mesgnum.Session, fieldnum.SessionAvgHeartRate).WithValue(uint8(100)), - ), - factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(78)), factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(100)), - ), + }}, }, } @@ -398,9 +398,9 @@ func makeEncodeWithDirectUpdateStrategyTableTest() []encodeWithDirectUpdateTestC { name: "happy flow coverage", fit: &proto.FIT{Messages: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), - ), + }}, }}, w: mockWriterAt{fnWriteOK, fnWriteAtOK}, }, @@ -419,9 +419,9 @@ func makeEncodeWithDirectUpdateStrategyTableTest() []encodeWithDirectUpdateTestC { name: "encode crc error", fit: &proto.FIT{Messages: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), - ), + }}, }}, w: func() io.Writer { fnWrites := []io.Writer{fnWriteOK, fnWriteOK, fnWriteOK, fnWriteErr} @@ -441,9 +441,9 @@ func makeEncodeWithDirectUpdateStrategyTableTest() []encodeWithDirectUpdateTestC { name: "update error", fit: &proto.FIT{FileHeader: proto.FileHeader{Size: 14, DataSize: 100, DataType: proto.DataTypeFIT}, Messages: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), - ), + }}, }}, w: mockWriterAt{fnWriteOK, fnWriteAtErr}, err: io.EOF, @@ -510,9 +510,9 @@ func makeEncodeWithEarlyCheckStrategy() []encodeWithEarlyCheckStrategyTestCase { { name: "encode header error", fit: &proto.FIT{Messages: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(uint16(typedef.ManufacturerGarmin)), - ), + }}, }}, w: fnWriteErr, err: io.EOF, @@ -520,9 +520,9 @@ func makeEncodeWithEarlyCheckStrategy() []encodeWithEarlyCheckStrategyTestCase { { name: "encode messages error", fit: &proto.FIT{Messages: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(uint16(typedef.ManufacturerGarmin)), - ), + }}, }}, w: func() io.Writer { fnInstances := []io.Writer{fnWriteOK, fnWriteErr} @@ -1120,10 +1120,10 @@ func makeEncodeMessagesTableTest() []encodeMessagesTestCase { name: "encode messages happy flow", mesgValidator: fnValidateOK, mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(uint16(typedef.ManufacturerGarmin)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProduct).WithValue(uint16(typedef.GarminProductEdge1030)), - ), + }}, }, }, { @@ -1387,9 +1387,9 @@ func TestEncodeMessagesWithContext(t *testing.T) { cancel() mesgs := []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileActivity)), - ), + }}, } enc := New(nil, WithWriteBufferSize(0)) err := enc.encodeMessagesWithContext(ctx, mesgs) diff --git a/encoder/stream_test.go b/encoder/stream_test.go index 8ccef38..cf86e68 100644 --- a/encoder/stream_test.go +++ b/encoder/stream_test.go @@ -27,12 +27,12 @@ func TestStreamEncoderOneSequenceHappyFlow(t *testing.T) { t.Fatal(err) } - fileIdMesg := factory.CreateMesgOnly(mesgnum.FileId).WithFields( + fileIdMesg := proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(time.Now())), - ) - recordMesg := factory.CreateMesgOnly(mesgnum.Record).WithFields( + }} + recordMesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(1000)), - ) + }} err = streamEnc.WriteMessage(&fileIdMesg) if err != nil { @@ -72,9 +72,9 @@ func TestStreamEncoderUnhappyFlow(t *testing.T) { enc := New(mockWriterAt{Writer: fnWriteErr}, WithWriteBufferSize(0)) streamEnc, _ := enc.StreamEncoder() - mesg := factory.CreateMesgOnly(mesgnum.FileId).WithFields( + mesg := proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(time.Now())), - ) + }} err := streamEnc.WriteMessage(&mesg) if !errors.Is(err, io.EOF) { t.Fatalf("expected err: %v, got: %v", io.EOF, err) @@ -155,9 +155,9 @@ func TestStreamEncoderWithoutWriteBuffer(t *testing.T) { t.Fatal(err) } - fileIdMesg := factory.CreateMesgOnly(mesgnum.FileId).WithFields( + fileIdMesg := proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(time.Now())), - ) + }} err = streamEnc.WriteMessage(&fileIdMesg) if err != nil { diff --git a/encoder/validator_test.go b/encoder/validator_test.go index efab36d..b08ccf0 100644 --- a/encoder/validator_test.go +++ b/encoder/validator_test.go @@ -135,7 +135,7 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "mesg contain expanded field", mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.Record).WithFields( + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), func() proto.Field { field := factory.CreateField(mesgnum.Record, fieldnum.RecordEnhancedSpeed) @@ -143,23 +143,23 @@ func TestMessageValidatorValidate(t *testing.T) { field.Value = proto.Uint32(1000) return field }(), - ), + }}, }, }, { name: "mesg contain field with scaled value", mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.Record).WithFields( + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue((float64(37304) / 5) - 500), // 6960.8m - ), + }}, }, }, { name: "mesg contain field value type not align", mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.Record).WithFields( + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint32(1000)), // should be uint16 - ), + }}, }, errs: []error{ErrValueTypeMismatch}, }, @@ -202,21 +202,21 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "invalid utf-8 string", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("\xbd"), - ), + }}, }, errs: []error{ErrInvalidUTF8String}, }, { name: "invalid utf-8 []string", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("valid utf-8 string"), - ), - factory.CreateMesg(mesgnum.SegmentFile).WithFields( + }}, + {Num: mesgnum.SegmentFile, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentFile, fieldnum.SegmentFileLeaderActivityIdString).WithValue([]string{"valid utf-8", "\xbd"}), // valid and invalid string in array - ), + }}, }, errs: []error{nil, ErrInvalidUTF8String}, }, @@ -282,9 +282,9 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "field value size exceed max allowed", mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue(strings.Repeat("a", 256)), - ), + }}, }, errs: []error{ErrExceedMaxAllowed}, }, @@ -308,13 +308,13 @@ func TestMessageValidatorValidate(t *testing.T) { return mesgValidator }(), mesgs: []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithDeveloperFields( - proto.DeveloperField{ + {Num: mesgnum.FileId, DeveloperFields: []proto.DeveloperField{ + { DeveloperDataIndex: 0, Num: 1, Value: proto.String(strings.Repeat("a", 256)), }, - ), + }}, }, errs: []error{ErrExceedMaxAllowed}, }, @@ -522,7 +522,7 @@ func TestMessageValidatorValidate(t *testing.T) { func BenchmarkValidate(b *testing.B) { b.StopTimer() mesgValidator := NewMessageValidator() - mesg := factory.CreateMesgOnly(mesgnum.Record).WithFields( + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16(10000)), func() proto.Field { @@ -543,7 +543,7 @@ func BenchmarkValidate(b *testing.B) { field.Value = proto.Uint32(1000) return field }(), - ) + }} b.StartTimer() for i := 0; i < b.N; i++ { diff --git a/internal/cmd/testgen/main.go b/internal/cmd/testgen/main.go index d87c646..29f6ca3 100644 --- a/internal/cmd/testgen/main.go +++ b/internal/cmd/testgen/main.go @@ -62,15 +62,15 @@ func createValidFitOnlyContainFileId(ctx context.Context) error { now := datetime.ToTime(uint32(1062766519)) fit := &proto.FIT{Messages: []proto.Message{ - factory.CreateMesgOnly(typedef.MesgNumFileId).WithFields( + {Num: typedef.MesgNumFileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("something ss"), factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(typedef.MesgNumActivity).WithFields( + }}, + {Num: typedef.MesgNumActivity, Fields: []proto.Field{ factory.CreateField(typedef.MesgNumActivity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), - ), + }}, factory.CreateMesg(typedef.MesgNumRecord).WithFieldValues(map[byte]any{ fieldnum.RecordTimestamp: datetime.ToUint32(now), fieldnum.RecordHeartRate: uint8(112), diff --git a/profile/filedef/activity_summary_test.go b/profile/filedef/activity_summary_test.go index e98b47d..77d61b4 100644 --- a/profile/filedef/activity_summary_test.go +++ b/profile/filedef/activity_summary_test.go @@ -19,32 +19,32 @@ import ( func newActivitySummaryMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileActivitySummary)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Lap).WithFields( + }}, + {Num: mesgnum.Lap, Fields: []proto.Field{ factory.CreateField(mesgnum.Lap, fieldnum.LapTimestamp).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.Session).WithFields( + }}, + {Num: mesgnum.Session, Fields: []proto.Field{ factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Activity).WithFields( + }}, + {Num: mesgnum.Activity, Fields: []proto.Field{ factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/activity_test.go b/profile/filedef/activity_test.go index bc48b82..17d5ed7 100644 --- a/profile/filedef/activity_test.go +++ b/profile/filedef/activity_test.go @@ -60,93 +60,93 @@ func newActivityMessageForTest(now time.Time) []proto.Message { func newActivityMessagesWithExpectedOrder(now time.Time) (mesgs []proto.Message, ordered []proto.Message) { mesgs = []proto.Message{ - 0: factory.CreateMesgOnly(mesgnum.FileId).WithFields( + 0: {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileActivity)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - 1: factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + 1: {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - 2: factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + 2: {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue([]string{"Heart Rate"}), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), - ), - 3: factory.CreateMesgOnly(mesgnum.DeviceInfo).WithFields( + }}, + 3: {Num: mesgnum.DeviceInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoManufacturer).WithValue(uint16(typedef.ManufacturerGarmin)), - ), - 4: factory.CreateMesgOnly(mesgnum.UserProfile).WithFields( + }}, + 4: {Num: mesgnum.UserProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileFriendlyName).WithValue("Mary Jane"), factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileAge).WithValue(uint8(21)), - ), - 5: factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + 5: {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventActivity)), factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(uint8(typedef.EventTypeStart)), - ), - 6: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 6: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 7: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 7: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 8: factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + 8: {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 9: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 9: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 10: factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + 10: {Num: mesgnum.Event, Fields: []proto.Field{ // Intentionally using same timestamp as last message. factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(now)), - ), - 11: factory.CreateMesgOnly(mesgnum.Lap).WithFields( + }}, + 11: {Num: mesgnum.Lap, Fields: []proto.Field{ factory.CreateField(mesgnum.Lap, fieldnum.LapTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 12: factory.CreateMesgOnly(mesgnum.Session).WithFields( + }}, + 12: {Num: mesgnum.Session, Fields: []proto.Field{ factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 13: factory.CreateMesgOnly(mesgnum.Activity).WithFields( + }}, + 13: {Num: mesgnum.Activity, Fields: []proto.Field{ factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unordered optional Messages - 14: factory.CreateMesgOnly(mesgnum.Length).WithFields( + 14: {Num: mesgnum.Length, Fields: []proto.Field{ factory.CreateField(mesgnum.Length, fieldnum.LengthAvgSpeed).WithValue(uint16(1000)), - ), - 15: factory.CreateMesgOnly(mesgnum.SegmentLap).WithFields( + }}, + 15: {Num: mesgnum.SegmentLap, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentLap, fieldnum.SegmentLapAvgCadence).WithValue(uint8(100)), - ), - 16: factory.CreateMesgOnly(mesgnum.ZonesTarget).WithFields( + }}, + 16: {Num: mesgnum.ZonesTarget, Fields: []proto.Field{ factory.CreateField(mesgnum.ZonesTarget, fieldnum.ZonesTargetMaxHeartRate).WithValue(uint8(190)), - ), - 17: factory.CreateMesgOnly(mesgnum.Workout).WithFields( + }}, + 17: {Num: mesgnum.Workout, Fields: []proto.Field{ factory.CreateField(mesgnum.Workout, fieldnum.WorkoutSport).WithValue(uint8(typedef.SportCycling)), - ), - 18: factory.CreateMesgOnly(mesgnum.WorkoutStep).WithFields( + }}, + 18: {Num: mesgnum.WorkoutStep, Fields: []proto.Field{ factory.CreateField(mesgnum.WorkoutStep, fieldnum.WorkoutStepIntensity).WithValue(uint8(typedef.IntensityActive)), - ), - 19: factory.CreateMesgOnly(mesgnum.Hr).WithFields( + }}, + 19: {Num: mesgnum.Hr, Fields: []proto.Field{ factory.CreateField(mesgnum.Hr, fieldnum.HrTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 20: factory.CreateMesgOnly(mesgnum.Hrv).WithFields( + }}, + 20: {Num: mesgnum.Hrv, Fields: []proto.Field{ factory.CreateField(mesgnum.Hrv, fieldnum.HrvTime).WithValue([]uint16{uint16(1000)}), - ), + }}, // Unrelated messages - 21: factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + 21: {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Special case: // 1. CoursePoint's Timestamp Num is 1 // 2. Set's Timestamp Num is 254 - 22: factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + 22: {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - 23: factory.CreateMesgOnly(mesgnum.Set).WithFields( + }}, + 23: {Num: mesgnum.Set, Fields: []proto.Field{ factory.CreateField(mesgnum.Set, fieldnum.SetTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } ordered = []proto.Message{ diff --git a/profile/filedef/blood_pressure_test.go b/profile/filedef/blood_pressure_test.go index 59ebfba..f5e359e 100644 --- a/profile/filedef/blood_pressure_test.go +++ b/profile/filedef/blood_pressure_test.go @@ -19,35 +19,35 @@ import ( func newBloodPressureMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileBloodPressure)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.UserProfile).WithFields( + }}, + {Num: mesgnum.UserProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileAge).WithValue(uint8(27)), - ), - factory.CreateMesgOnly(mesgnum.BloodPressure).WithFields( + }}, + {Num: mesgnum.BloodPressure, Fields: []proto.Field{ factory.CreateField(mesgnum.BloodPressure, fieldnum.BloodPressureTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), factory.CreateField(mesgnum.BloodPressure, fieldnum.BloodPressureSystolicPressure).WithValue(uint16(110)), factory.CreateField(mesgnum.BloodPressure, fieldnum.BloodPressureDiastolicPressure).WithValue(uint16(80)), factory.CreateField(mesgnum.BloodPressure, fieldnum.BloodPressureHeartRate).WithValue(uint8(100)), - ), - factory.CreateMesgOnly(mesgnum.DeviceInfo).WithFields( + }}, + {Num: mesgnum.DeviceInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/course_test.go b/profile/filedef/course_test.go index 9b7fe5c..480f8df 100644 --- a/profile/filedef/course_test.go +++ b/profile/filedef/course_test.go @@ -19,50 +19,50 @@ import ( func newCourseMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileCourse)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Course).WithFields( + }}, + {Num: mesgnum.Course, Fields: []proto.Field{ factory.CreateField(mesgnum.Course, fieldnum.CourseSport).WithValue(uint8(typedef.SportRunning)), - ), - factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), factory.CreateField(mesgnum.Event, fieldnum.EventEvent).WithValue(uint8(typedef.EventActivity)), factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(uint8(typedef.EventTypeStart)), - ), - factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Lap).WithFields( + }}, + {Num: mesgnum.Lap, Fields: []proto.Field{ factory.CreateField(mesgnum.Lap, fieldnum.LapTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unordered optional Messages - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/device_test.go b/profile/filedef/device_test.go index a318212..a33ddb5 100644 --- a/profile/filedef/device_test.go +++ b/profile/filedef/device_test.go @@ -19,42 +19,42 @@ import ( func newDeviceMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileDevice)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Software).WithFields( + }}, + {Num: mesgnum.Software, Fields: []proto.Field{ factory.CreateField(mesgnum.Software, fieldnum.SoftwareMessageIndex).WithValue(uint16(typedef.MessageIndexReserved)), - ), - factory.CreateMesgOnly(mesgnum.Capabilities).WithFields( + }}, + {Num: mesgnum.Capabilities, Fields: []proto.Field{ factory.CreateField(mesgnum.Capabilities, fieldnum.CapabilitiesSports).WithValue([]uint8{ uint8(typedef.SportBits0Basketball), uint8(typedef.SportBits1AmericanFootball), uint8(typedef.SportBits2Paddling), }), - ), - factory.CreateMesgOnly(mesgnum.FileCapabilities).WithFields( + }}, + {Num: mesgnum.FileCapabilities, Fields: []proto.Field{ factory.CreateField(mesgnum.FileCapabilities, fieldnum.FileCapabilitiesType).WithValue(uint8(typedef.FileActivity)), - ), - factory.CreateMesgOnly(mesgnum.MesgCapabilities).WithFields( + }}, + {Num: mesgnum.MesgCapabilities, Fields: []proto.Field{ factory.CreateField(mesgnum.MesgCapabilities, fieldnum.MesgCapabilitiesFile).WithValue(uint8(typedef.FileActivity)), - ), - factory.CreateMesgOnly(mesgnum.FieldCapabilities).WithFields( + }}, + {Num: mesgnum.FieldCapabilities, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldCapabilities, fieldnum.FieldCapabilitiesFile).WithValue(uint8(typedef.FileActivity)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/filedef_test.go b/profile/filedef/filedef_test.go index eb4f1fc..47d7c5e 100644 --- a/profile/filedef/filedef_test.go +++ b/profile/filedef/filedef_test.go @@ -42,36 +42,36 @@ func TestSortMessagesByTimestamp(t *testing.T) { // 1. CoursePoint's Timestamp Num is 1 // 2. Set's Timestamp Num is 254 messages := []proto.Message{ - 0: factory.CreateMesgOnly(mesgnum.FileId).WithFields( + 0: {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerDevelopment.Uint16()), - ), - 1: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 1: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), - ), - 2: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 2: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), - ), - 3: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 3: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(1 * time.Second))), - ), - 4: factory.CreateMesgOnly(mesgnum.Event).WithFields( + }}, + 4: {Num: mesgnum.Event, Fields: []proto.Field{ factory.CreateField(mesgnum.Event, fieldnum.EventTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), - ), - 5: factory.CreateMesgOnly(mesgnum.Session).WithFields( + }}, + 5: {Num: mesgnum.Session, Fields: []proto.Field{ factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), - ), - 6: factory.CreateMesgOnly(mesgnum.UserProfile).WithFields( + }}, + 6: {Num: mesgnum.UserProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileFriendlyName).WithValue("muktihari"), - ), - 7: factory.CreateMesgOnly(mesgnum.Set).WithFields( + }}, + 7: {Num: mesgnum.Set, Fields: []proto.Field{ factory.CreateField(mesgnum.Set, fieldnum.SetTimestamp).WithValue(datetime.ToUint32(now.Add(4 * time.Second))), - ), - 8: factory.CreateMesgOnly(mesgnum.Record).WithFields( + }}, + 8: {Num: mesgnum.Record, Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), - ), - 9: factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + 9: {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(now.Add(3 * time.Second))), - ), + }}, } expected := []proto.Message{ diff --git a/profile/filedef/goals_test.go b/profile/filedef/goals_test.go index b7af4d4..8698507 100644 --- a/profile/filedef/goals_test.go +++ b/profile/filedef/goals_test.go @@ -19,26 +19,26 @@ import ( func newGoalsMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileGoals)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Goal).WithFields( + }}, + {Num: mesgnum.Goal, Fields: []proto.Field{ factory.CreateField(mesgnum.Goal, fieldnum.GoalSport).WithValue(uint8(typedef.SportSoccer)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/listener_test.go b/profile/filedef/listener_test.go index 6b616b9..9065cb3 100644 --- a/profile/filedef/listener_test.go +++ b/profile/filedef/listener_test.go @@ -169,17 +169,17 @@ func TestListenerForSingleFitFile(t *testing.T) { func() table { mesgs := newActivityMessageForTest(now) mesgs = append(mesgs, - factory.CreateMesgOnly(mesgnum.Record). - WithFields( + proto.Message{Num: mesgnum.Record, + Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ). - WithDeveloperFields( - proto.DeveloperField{ + }, + DeveloperFields: []proto.DeveloperField{ + { DeveloperDataIndex: 0, Num: 0, Value: proto.Uint8(100), }, - ), + }}, ) return table{ name: "default listener for activity containing developer fields", diff --git a/profile/filedef/monitoring_ab_test.go b/profile/filedef/monitoring_ab_test.go index 2f43c52..f4cfd6c 100644 --- a/profile/filedef/monitoring_ab_test.go +++ b/profile/filedef/monitoring_ab_test.go @@ -19,39 +19,39 @@ import ( func newMonitoringAMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileMonitoringA)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.MonitoringInfo).WithFields( + }}, + {Num: mesgnum.MonitoringInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.MonitoringInfo, fieldnum.MonitoringInfoActivityType).WithValue([]uint8{ uint8(typedef.ActivityTypeCycling), uint8(typedef.ActivityTypeRunning), }), - ), - factory.CreateMesgOnly(mesgnum.Monitoring).WithFields( + }}, + {Num: mesgnum.Monitoring, Fields: []proto.Field{ factory.CreateField(mesgnum.Monitoring, fieldnum.MonitoringActivityType).WithValue(uint8(typedef.ActivityTypeCycling)), - ), - factory.CreateMesgOnly(mesgnum.Monitoring).WithFields( + }}, + {Num: mesgnum.Monitoring, Fields: []proto.Field{ factory.CreateField(mesgnum.Monitoring, fieldnum.MonitoringActivityType).WithValue(uint8(typedef.ActivityTypeRunning)), - ), - factory.CreateMesgOnly(mesgnum.DeviceInfo).WithFields( + }}, + {Num: mesgnum.DeviceInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoBatteryStatus).WithValue(uint8(typedef.BatteryStatusGood)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/monitoring_daily_test.go b/profile/filedef/monitoring_daily_test.go index 42f49a4..01301f9 100644 --- a/profile/filedef/monitoring_daily_test.go +++ b/profile/filedef/monitoring_daily_test.go @@ -19,32 +19,32 @@ import ( func newMonitoringDailyMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileMonitoringDaily)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.MonitoringInfo).WithFields( + }}, + {Num: mesgnum.MonitoringInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.MonitoringInfo, fieldnum.MonitoringInfoTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.Monitoring).WithFields( + }}, + {Num: mesgnum.Monitoring, Fields: []proto.Field{ factory.CreateField(mesgnum.Monitoring, fieldnum.MonitoringIntensity).WithValue(uint8(typedef.IntensityActive)), - ), - factory.CreateMesgOnly(mesgnum.DeviceInfo).WithFields( + }}, + {Num: mesgnum.DeviceInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/schedules_test.go b/profile/filedef/schedules_test.go index 0367751..e757b8f 100644 --- a/profile/filedef/schedules_test.go +++ b/profile/filedef/schedules_test.go @@ -19,26 +19,26 @@ import ( func newSchedulesMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileSchedules)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Schedule).WithFields( + }}, + {Num: mesgnum.Schedule, Fields: []proto.Field{ factory.CreateField(mesgnum.Schedule, fieldnum.ScheduleCompleted).WithValue(true), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/segment_list_test.go b/profile/filedef/segment_list_test.go index d292d68..15712bf 100644 --- a/profile/filedef/segment_list_test.go +++ b/profile/filedef/segment_list_test.go @@ -19,29 +19,29 @@ import ( func newSegmentListMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileSegmentList)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FileCreator).WithFields( + }}, + {Num: mesgnum.FileCreator, Fields: []proto.Field{ factory.CreateField(mesgnum.FileCreator, fieldnum.FileCreatorSoftwareVersion).WithValue(uint16(1)), - ), - factory.CreateMesgOnly(mesgnum.SegmentFile).WithFields( + }}, + {Num: mesgnum.SegmentFile, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentFile, fieldnum.SegmentFileEnabled).WithValue(true), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/segment_test.go b/profile/filedef/segment_test.go index 9a2e9a0..954d824 100644 --- a/profile/filedef/segment_test.go +++ b/profile/filedef/segment_test.go @@ -19,35 +19,35 @@ import ( func newSegmentMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileSegment)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.SegmentId).WithFields( + }}, + {Num: mesgnum.SegmentId, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentId, fieldnum.SegmentIdEnabled).WithValue(true), - ), - factory.CreateMesgOnly(mesgnum.SegmentLeaderboardEntry).WithFields( + }}, + {Num: mesgnum.SegmentLeaderboardEntry, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentLeaderboardEntry, fieldnum.SegmentLeaderboardEntryName).WithValue("entry test"), - ), - factory.CreateMesgOnly(mesgnum.SegmentLap).WithFields( + }}, + {Num: mesgnum.SegmentLap, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentLap, fieldnum.SegmentLapName).WithValue("lap test"), - ), - factory.CreateMesgOnly(mesgnum.SegmentPoint).WithFields( + }}, + {Num: mesgnum.SegmentPoint, Fields: []proto.Field{ factory.CreateField(mesgnum.SegmentPoint, fieldnum.SegmentPointAltitude).WithValue(uint16(10000)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/settings_test.go b/profile/filedef/settings_test.go index 039d5d8..7ef2d33 100644 --- a/profile/filedef/settings_test.go +++ b/profile/filedef/settings_test.go @@ -19,38 +19,38 @@ import ( func newSettingsMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileSettings)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.UserProfile).WithFields( + }}, + {Num: mesgnum.UserProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileAge).WithValue(uint8(29)), - ), - factory.CreateMesgOnly(mesgnum.HrmProfile).WithFields( + }}, + {Num: mesgnum.HrmProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.HrmProfile, fieldnum.HrmProfileEnabled).WithValue(true), - ), - factory.CreateMesgOnly(mesgnum.SdmProfile).WithFields( + }}, + {Num: mesgnum.SdmProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.SdmProfile, fieldnum.SdmProfileEnabled).WithValue(true), - ), - factory.CreateMesgOnly(mesgnum.BikeProfile).WithFields( + }}, + {Num: mesgnum.BikeProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.BikeProfile, fieldnum.BikeProfileEnabled).WithValue(true), - ), - factory.CreateMesgOnly(mesgnum.DeviceSettings).WithFields( + }}, + {Num: mesgnum.DeviceSettings, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceSettings, fieldnum.DeviceSettingsBacklightMode).WithValue(uint8(typedef.BacklightModeAutoBrightness)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/sport_test.go b/profile/filedef/sport_test.go index b9daceb..0893000 100644 --- a/profile/filedef/sport_test.go +++ b/profile/filedef/sport_test.go @@ -19,44 +19,44 @@ import ( func newSportMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileSport)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.ZonesTarget).WithFields( + }}, + {Num: mesgnum.ZonesTarget, Fields: []proto.Field{ factory.CreateField(mesgnum.ZonesTarget, fieldnum.ZonesTargetMaxHeartRate).WithValue(uint8(190)), - ), - factory.CreateMesgOnly(mesgnum.Sport).WithFields( + }}, + {Num: mesgnum.Sport, Fields: []proto.Field{ factory.CreateField(mesgnum.Sport, fieldnum.SportSport).WithValue(uint8(typedef.SportAmericanFootball)), - ), - factory.CreateMesgOnly(mesgnum.HrZone).WithFields( + }}, + {Num: mesgnum.HrZone, Fields: []proto.Field{ factory.CreateField(mesgnum.HrZone, fieldnum.HrZoneHighBpm).WithValue(uint8(177)), - ), - factory.CreateMesgOnly(mesgnum.PowerZone).WithFields( + }}, + {Num: mesgnum.PowerZone, Fields: []proto.Field{ factory.CreateField(mesgnum.PowerZone, fieldnum.PowerZoneHighValue).WithValue(uint16(200)), - ), - factory.CreateMesgOnly(mesgnum.MetZone).WithFields( + }}, + {Num: mesgnum.MetZone, Fields: []proto.Field{ factory.CreateField(mesgnum.MetZone, fieldnum.MetZoneHighBpm).WithValue(uint8(178)), - ), - factory.CreateMesgOnly(mesgnum.SpeedZone).WithFields( + }}, + {Num: mesgnum.SpeedZone, Fields: []proto.Field{ factory.CreateField(mesgnum.SpeedZone, fieldnum.SpeedZoneHighValue).WithValue(uint16(10000)), - ), - factory.CreateMesgOnly(mesgnum.CadenceZone).WithFields( + }}, + {Num: mesgnum.CadenceZone, Fields: []proto.Field{ factory.CreateField(mesgnum.CadenceZone, fieldnum.CadenceZoneHighValue).WithValue(uint8(100)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/totals_test.go b/profile/filedef/totals_test.go index f44518f..309974e 100644 --- a/profile/filedef/totals_test.go +++ b/profile/filedef/totals_test.go @@ -19,26 +19,26 @@ import ( func newTotalsMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileTotals)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Totals).WithFields( + }}, + {Num: mesgnum.Totals, Fields: []proto.Field{ factory.CreateField(mesgnum.Totals, fieldnum.TotalsSport).WithValue(uint8(typedef.SportSoccer)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/weight_test.go b/profile/filedef/weight_test.go index 694674e..5a1adfd 100644 --- a/profile/filedef/weight_test.go +++ b/profile/filedef/weight_test.go @@ -19,32 +19,32 @@ import ( func newWeightMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileWeight)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.UserProfile).WithFields( + }}, + {Num: mesgnum.UserProfile, Fields: []proto.Field{ factory.CreateField(mesgnum.UserProfile, fieldnum.UserProfileAge).WithValue(uint8(27)), - ), - factory.CreateMesgOnly(mesgnum.WeightScale).WithFields( + }}, + {Num: mesgnum.WeightScale, Fields: []proto.Field{ factory.CreateField(mesgnum.WeightScale, fieldnum.WeightScaleBmi).WithValue(uint16(1000)), - ), - factory.CreateMesgOnly(mesgnum.DeviceInfo).WithFields( + }}, + {Num: mesgnum.DeviceInfo, Fields: []proto.Field{ factory.CreateField(mesgnum.DeviceInfo, fieldnum.DeviceInfoTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + }}, + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/profile/filedef/workout_test.go b/profile/filedef/workout_test.go index c50c619..5721005 100644 --- a/profile/filedef/workout_test.go +++ b/profile/filedef/workout_test.go @@ -19,32 +19,32 @@ import ( func newWorkoutMessageForTest(now time.Time) []proto.Message { return []proto.Message{ - factory.CreateMesgOnly(mesgnum.FileId).WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(uint8(typedef.FileWorkout)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), - ), - factory.CreateMesgOnly(mesgnum.DeveloperDataId).WithFields( + }}, + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.FieldDescription).WithFields( + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), - ), - factory.CreateMesgOnly(mesgnum.Workout).WithFields( + }}, + {Num: mesgnum.Workout, Fields: []proto.Field{ factory.CreateField(mesgnum.Workout, fieldnum.WorkoutSport).WithValue(uint8(typedef.SportSwimming)), - ), - factory.CreateMesgOnly(mesgnum.WorkoutStep).WithFields( + }}, + {Num: mesgnum.WorkoutStep, Fields: []proto.Field{ factory.CreateField(mesgnum.WorkoutStep, fieldnum.WorkoutStepEquipment).WithValue(uint8(typedef.WorkoutEquipmentSwimFins)), - ), - factory.CreateMesgOnly(mesgnum.WorkoutStep).WithFields( + }}, + {Num: mesgnum.WorkoutStep, Fields: []proto.Field{ factory.CreateField(mesgnum.WorkoutStep, fieldnum.WorkoutStepEquipment).WithValue(uint8(typedef.WorkoutEquipmentSwimSnorkel)), - ), + }}, // Unrelated messages - factory.CreateMesgOnly(mesgnum.CoursePoint).WithFields( + {Num: mesgnum.CoursePoint, Fields: []proto.Field{ factory.CreateField(mesgnum.CoursePoint, fieldnum.CoursePointTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), - factory.CreateMesgOnly(mesgnum.BarometerData).WithFields( + }}, + {Num: mesgnum.BarometerData, Fields: []proto.Field{ factory.CreateField(mesgnum.BarometerData, fieldnum.BarometerDataTimestamp).WithValue(datetime.ToUint32(incrementSecond(&now))), - ), + }}, } } diff --git a/proto/proto.go b/proto/proto.go index bcb2eea..2c9b69a 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -170,12 +170,6 @@ type Message struct { DeveloperFields []DeveloperField // List of DeveloperField } -// WithFields puts the provided fields into the message's fields. -func (m Message) WithFields(fields ...Field) Message { - m.Fields = fields - return m -} - // WithFieldValues assigns the values of the targeted fields with the given map, // where map[byte]any represents the field numbers and their respective values. // If the Message does not have a corresponding field number match in the Fields, no value will be assigned or added. @@ -192,12 +186,6 @@ func (m Message) WithFieldValues(fieldNumValues map[byte]any) Message { return m } -// WithFields puts the provided fields into the message's fields. -func (m Message) WithDeveloperFields(developerFields ...DeveloperField) Message { - m.DeveloperFields = developerFields - return m -} - // FieldByNum returns a pointer to the Field in a Message, if not found return nil. func (m *Message) FieldByNum(num byte) *Field { for i := range m.Fields { From e1c5036efa2cdde1171a3476594322efd8f869b0 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 00:26:35 +0700 Subject: [PATCH 03/22] remove internal/kit --- internal/kit/kit.go | 8 --- internal/kit/kit_test.go | 21 -------- proto/value_marshal_test.go | 99 ++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 80 deletions(-) delete mode 100644 internal/kit/kit.go delete mode 100644 internal/kit/kit_test.go diff --git a/internal/kit/kit.go b/internal/kit/kit.go deleted file mode 100644 index 1b56f1e..0000000 --- a/internal/kit/kit.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2023 The FIT SDK for Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package kit - -// Ptr returns new pointer of v -func Ptr[T any](v T) *T { return &v } diff --git a/internal/kit/kit_test.go b/internal/kit/kit_test.go deleted file mode 100644 index 0fe4a81..0000000 --- a/internal/kit/kit_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 The FIT SDK for Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package kit_test - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/muktihari/fit/internal/kit" -) - -func TestPtr(t *testing.T) { - val := float64(10) - ptr := kit.Ptr(val) - - if diff := cmp.Diff(val, *ptr); diff != "" { - t.Fatal(diff) - } -} diff --git a/proto/value_marshal_test.go b/proto/value_marshal_test.go index bdabc08..6d8fb14 100644 --- a/proto/value_marshal_test.go +++ b/proto/value_marshal_test.go @@ -14,66 +14,63 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/muktihari/fit/internal/kit" ) func TestValueMarshalAppend(t *testing.T) { tt := []struct { - b *[]byte value Value err error }{ - {b: kit.Ptr([]byte{}), value: Bool(false)}, - {b: kit.Ptr([]byte{}), value: Bool(true)}, - {b: kit.Ptr([]byte{}), value: Int8(-19)}, - {b: kit.Ptr([]byte{}), value: Uint8(129)}, - {b: kit.Ptr([]byte{}), value: Int16(1429)}, - {b: kit.Ptr([]byte{}), value: Int16(-429)}, - {b: kit.Ptr([]byte{}), value: Uint16(9929)}, - {b: kit.Ptr([]byte{}), value: Int32(819293429)}, - {b: kit.Ptr([]byte{}), value: Int32(-8979123)}, - {b: kit.Ptr([]byte{}), value: Uint32(9929)}, - {b: kit.Ptr([]byte{}), value: Int64(819293429)}, - {b: kit.Ptr([]byte{}), value: Int64(-8979123)}, - {b: kit.Ptr([]byte{}), value: Uint64(9929)}, - {b: kit.Ptr([]byte{}), value: Float32(819293429.192321)}, - {b: kit.Ptr([]byte{}), value: Float32(-8979123.546734)}, - {b: kit.Ptr([]byte{}), value: Float64(8192934298908979.192321)}, - {b: kit.Ptr([]byte{}), value: Float64(-897912398989898.546734)}, - {b: kit.Ptr([]byte{}), value: String("FIT SDK")}, - {b: kit.Ptr([]byte{}), value: String("")}, - {b: kit.Ptr([]byte{}), value: SliceBool([]bool{true, false})}, - {b: kit.Ptr([]byte{}), value: SliceUint8([]byte{1, 2})}, - {b: kit.Ptr([]byte{}), value: SliceUint8([]uint8{1, 2})}, - {b: kit.Ptr([]byte{}), value: SliceInt8([]int8{-19})}, - {b: kit.Ptr([]byte{}), value: SliceUint8([]uint8{129})}, - {b: kit.Ptr([]byte{}), value: SliceInt16([]int16{1429})}, - {b: kit.Ptr([]byte{}), value: SliceInt16([]int16{-429})}, - {b: kit.Ptr([]byte{}), value: SliceUint16([]uint16{9929})}, - {b: kit.Ptr([]byte{}), value: SliceInt32([]int32{819293429})}, - {b: kit.Ptr([]byte{}), value: SliceInt32([]int32{-8979123})}, - {b: kit.Ptr([]byte{}), value: SliceUint32([]uint32{9929})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{"supported"})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{""})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{"\x00"})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{"\x00", "\x00"})}, - {b: kit.Ptr([]byte{}), value: SliceString([]string{string([]byte{'\x00'})})}, - {b: kit.Ptr([]byte{}), value: SliceInt64([]int64{819293429})}, - {b: kit.Ptr([]byte{}), value: SliceInt64([]int64{-8979123})}, - {b: kit.Ptr([]byte{}), value: SliceUint64([]uint64{9929})}, - {b: kit.Ptr([]byte{}), value: SliceFloat32([]float32{819293429.192321})}, - {b: kit.Ptr([]byte{}), value: SliceFloat32([]float32{-8979123.546734})}, - {b: kit.Ptr([]byte{}), value: SliceFloat64([]float64{8192934298908979.192321})}, - {b: kit.Ptr([]byte{}), value: SliceFloat64([]float64{-897912398989898.546734})}, - {b: kit.Ptr([]byte{}), value: Value{}, err: ErrTypeNotSupported}, + {value: Bool(false)}, + {value: Bool(true)}, + {value: Int8(-19)}, + {value: Uint8(129)}, + {value: Int16(1429)}, + {value: Int16(-429)}, + {value: Uint16(9929)}, + {value: Int32(819293429)}, + {value: Int32(-8979123)}, + {value: Uint32(9929)}, + {value: Int64(819293429)}, + {value: Int64(-8979123)}, + {value: Uint64(9929)}, + {value: Float32(819293429.192321)}, + {value: Float32(-8979123.546734)}, + {value: Float64(8192934298908979.192321)}, + {value: Float64(-897912398989898.546734)}, + {value: String("FIT SDK")}, + {value: String("")}, + {value: SliceBool([]bool{true, false})}, + {value: SliceUint8([]byte{1, 2})}, + {value: SliceUint8([]uint8{1, 2})}, + {value: SliceInt8([]int8{-19})}, + {value: SliceUint8([]uint8{129})}, + {value: SliceInt16([]int16{1429})}, + {value: SliceInt16([]int16{-429})}, + {value: SliceUint16([]uint16{9929})}, + {value: SliceInt32([]int32{819293429})}, + {value: SliceInt32([]int32{-8979123})}, + {value: SliceUint32([]uint32{9929})}, + {value: SliceString([]string{"supported"})}, + {value: SliceString([]string{})}, + {value: SliceString([]string{""})}, + {value: SliceString([]string{"\x00"})}, + {value: SliceString([]string{"\x00", "\x00"})}, + {value: SliceString([]string{string([]byte{'\x00'})})}, + {value: SliceInt64([]int64{819293429})}, + {value: SliceInt64([]int64{-8979123})}, + {value: SliceUint64([]uint64{9929})}, + {value: SliceFloat32([]float32{819293429.192321})}, + {value: SliceFloat32([]float32{-8979123.546734})}, + {value: SliceFloat64([]float64{8192934298908979.192321})}, + {value: SliceFloat64([]float64{-897912398989898.546734})}, + {value: Value{}, err: ErrTypeNotSupported}, } for i, tc := range tt { for arch := byte(0); arch <= 1; arch++ { t.Run(fmt.Sprintf("[%d] %T(%v))", i, tc.value.Any(), tc.value.Any()), func(t *testing.T) { - var err error - *tc.b, err = tc.value.MarshalAppend(nil, arch) + b, err := tc.value.MarshalAppend(nil, arch) if !errors.Is(err, tc.err) { t.Fatalf("expected err: %v, got: %v", tc.err, err) } @@ -86,12 +83,12 @@ func TestValueMarshalAppend(t *testing.T) { t.Fatalf("marshalWithReflectionForTest: %v", err) } - if len(*tc.b) == 0 && len(buf.Bytes()) == 0 { + if len(b) == 0 && len(buf.Bytes()) == 0 { return } - if diff := cmp.Diff(*tc.b, buf.Bytes()); diff != "" { - fmt.Printf("value: %v, b: %v, buf: %v\n", tc.value.Any(), *tc.b, buf.Bytes()) + if diff := cmp.Diff(b, buf.Bytes()); diff != "" { + fmt.Printf("value: %v, b: %v, buf: %v\n", tc.value.Any(), b, buf.Bytes()) t.Fatal(diff) } From 439812aaf4f39f06214eafa73944d61a894554de Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 10:33:23 +0700 Subject: [PATCH 04/22] remove (Message) WithFieldValues --- encoder/encoder_bench_test.go | 107 ++++++------- encoder/encoder_test.go | 154 +++++++++---------- encoder/validator_test.go | 188 +++++++++++------------ internal/cmd/fitgen/factory/factory.tmpl | 14 -- internal/cmd/testgen/big_activity.go | 108 ++++++------- internal/cmd/testgen/main.go | 21 ++- profile/mesgdef/mesgdef_test.go | 11 +- profile/mesgdef/record_gen_test.go | 10 +- proto/proto.go | 16 -- proto/proto_marshal.go | 6 +- proto/proto_test.go | 29 ---- 11 files changed, 307 insertions(+), 357 deletions(-) diff --git a/encoder/encoder_bench_test.go b/encoder/encoder_bench_test.go index 5ff00d7..0b2212a 100644 --- a/encoder/encoder_bench_test.go +++ b/encoder/encoder_bench_test.go @@ -35,48 +35,49 @@ var DiscardAt = discardAt{} func createFitForBenchmark(recodSize int) *proto.FIT { now := time.Now() - fit := new(proto.FIT) - fit.Messages = make([]proto.Message, 0, recodSize) - fit.Messages = append(fit.Messages, - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - fieldnum.FileIdManufacturer: typedef.ManufacturerBryton, - fieldnum.FileIdProductName: "1901", - fieldnum.FileIdNumber: uint16(0), - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - fieldnum.FileIdSerialNumber: uint32(5122), - }), - factory.CreateMesg(mesgnum.Sport).WithFieldValues(map[byte]any{ - fieldnum.SportSport: typedef.SportCycling, - fieldnum.SportSubSport: typedef.SubSportRoad, - }), - factory.CreateMesg(mesgnum.Activity).WithFieldValues(map[byte]any{ - fieldnum.ActivityTimestamp: datetime.ToUint32(now), - fieldnum.ActivityType: typedef.ActivityTypeCycling, - fieldnum.ActivityTotalTimerTime: uint32(30877.0 * 1000), - fieldnum.ActivityNumSessions: uint16(1), - fieldnum.ActivityEvent: typedef.EventActivity, - }), - factory.CreateMesg(mesgnum.Session).WithFieldValues(map[byte]any{ - fieldnum.SessionTimestamp: datetime.ToUint32(now), - fieldnum.SessionStartTime: datetime.ToUint32(now), - fieldnum.SessionTotalElapsedTime: uint32(30877.0 * 1000), - fieldnum.SessionTotalDistance: uint32(32172.05 * 100), - fieldnum.SessionSport: typedef.SportCycling, - fieldnum.SessionSubSport: typedef.SubSportRoad, - fieldnum.SessionTotalMovingTime: uint32(22079.0 * 1000), - fieldnum.SessionTotalCalories: uint16(12824), - fieldnum.SessionAvgSpeed: uint16(5.98 * 1000), - fieldnum.SessionMaxSpeed: uint16(13.05 * 1000), - fieldnum.SessionMaxAltitude: uint16((504.0 + 500) * 5), - fieldnum.SessionTotalAscent: uint16(909), - fieldnum.SessionTotalDescent: uint16(901), - fieldnum.SessionSwcLat: int32(0), - fieldnum.SessionSwcLong: int32(0), - fieldnum.SessionNecLat: int32(0), - fieldnum.SessionNecLong: int32(0), - }), - ) + fit := &proto.FIT{ + Messages: []proto.Message{ + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProduct).WithValue(uint16(1901)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("Rider 420"), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdNumber).WithValue(uint16(0)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdSerialNumber).WithValue(uint32(5122)), + }}, + {Num: mesgnum.Sport, Fields: []proto.Field{ + factory.CreateField(mesgnum.Sport, fieldnum.SportSport).WithValue(typedef.SportCycling), + factory.CreateField(mesgnum.Sport, fieldnum.SportSubSport).WithValue(typedef.SubSportRoad), + }}, + {Num: mesgnum.Activity, Fields: []proto.Field{ + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityType).WithValue(typedef.ActivityTypeCycling), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTotalTimerTime).WithValue(uint32(30877.0 * 1000)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityNumSessions).WithValue(uint16(1)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityEvent).WithValue(typedef.EventActivity), + }}, + {Num: mesgnum.Session, Fields: []proto.Field{ + factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Session, fieldnum.SessionStartTime).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalElapsedTime).WithValue(uint32(30877.0 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalDistance).WithValue(uint32(32172.05 * 100)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSport).WithValue(typedef.SportCycling), + factory.CreateField(mesgnum.Session, fieldnum.SessionSubSport).WithValue(typedef.SubSportRoad), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalMovingTime).WithValue(uint32(22079.0 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalCalories).WithValue(uint16(12824)), + factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(5.98 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionMaxSpeed).WithValue(uint16(13.05 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionMaxAltitude).WithValue(uint16((504.0 + 500) * 5)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalAscent).WithValue(uint16(909)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalDescent).WithValue(uint16(901)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSwcLat).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSwcLong).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionNecLat).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionNecLong).WithValue(int32(0)), + }}, + }, + } for i := 0; i < recodSize-len(fit.Messages); i++ { now = now.Add(time.Second) // only time is moving forward @@ -95,17 +96,17 @@ func createFitForBenchmark(recodSize int) *proto.FIT { now = now.Add(time.Second) // gap } - record := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - fieldnum.RecordPositionLat: int32(-90481372), - fieldnum.RecordPositionLong: int32(1323227263), - fieldnum.RecordSpeed: uint16(8.33 * 1000), - fieldnum.RecordDistance: uint32(405.81 * 100), - fieldnum.RecordHeartRate: uint8(110), - fieldnum.RecordCadence: uint8(85), - fieldnum.RecordAltitude: uint16((166.0 + 500.0) * 5.0), - fieldnum.RecordTemperature: int8(32), - }) + record := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLat).WithValue(int32(-90481372)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLong).WithValue(int32(1323227263)), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(8.33 * 1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(405.81 * 100)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(110)), + factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(85)), + factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16((166.0 + 500.0) * 5.0)), + factory.CreateField(mesgnum.Record, fieldnum.RecordTemperature).WithValue(int8(32)), + }} if i%200 == 0 { // assume every 200 record hr sensor is not sending any data record.RemoveFieldByNum(fieldnum.RecordHeartRate) diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index f13054a..f8e52b8 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -876,16 +876,16 @@ func TestEncodeMessage(t *testing.T) { }{ { name: "encode message with default header option happy flow", - mesg: factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - }), + mesg: proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + }}, w: fnWriteOK, }, { name: "encode message with big-endian", - mesg: factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - }), + mesg: proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + }}, w: fnWriteOK, opts: []Option{WithBigEndian()}, endianness: bigEndian, @@ -895,9 +895,9 @@ func TestEncodeMessage(t *testing.T) { opts: []Option{ WithNormalHeader(2), }, - mesg: factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - }), + mesg: proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + }}, w: fnWriteOK, }, { @@ -905,9 +905,9 @@ func TestEncodeMessage(t *testing.T) { opts: []Option{ WithCompressedTimestampHeader(), }, - mesg: factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - }), + mesg: proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + }}, w: fnWriteOK, }, { @@ -1053,17 +1053,17 @@ func TestEncodeMessage(t *testing.T) { func TestEncodeMessageWithMultipleLocalMessageType(t *testing.T) { now := time.Now() mesgs := []proto.Message{ - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(time.Second)), - fieldnum.RecordHeartRate: uint8(70), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(2 * time.Second)), - fieldnum.RecordSpeed: uint16(1000), - }), + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(time.Second))), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(70)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), + }}, } t.Run("multiple local mesg type", func(t *testing.T) { @@ -1092,9 +1092,9 @@ func TestEncodeMessageWithMultipleLocalMessageType(t *testing.T) { } // add 4th mesg, header should be 0, reset. - mesg := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - }) + mesg := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + }} buf.Reset() if err := enc.encodeMessage(&mesg); err != nil { t.Fatal(err) @@ -1139,7 +1139,7 @@ func makeEncodeMessagesTableTest() []encodeMessagesTestCase { }, { name: "missing file_id mesg", - mesgs: []proto.Message{factory.CreateMesg(mesgnum.Record)}, + mesgs: []proto.Message{{Num: mesgnum.Record}}, err: ErrMissingFileId, }, } @@ -1183,22 +1183,22 @@ func TestCompressTimestampInHeader(t *testing.T) { { name: "compress timestamp in header happy flow", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdManufacturer: typedef.ManufacturerGarmin, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(time.Second)), // +1s - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(2 * time.Second)), // +2s - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(32 * time.Second)), // +32 rollover - }), + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerGarmin), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(time.Second))), // +1), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(2 * time.Second))), // +2), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(32 * time.Second))), // +32 rollove), + }}, }, headers: []byte{ proto.MesgNormalHeaderMask, // file_id: has no timestamp @@ -1211,19 +1211,19 @@ func TestCompressTimestampInHeader(t *testing.T) { { name: "compress timestamp in header happy flow: roll over occurred exactly after 32 seconds", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdManufacturer: typedef.ManufacturerGarmin, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(32 * time.Second)), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now.Add(33 * time.Second)), - }), + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerGarmin), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(32 * time.Second))), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now.Add(33 * time.Second))), + }}, }, headers: []byte{ proto.MesgNormalHeaderMask, // file_id: has no timestamp @@ -1235,13 +1235,13 @@ func TestCompressTimestampInHeader(t *testing.T) { { name: "timestamp less than DateTimeMin", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdManufacturer: typedef.ManufacturerGarmin, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: uint32(1234), - }), + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerGarmin), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(uint32(1234)), + }}, }, headers: []byte{ proto.MesgNormalHeaderMask, @@ -1251,13 +1251,13 @@ func TestCompressTimestampInHeader(t *testing.T) { { name: "timestamp wrong type not uint32 or typedef.DateTime", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdManufacturer: typedef.ManufacturerGarmin, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: typedef.DateTime(datetime.ToUint32(now)), - }), + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerGarmin), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(typedef.DateTime(datetime.ToUint32(now))), + }}, }, headers: []byte{ proto.MesgNormalHeaderMask, @@ -1267,13 +1267,13 @@ func TestCompressTimestampInHeader(t *testing.T) { { name: "timestamp wrong type not uint32 or typedef.DateTime", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdManufacturer: typedef.ManufacturerGarmin, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - }), - factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: now, // time.Time{} - }), + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerGarmin), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(now), // time.Time{), + }}, }, headers: []byte{ proto.MesgNormalHeaderMask, diff --git a/encoder/validator_test.go b/encoder/validator_test.go index b08ccf0..91aa676 100644 --- a/encoder/validator_test.go +++ b/encoder/validator_test.go @@ -106,18 +106,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "valid message with developer fields happy flow", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Heart Rate", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(fieldnum.RecordHeartRate), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint8), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -166,10 +166,10 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "valid message with developer data index not found in previous message sequence", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - }), + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -184,10 +184,10 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "valid message with field description not found in previous message sequence", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -248,18 +248,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "n developer fields exceed allowed", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Heart Rate", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(fieldnum.RecordHeartRate), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint8), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), + }}, { Num: mesgnum.Record, Fields: []proto.Field{ @@ -321,18 +321,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "valid message with developer fields invalid value", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Heart Rate", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(fieldnum.RecordHeartRate), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint8), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -350,20 +350,20 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "mesg contain developer field value scaled", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Custom Distance", - fieldnum.FieldDescriptionNativeMesgNum: uint16(basetype.Uint16Invalid), - fieldnum.FieldDescriptionNativeFieldNum: uint8(basetype.Uint8Invalid), - fieldnum.FieldDescriptionScale: uint8(100), - fieldnum.FieldDescriptionOffset: int8(0), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint16), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Custom Distance"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(basetype.Uint16Invalid)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(basetype.Uint8Invalid)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionScale).WithValue(uint8(100)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionOffset).WithValue(int8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint16)), + }}, { Num: mesgnum.Record, Fields: []proto.Field{ @@ -382,18 +382,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "mesg contain developer field with native value scaled", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Altitude", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(fieldnum.RecordAltitude), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint16), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Altitude"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordAltitude)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint16)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -411,18 +411,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "mesg contain developer field with unknown native", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "??", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(255), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint16), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("??"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(255)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint16)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), @@ -462,18 +462,18 @@ func TestMessageValidatorValidate(t *testing.T) { { name: "valid message with developer fields has invalid value", mesgs: []proto.Message{ - factory.CreateMesg(mesgnum.DeveloperDataId).WithFieldValues(map[byte]any{ - fieldnum.DeveloperDataIdDeveloperDataIndex: uint8(0), - fieldnum.DeveloperDataIdApplicationId: []byte{0, 1, 2, 3}, - }), - factory.CreateMesg(mesgnum.FieldDescription).WithFieldValues(map[byte]any{ - fieldnum.FieldDescriptionDeveloperDataIndex: uint8(0), - fieldnum.FieldDescriptionFieldDefinitionNumber: uint8(0), - fieldnum.FieldDescriptionFieldName: "Heart Rate", - fieldnum.FieldDescriptionNativeMesgNum: uint16(mesgnum.Record), - fieldnum.FieldDescriptionNativeFieldNum: uint8(fieldnum.RecordHeartRate), - fieldnum.FieldDescriptionFitBaseTypeId: uint8(basetype.Uint8), - }), + {Num: mesgnum.DeveloperDataId, Fields: []proto.Field{ + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.DeveloperDataId, fieldnum.DeveloperDataIdApplicationId).WithValue([]byte{0, 1, 2, 3}), + }}, + {Num: mesgnum.FieldDescription, Fields: []proto.Field{ + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionDeveloperDataIndex).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldDefinitionNumber).WithValue(uint8(0)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFieldName).WithValue("Heart Rate"), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeMesgNum).WithValue(uint16(mesgnum.Record)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionNativeFieldNum).WithValue(uint8(fieldnum.RecordHeartRate)), + factory.CreateField(mesgnum.FieldDescription, fieldnum.FieldDescriptionFitBaseTypeId).WithValue(uint8(basetype.Uint8)), + }}, { Fields: []proto.Field{ factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(time.Now())), diff --git a/internal/cmd/fitgen/factory/factory.tmpl b/internal/cmd/fitgen/factory/factory.tmpl index a87b8f2..6abe0e4 100644 --- a/internal/cmd/fitgen/factory/factory.tmpl +++ b/internal/cmd/fitgen/factory/factory.tmpl @@ -120,11 +120,6 @@ func (f *Factory) CreateMesg(num typedef.MesgNum) proto.Message { return mesg } -{{ template "create_mesg_only_doc" -}} -func (f *Factory) CreateMesgOnly(num typedef.MesgNum) proto.Message { - return proto.Message{Num: num} -} - {{ template "create_field_doc" -}} func (f *Factory) CreateField(mesgNum typedef.MesgNum, num byte) proto.Field { if mesgNum < {{ .LenMesgs }} && mesgs[mesgNum] != nil { @@ -191,11 +186,6 @@ func CreateMesg(num typedef.MesgNum) proto.Message { return std.CreateMesg(num) } -{{- template "create_mesg_only_doc" -}} -func CreateMesgOnly(num typedef.MesgNum) proto.Message{ - return std.CreateMesgOnly(num) -} - {{ template "create_field_doc" -}} func CreateField(mesgNum typedef.MesgNum, num byte) proto.Field { return std.CreateField(mesgNum, num) @@ -220,10 +210,6 @@ func RegisterMesg(mesg proto.Message) error { // using CreateField method. {{ end }} -{{ define "create_mesg_only_doc" }} -// CreateMesgOnly is a syntax sugar for creating proto.Message{Num: num}. -{{ end }} - {{ define "create_field_doc" }} // CreateField creates new field based on defined messages in the factory. If not found, it returns new field with "unknown" name. // diff --git a/internal/cmd/testgen/big_activity.go b/internal/cmd/testgen/big_activity.go index 7475b93..212cacc 100644 --- a/internal/cmd/testgen/big_activity.go +++ b/internal/cmd/testgen/big_activity.go @@ -29,63 +29,65 @@ func createBigActivityFile(ctx context.Context) error { defer f.Close() now := datetime.ToTime(uint32(1062766519)) - fit := new(proto.FIT) - fit.Messages = make([]proto.Message, 0, RecordSize) - fit.Messages = append(fit.Messages, - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - fieldnum.FileIdManufacturer: typedef.ManufacturerBryton, - fieldnum.FileIdProductName: "1901", - fieldnum.FileIdNumber: uint16(0), - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - fieldnum.FileIdSerialNumber: uint32(5122), - }), - factory.CreateMesg(mesgnum.Sport).WithFieldValues(map[byte]any{ - fieldnum.SportSport: typedef.SportCycling, - fieldnum.SportSubSport: typedef.SubSportRoad, - }), - factory.CreateMesg(mesgnum.Activity).WithFieldValues(map[byte]any{ - fieldnum.ActivityTimestamp: datetime.ToUint32(now), - fieldnum.ActivityType: typedef.ActivityTypeCycling, - fieldnum.ActivityTotalTimerTime: uint32(30877.0 * 1000), - fieldnum.ActivityNumSessions: uint16(1), - fieldnum.ActivityEvent: typedef.EventActivity, - }), - factory.CreateMesg(mesgnum.Session).WithFieldValues(map[byte]any{ - fieldnum.SessionTimestamp: datetime.ToUint32(now), - fieldnum.SessionStartTime: datetime.ToUint32(now), - fieldnum.SessionTotalElapsedTime: uint32(30877.0 * 1000), - fieldnum.SessionTotalDistance: uint32(32172.05 * 100), - fieldnum.SessionSport: typedef.SportCycling, - fieldnum.SessionSubSport: typedef.SubSportRoad, - fieldnum.SessionTotalMovingTime: uint32(22079.0 * 1000), - fieldnum.SessionTotalCalories: uint16(12824), - fieldnum.SessionAvgSpeed: uint16(5.98 * 1000), - fieldnum.SessionMaxSpeed: uint16(13.05 * 1000), - fieldnum.SessionMaxAltitude: uint16((504.0 + 500) * 5), - fieldnum.SessionTotalAscent: uint16(909), - fieldnum.SessionTotalDescent: uint16(901), - fieldnum.SessionSwcLat: int32(0), - fieldnum.SessionSwcLong: int32(0), - fieldnum.SessionNecLat: int32(0), - fieldnum.SessionNecLong: int32(0), - }), - ) + fit := &proto.FIT{ + Messages: []proto.Message{ + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProduct).WithValue(uint16(1901)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("Rider 420"), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdNumber).WithValue(uint16(0)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdSerialNumber).WithValue(uint32(5122)), + }}, + {Num: mesgnum.Sport, Fields: []proto.Field{ + factory.CreateField(mesgnum.Sport, fieldnum.SportSport).WithValue(typedef.SportCycling), + factory.CreateField(mesgnum.Sport, fieldnum.SportSubSport).WithValue(typedef.SubSportRoad), + }}, + {Num: mesgnum.Activity, Fields: []proto.Field{ + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityType).WithValue(typedef.ActivityTypeCycling), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTotalTimerTime).WithValue(uint32(30877.0 * 1000)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityNumSessions).WithValue(uint16(1)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityEvent).WithValue(typedef.EventActivity), + }}, + {Num: mesgnum.Session, Fields: []proto.Field{ + factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Session, fieldnum.SessionStartTime).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalElapsedTime).WithValue(uint32(30877.0 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalDistance).WithValue(uint32(32172.05 * 100)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSport).WithValue(typedef.SportCycling), + factory.CreateField(mesgnum.Session, fieldnum.SessionSubSport).WithValue(typedef.SubSportRoad), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalMovingTime).WithValue(uint32(22079.0 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalCalories).WithValue(uint16(12824)), + factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(5.98 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionMaxSpeed).WithValue(uint16(13.05 * 1000)), + factory.CreateField(mesgnum.Session, fieldnum.SessionMaxAltitude).WithValue(uint16((504.0 + 500) * 5)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalAscent).WithValue(uint16(909)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalDescent).WithValue(uint16(901)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSwcLat).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionSwcLong).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionNecLat).WithValue(int32(0)), + factory.CreateField(mesgnum.Session, fieldnum.SessionNecLong).WithValue(int32(0)), + }}, + }, + } n := RecordSize - len(fit.Messages) for i := 0; i < n; i++ { now = now.Add(time.Second) // only time is moving forward - fit.Messages = append(fit.Messages, factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - fieldnum.RecordPositionLat: int32(-90481372), - fieldnum.RecordPositionLong: int32(1323227263), - fieldnum.RecordSpeed: uint16(8.33 * 1000), - fieldnum.RecordDistance: uint32(405.81 * 100), - fieldnum.RecordHeartRate: uint8(110), - fieldnum.RecordCadence: uint8(85), - fieldnum.RecordAltitude: uint16((166.0 + 500.0) * 5.0), - fieldnum.RecordTemperature: int8(32), - })) + record := proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLat).WithValue(int32(-90481372)), + factory.CreateField(mesgnum.Record, fieldnum.RecordPositionLong).WithValue(int32(1323227263)), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(8.33 * 1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(405.81 * 100)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(110)), + factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(85)), + factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16((166.0 + 500.0) * 5.0)), + factory.CreateField(mesgnum.Record, fieldnum.RecordTemperature).WithValue(int8(32)), + }} + fit.Messages = append(fit.Messages, record) } enc := encoder.New(f) diff --git a/internal/cmd/testgen/main.go b/internal/cmd/testgen/main.go index 29f6ca3..641615d 100644 --- a/internal/cmd/testgen/main.go +++ b/internal/cmd/testgen/main.go @@ -62,24 +62,21 @@ func createValidFitOnlyContainFileId(ctx context.Context) error { now := datetime.ToTime(uint32(1062766519)) fit := &proto.FIT{Messages: []proto.Message{ - {Num: typedef.MesgNumFileId, Fields: []proto.Field{ + {Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("something ss"), factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), }}, - {Num: typedef.MesgNumActivity, Fields: []proto.Field{ - factory.CreateField(typedef.MesgNumActivity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), + {Num: mesgnum.Activity, Fields: []proto.Field{ + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(112)), + factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(80)), + factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(Uint16((150 + 500) * 5)), }}, - factory.CreateMesg(typedef.MesgNumRecord).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(now), - fieldnum.RecordHeartRate: uint8(112), - fieldnum.RecordCadence: uint8(80), - // fieldnum.RecordAltitude: float64(150), // input scaled value - fieldnum.RecordAltitude: Uint16((150 + 500) * 5), // input scaled value - // fieldnum.RecordAltitude: uint16((150 + 500) * 5), // input scaled value - // fieldnum.RecordAltitude: "something", // input scaled value - }), }} enc := encoder.New(f) diff --git a/profile/mesgdef/mesgdef_test.go b/profile/mesgdef/mesgdef_test.go index 481c693..45e29a4 100644 --- a/profile/mesgdef/mesgdef_test.go +++ b/profile/mesgdef/mesgdef_test.go @@ -13,6 +13,7 @@ import ( "github.com/muktihari/fit/profile/typedef" "github.com/muktihari/fit/profile/untyped/fieldnum" "github.com/muktihari/fit/profile/untyped/mesgnum" + "github.com/muktihari/fit/proto" ) func TestDefaultOptions(t *testing.T) { @@ -34,9 +35,13 @@ func TestUnsafeCast(t *testing.T) { typedef.AttitudeValidityHwFail, typedef.AttitudeValiditySolutionCoasting, } - mesg := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.AviationAttitudeValidity: attitudeValidities, - }) + mesg := factory.CreateMesg(mesgnum.Record) + for i := range mesg.Fields { + if mesg.Fields[i].Num == fieldnum.AviationAttitudeValidity { + mesg.Fields[i].Value = proto.SliceUint16(attitudeValidities) + break + } + } aviationAttitude := NewAviationAttitude(&mesg) newMesg := aviationAttitude.ToMesg(nil) diff --git a/profile/mesgdef/record_gen_test.go b/profile/mesgdef/record_gen_test.go index 3c9ad4a..336bb63 100644 --- a/profile/mesgdef/record_gen_test.go +++ b/profile/mesgdef/record_gen_test.go @@ -28,9 +28,13 @@ func BenchmarkNewRecord(b *testing.B) { } func BenchmarkRecordToMesg(b *testing.B) { - mesg := factory.CreateMesg(mesgnum.Record).WithFieldValues(map[byte]any{ - fieldnum.RecordTimestamp: datetime.ToUint32(time.Now()), - }) + mesg := factory.CreateMesg(mesgnum.Record) + for i := range mesg.Fields { + if mesg.Fields[i].Num == fieldnum.RecordTimestamp { + mesg.Fields[i].Value = proto.Uint32(datetime.ToUint32(time.Now())) + break + } + } record := NewRecord(&mesg) b.ResetTimer() diff --git a/proto/proto.go b/proto/proto.go index 2c9b69a..a36da9d 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -170,22 +170,6 @@ type Message struct { DeveloperFields []DeveloperField // List of DeveloperField } -// WithFieldValues assigns the values of the targeted fields with the given map, -// where map[byte]any represents the field numbers and their respective values. -// If the Message does not have a corresponding field number match in the Fields, no value will be assigned or added. -func (m Message) WithFieldValues(fieldNumValues map[byte]any) Message { - for i := range m.Fields { - value, ok := fieldNumValues[m.Fields[i].Num] - if !ok { - continue - } - if value != nil { // only accept non-nil value. - m.Fields[i].Value = Any(value) - } - } - return m -} - // FieldByNum returns a pointer to the Field in a Message, if not found return nil. func (m *Message) FieldByNum(num byte) *Field { for i := range m.Fields { diff --git a/proto/proto_marshal.go b/proto/proto_marshal.go index 7e3d1ee..a512530 100644 --- a/proto/proto_marshal.go +++ b/proto/proto_marshal.go @@ -20,7 +20,7 @@ const MaxBytesPerMessage = 1 + (255*255)*2 const MaxBytesPerMessageDefinition = 5 + 1 + (255 * 3) + 1 + (255 * 3) // MarshalAppend appends the FIT format encoding of FileHeader to b, returning the result. -func (h FileHeader) MarshalAppend(b []byte) ([]byte, error) { +func (h *FileHeader) MarshalAppend(b []byte) ([]byte, error) { b = append(b, h.Size, h.ProtocolVersion) b = binary.LittleEndian.AppendUint16(b, h.ProfileVersion) b = binary.LittleEndian.AppendUint32(b, h.DataSize) @@ -32,7 +32,7 @@ func (h FileHeader) MarshalAppend(b []byte) ([]byte, error) { } // MarshalAppend appends the FIT format encoding of MessageDefinition to b, returning the result. -func (m MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { +func (m *MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { b = append(b, m.Header) b = append(b, m.Reserved) b = append(b, m.Architecture) @@ -67,7 +67,7 @@ func (m MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { } // MarshalAppend appends the FIT format encoding of Message to b, returning the result. -func (m Message) MarshalAppend(b []byte) ([]byte, error) { +func (m *Message) MarshalAppend(b []byte) ([]byte, error) { b = append(b, m.Header) var err error diff --git a/proto/proto_test.go b/proto/proto_test.go index c9ddf69..64c6b29 100644 --- a/proto/proto_test.go +++ b/proto/proto_test.go @@ -98,35 +98,6 @@ func TestMessageDefinitionClone(t *testing.T) { } } -func TestMessageWithFieldValues(t *testing.T) { - tt := []struct { - name string - fieldValues map[byte]any - }{ - { - name: "withFieldValues", - fieldValues: map[byte]any{ - fieldnum.RecordSpeed: uint16(1000), - fieldnum.RecordCadence: uint16(100), - }, - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - mesg := factory.CreateMesg(mesgnum.Record) - mesg.WithFieldValues(tc.fieldValues) - for i := range mesg.Fields { - if value, ok := tc.fieldValues[mesg.Fields[i].Num]; ok { - if mesg.Fields[i].Value.Any() != value { - t.Errorf("expected %T(%v), got: %T(%v)", value, value, mesg.Fields[i].Value, mesg.Fields[i].Value) - } - } - } - }) - } -} - func TestMessageFieldByNum(t *testing.T) { sharedField := factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart) From 51e3b700eb8fd597da7f3f84b5d08bce5a6fbefa Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 11:05:26 +0700 Subject: [PATCH 05/22] remove all Clone() method --- decoder/decoder.go | 5 ++- encoder/encoder_test.go | 6 ++- proto/proto.go | 49 ----------------------- proto/proto_test.go | 86 ----------------------------------------- 4 files changed, 8 insertions(+), 138 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index b983fdb..f95ba1f 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -610,7 +610,10 @@ func (d *Decoder) decodeMessageDefinition(header byte) error { d.localMessageDefinitions[localMesgNum] = mesgDef if len(d.options.mesgDefListeners) > 0 { - mesgDef := mesgDef.Clone() // Clone since we don't have control of the object lifecycle outside Decoder. + // Clone since we don't have control of the object lifecycle outside Decoder. + mesgDef := *mesgDef + mesgDef.FieldDefinitions = append(mesgDef.FieldDefinitions[:0:0], mesgDef.FieldDefinitions...) + mesgDef.DeveloperFieldDefinitions = append(mesgDef.DeveloperFieldDefinitions[:0:0], mesgDef.DeveloperFieldDefinitions...) for i := range d.options.mesgDefListeners { d.options.mesgDefListeners[i].OnMesgDef(mesgDef) // blocking or non-blocking depends on listeners' implementation. } diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index f8e52b8..bf49d16 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -1024,7 +1024,9 @@ func TestEncodeMessage(t *testing.T) { factory.CreateField(mesgnum.Record, fieldnum.RecordAltitude).WithValue(uint16((166.0 + 500.0) * 5.0)), }, } - expected := mesg.Clone() + expected := proto.Message{ + Fields: append(mesg.Fields[:0:0], mesg.Fields...), + } enc := New(io.Discard, WithCompressedTimestampHeader(), @@ -1072,7 +1074,7 @@ func TestEncodeMessageWithMultipleLocalMessageType(t *testing.T) { mesgs := append(mesgs[:0:0], mesgs...) for i := range mesgs { - mesgs[i] = mesgs[i].Clone() + mesgs[i].Fields = append(mesgs[i].Fields[:0:0], mesgs[i].Fields...) } buf := new(bytes.Buffer) diff --git a/proto/proto.go b/proto/proto.go index a36da9d..903ef84 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -139,13 +139,6 @@ func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { return mesgDef, nil } -// Clone clones MessageDefinition -func (m MessageDefinition) Clone() MessageDefinition { - m.FieldDefinitions = append(m.FieldDefinitions[:0:0], m.FieldDefinitions...) - m.DeveloperFieldDefinitions = append(m.DeveloperFieldDefinitions[:0:0], m.DeveloperFieldDefinitions...) - return m -} - // FieldDefinition is the definition of the upcoming field within the message's structure. type FieldDefinition struct { Num byte // The field definition number @@ -200,19 +193,6 @@ func (m *Message) RemoveFieldByNum(num byte) { } } -// Clone clones Message. -func (m Message) Clone() Message { - m.Fields = append(m.Fields[:0:0], m.Fields...) - for i := range m.Fields { - m.Fields[i] = m.Fields[i].Clone() - } - m.DeveloperFields = append(m.DeveloperFields[:0:0], m.DeveloperFields...) - for i := range m.DeveloperFields { - m.DeveloperFields[i] = m.DeveloperFields[i].Clone() - } - return m -} - // FieldBase acts as a fundamental representation of a field as defined in the Global FIT Profile. // The value of this representation should not be altered, except in the case of an unknown field. type FieldBase struct { @@ -292,23 +272,6 @@ func convertToInt64(val Value) (int64, bool) { return 0, false } -// Clone clones Field -func (f Field) Clone() Field { - if f.FieldBase == nil { - return f - } - - fieldBase := *f.FieldBase // also include FieldBase, clone is meant to be a deep copy - fieldBase.Components = append(fieldBase.Components[:0:0], fieldBase.Components...) - fieldBase.SubFields = append(fieldBase.SubFields[:0:0], fieldBase.SubFields...) - for i := range fieldBase.SubFields { - fieldBase.SubFields[i] = fieldBase.SubFields[i].Clone() - } - f.FieldBase = &fieldBase - - return f -} - // DeveloperField is a way to add custom data fields to existing messages. Developer Data Fields can be added // to any message at runtime by providing a self-describing FieldDefinition messages prior to that message. // The combination of the DeveloperDataIndex and FieldDefinitionNumber create a unique id for each FieldDescription. @@ -323,11 +286,6 @@ type DeveloperField struct { Value Value } -// Clone clones DeveloperField -func (f DeveloperField) Clone() DeveloperField { - return f -} - // Component is a way of compressing one or more fields into a bit field expressed in a single containing field. // The component can be expanded as a main Field in a Message or to update the value of the destination main Field. type Component struct { @@ -349,13 +307,6 @@ type SubField struct { Components []Component } -// Clone clones SubField -func (s SubField) Clone() SubField { - s.Components = append(s.Components[:0:0], s.Components...) - s.Maps = append(s.Maps[:0:0], s.Maps...) - return s -} - // SubFieldMap is the mapping between SubField and the corresponding main Field in a Message. // When any Field in a Message has Field.Num == RefFieldNum and Field.Value == RefFieldValue, then the SubField containing // this mapping can be interpreted as the main Field's properties (name, scale, type etc.) diff --git a/proto/proto_test.go b/proto/proto_test.go index 64c6b29..d50da93 100644 --- a/proto/proto_test.go +++ b/proto/proto_test.go @@ -10,7 +10,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/muktihari/fit/factory" - "github.com/muktihari/fit/profile/basetype" "github.com/muktihari/fit/profile/typedef" "github.com/muktihari/fit/profile/untyped/fieldnum" "github.com/muktihari/fit/profile/untyped/mesgnum" @@ -78,26 +77,6 @@ func TestFitWithMessages(t *testing.T) { } } -func TestMessageDefinitionClone(t *testing.T) { - mesgDef := proto.MessageDefinition{ - FieldDefinitions: []proto.FieldDefinition{ - {Num: fieldnum.RecordCadence, Size: 1, BaseType: basetype.Uint8}, - {Num: fieldnum.RecordHeartRate, Size: 1, BaseType: basetype.Uint8}, - }, - DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ - {Num: 0, DeveloperDataIndex: 0, Size: 1}, - }, - } - - cloned := mesgDef.Clone() - cloned.FieldDefinitions[0].Num = 100 - cloned.DeveloperFieldDefinitions[0].Num = 100 - - if diff := cmp.Diff(mesgDef, cloned); diff == "" { - t.Fatalf("expected deep cloned, but some data still being referenced.") - } -} - func TestMessageFieldByNum(t *testing.T) { sharedField := factory.CreateField(mesgnum.Event, fieldnum.EventEventType).WithValue(typedef.EventTypeStart) @@ -216,33 +195,6 @@ func TestMessageRemoveFieldByNum(t *testing.T) { } } -func TestMessageClone(t *testing.T) { - mesg := proto.Message{Num: mesgnum.Session, Fields: []proto.Field{ - factory.CreateField(mesgnum.Session, fieldnum.SessionAvgAltitude).WithValue(uint16(1000)), - factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(1000)), - }, - DeveloperFields: []proto.DeveloperField{ - { - Num: 0, - DeveloperDataIndex: 0, - Value: proto.Uint8(1), - }, - {}, - }} - - cloned := mesg.Clone() - cloned.Fields[0].Num = 100 - cloned.DeveloperFields[0].Num = 100 - - if diff := cmp.Diff(mesg, cloned, - cmp.Transformer("Value", func(v proto.Value) any { - return v.Any() - }), - ); diff == "" { - t.Fatalf("expected deep cloned, but some data still being referenced.") - } -} - func TestFieldSubFieldSubtitution(t *testing.T) { tt := []struct { name string @@ -293,41 +245,3 @@ func TestFieldSubFieldSubtitution(t *testing.T) { }) } } - -func TestFieldClone(t *testing.T) { - field := factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed) - - cloned := field.Clone() - cloned.Components[0].Scale = 777 - - if diff := cmp.Diff(field, cloned, - cmp.Transformer("Value", func(v proto.Value) any { - return v.Any() - }), - ); diff == "" { - t.Fatalf("expected deep cloned, but some data still being referenced.") - } - - field = factory.CreateField(mesgnum.Session, fieldnum.SessionTotalCycles) - cloned = field.Clone() - field.SubFields[0].Name = "FIT SDK for Go" - - if diff := cmp.Diff(field, cloned, - cmp.Transformer("Value", func(v proto.Value) any { - return v.Any() - }), - ); diff == "" { - t.Fatalf("should not changed") - } - - field = proto.Field{} - cloned = field.Clone() - - if diff := cmp.Diff(field, cloned, - cmp.Transformer("Value", func(v proto.Value) any { - return v.Any() - }), - ); diff != "" { - t.Fatalf("empty field base, field should be returned as is: %v", diff) - } -} From fc93dee27a2cf44724d8827cb28e53270e8c81f0 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 12:10:32 +0700 Subject: [PATCH 06/22] encoder: add unit test for newMessageDefinition --- encoder/encoder.go | 8 +- encoder/encoder_test.go | 172 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 2 deletions(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index 42b2e94..746a16d 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -191,6 +191,10 @@ func New(w io.Writer, opts ...Option) *Encoder { protocolValidator: new(proto.Validator), localMesgNumLRU: new(lru), buf: make([]byte, 0, 1536), + mesgDef: proto.MessageDefinition{ + FieldDefinitions: make([]proto.FieldDefinition, 255), + DeveloperFieldDefinitions: make([]proto.DeveloperFieldDefinition, 255), + }, } e.Reset(w, opts...) return e @@ -463,7 +467,7 @@ func (e *Encoder) encodeMessage(mesg *proto.Message) (err error) { } } - mesgDef := e.createMessageDefinition(mesg) + mesgDef := e.newMessageDefinition(mesg) if err := e.protocolValidator.ValidateMessageDefinition(mesgDef); err != nil { return err } @@ -530,7 +534,7 @@ func (e *Encoder) compressTimestampIntoHeader(mesg *proto.Message) { mesg.RemoveFieldByNum(proto.FieldNumTimestamp) } -func (e *Encoder) createMessageDefinition(mesg *proto.Message) *proto.MessageDefinition { +func (e *Encoder) newMessageDefinition(mesg *proto.Message) *proto.MessageDefinition { e.mesgDef.Header = proto.MesgDefinitionMask e.mesgDef.Reserved = mesg.Reserved e.mesgDef.Architecture = mesg.Architecture diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index bf49d16..f4af131 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -1300,6 +1300,178 @@ func TestCompressTimestampInHeader(t *testing.T) { } } +func TestNewMessageDefinition(t *testing.T) { + tt := []struct { + name string + mesg *proto.Message + mesgDef *proto.MessageDefinition + }{ + { + name: "fields only with non-array values", + mesg: &proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: proto.Uint8(typedef.FileActivity.Byte())}, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask, + MesgNum: mesgnum.FileId, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.FileIdType, + Size: 1, + BaseType: basetype.Enum, + }, + }, + }, + }, + { + name: "fields only with mesg architecture big-endian", + mesg: func() *proto.Message { + mesg := &proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: proto.Uint8(typedef.FileActivity.Byte())}, + }} + mesg.Architecture = 1 // big-endian + return mesg + }(), + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask, + Architecture: 1, // big-endian + MesgNum: mesgnum.FileId, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.FileIdType, + Size: 1, + BaseType: basetype.Enum, + }, + }, + }, + }, + { + name: "fields only with string value", + mesg: &proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.FileIdProductName, BaseType: basetype.String}, Value: proto.String("FIT SDK Go")}, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask, + MesgNum: mesgnum.FileId, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.FileIdProductName, + Size: 1 * 11, // len("FIT SDK Go") == 10 + '0x00' + BaseType: basetype.String, + }, + }, + }, + }, + { + name: "fields only with array of byte", + mesg: &proto.Message{Num: mesgnum.UserProfile, Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: proto.SliceUint8([]byte{2, 9})}, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + }, + }, + + { + name: "developer fields", + mesg: &proto.Message{Num: mesgnum.UserProfile, + Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: proto.SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []proto.DeveloperField{ + {Num: 0, DeveloperDataIndex: 0, Value: proto.Uint8(1)}, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask | proto.DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ + { + Num: 0, Size: 1, DeveloperDataIndex: 0, + }, + }, + }, + }, + { + name: "developer fields with string value \"FIT SDK Go\", size should be 11", + mesg: &proto.Message{Num: mesgnum.UserProfile, + Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: proto.SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []proto.DeveloperField{ + { + Num: 0, DeveloperDataIndex: 0, Value: proto.String("FIT SDK Go"), + }, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask | proto.DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ + { + Num: 0, Size: 11, DeveloperDataIndex: 0, + }, + }, + }, + }, + { + name: "developer fields with value []uint16{1,2,3}, size should be 3*2 = 6", + mesg: &proto.Message{Num: mesgnum.UserProfile, + Fields: []proto.Field{ + {FieldBase: &proto.FieldBase{Num: fieldnum.UserProfileGlobalId, BaseType: basetype.Byte}, Value: proto.SliceUint8([]byte{2, 9})}, + }, + DeveloperFields: []proto.DeveloperField{ + {Num: 0, DeveloperDataIndex: 0, Value: proto.SliceUint16([]uint16{1, 2, 3})}, + }}, + mesgDef: &proto.MessageDefinition{ + Header: proto.MesgDefinitionMask | proto.DevDataMask, + MesgNum: mesgnum.UserProfile, + FieldDefinitions: []proto.FieldDefinition{ + { + Num: fieldnum.UserProfileGlobalId, + Size: 2, + BaseType: basetype.Byte, + }, + }, + DeveloperFieldDefinitions: []proto.DeveloperFieldDefinition{ + { + Num: 0, Size: 6, DeveloperDataIndex: 0, + }, + }, + }, + }, + } + + for i, tc := range tt { + t.Run(fmt.Sprintf("[%d] %s", i, tc.name), func(t *testing.T) { + mesgDef := (&Encoder{}).newMessageDefinition(tc.mesg) + if diff := cmp.Diff(mesgDef, tc.mesgDef); diff != "" { + t.Fatal(diff) + } + }) + } +} + // bufferAt wraps bytes.Buffer to enable WriteAt for faster encoding. type bufferAt struct{ *bytes.Buffer } From 328efc45b4fde7968967e893a3aff360f41dfde0 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 12:11:32 +0700 Subject: [PATCH 07/22] remove factory CreateMesgOnly --- factory/exported_gen.go | 9 ++------- factory/factory_gen.go | 9 ++------- internal/cmd/fitgen/factory/factory.tmpl | 4 ++-- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/factory/exported_gen.go b/factory/exported_gen.go index 89dc5ab..7cd2478 100755 --- a/factory/exported_gen.go +++ b/factory/exported_gen.go @@ -19,7 +19,7 @@ func StandardFactory() *Factory { return std } // CreateMesg creates new message based on defined messages in the factory. If not found, it returns proto.Message{Num: num}. // // This will create a shallow copy of the Fields, so changing any value declared in Field's FieldBase is prohibited -// (except in case of unknown field). If you want a deep copy of the mesg, use mesg.Clone(). +// (except in case of unknown field). // // NOTE: This method is not used by either the Decoder or the Encoder, and the data will only be populated once upon the first invocation. // Unless you need most of the returned fields, it's recommended create an empty proto.Message{Num: num} then fill only the necessary fields @@ -28,15 +28,10 @@ func CreateMesg(num typedef.MesgNum) proto.Message { return std.CreateMesg(num) } -// CreateMesgOnly is a syntax sugar for creating proto.Message{Num: num}. -func CreateMesgOnly(num typedef.MesgNum) proto.Message { - return std.CreateMesgOnly(num) -} - // CreateField creates new field based on defined messages in the factory. If not found, it returns new field with "unknown" name. // // The returned field contains a pointer reference to FieldBase defined in the factory, so changing any value -// declared in FieldBase is prohibited (except in the case of unknown field). If you want a deep copy, use field.Clone(). +// declared in FieldBase is prohibited (except in the case of unknown field). func CreateField(mesgNum typedef.MesgNum, num byte) proto.Field { return std.CreateField(mesgNum, num) } diff --git a/factory/factory_gen.go b/factory/factory_gen.go index a6cb597..1cff314 100755 --- a/factory/factory_gen.go +++ b/factory/factory_gen.go @@ -51,7 +51,7 @@ var ( // cache for CreateMesg method // CreateMesg creates new message based on defined messages in the factory. If not found, it returns proto.Message{Num: num}. // // This will create a shallow copy of the Fields, so changing any value declared in Field's FieldBase is prohibited -// (except in case of unknown field). If you want a deep copy of the mesg, use mesg.Clone(). +// (except in case of unknown field). // // NOTE: This method is not used by either the Decoder or the Encoder, and the data will only be populated once upon the first invocation. // Unless you need most of the returned fields, it's recommended create an empty proto.Message{Num: num} then fill only the necessary fields @@ -118,15 +118,10 @@ func (f *Factory) CreateMesg(num typedef.MesgNum) proto.Message { return mesg } -// CreateMesgOnly is a syntax sugar for creating proto.Message{Num: num}. -func (f *Factory) CreateMesgOnly(num typedef.MesgNum) proto.Message { - return proto.Message{Num: num} -} - // CreateField creates new field based on defined messages in the factory. If not found, it returns new field with "unknown" name. // // The returned field contains a pointer reference to FieldBase defined in the factory, so changing any value -// declared in FieldBase is prohibited (except in the case of unknown field). If you want a deep copy, use field.Clone(). +// declared in FieldBase is prohibited (except in the case of unknown field). func (f *Factory) CreateField(mesgNum typedef.MesgNum, num byte) proto.Field { if mesgNum < 410 && mesgs[mesgNum] != nil { fieldBase := mesgs[mesgNum][num] diff --git a/internal/cmd/fitgen/factory/factory.tmpl b/internal/cmd/fitgen/factory/factory.tmpl index 6abe0e4..4c8f9ab 100644 --- a/internal/cmd/fitgen/factory/factory.tmpl +++ b/internal/cmd/fitgen/factory/factory.tmpl @@ -203,7 +203,7 @@ func RegisterMesg(mesg proto.Message) error { // CreateMesg creates new message based on defined messages in the factory. If not found, it returns proto.Message{Num: num}. // // This will create a shallow copy of the Fields, so changing any value declared in Field's FieldBase is prohibited -// (except in case of unknown field). If you want a deep copy of the mesg, use mesg.Clone(). +// (except in case of unknown field). // // NOTE: This method is not used by either the Decoder or the Encoder, and the data will only be populated once upon the first invocation. // Unless you need most of the returned fields, it's recommended create an empty proto.Message{Num: num} then fill only the necessary fields @@ -214,7 +214,7 @@ func RegisterMesg(mesg proto.Message) error { // CreateField creates new field based on defined messages in the factory. If not found, it returns new field with "unknown" name. // // The returned field contains a pointer reference to FieldBase defined in the factory, so changing any value -// declared in FieldBase is prohibited (except in the case of unknown field). If you want a deep copy, use field.Clone(). +// declared in FieldBase is prohibited (except in the case of unknown field). {{ end }} {{ define "register_mesg_doc" }} From a76a593963c2bb09a39515537fd3674bd2af1cd9 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 12:19:00 +0700 Subject: [PATCH 08/22] decoder: make Accumulator values private and update docs --- decoder/accumulator.go | 65 ++++++++++++++++++++----------------- decoder/accumulator_test.go | 8 ++--- decoder/decoder_test.go | 5 +-- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/decoder/accumulator.go b/decoder/accumulator.go index 24468f9..5b741b4 100644 --- a/decoder/accumulator.go +++ b/decoder/accumulator.go @@ -8,53 +8,60 @@ import ( "github.com/muktihari/fit/profile/typedef" ) +// Accumulator is value accumulator. type Accumulator struct { - AccumulatedValues []AccumulatedValue // use slice over map since len(values) is relatively small + values []value // use slice over map since len(values) is relatively small } +// NewAccumulator creates new accumulator. func NewAccumulator() *Accumulator { - return &Accumulator{} // No need to make AccumulatedValues as it will be created on append anyway. + return &Accumulator{} } -func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, value uint32) { - for i := range a.AccumulatedValues { - field := &a.AccumulatedValues[i] - if field.MesgNum == mesgNum && field.DestFieldNum == destFieldNum { - field.Value = value - field.Last = value +// Collect collects value, it will either append the value when not exist or replace existing one. +func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, val uint32) { + for i := range a.values { + av := &a.values[i] + if av.mesgNum == mesgNum && av.fieldNum == destFieldNum { + av.value = val + av.last = val return } } - a.AccumulatedValues = append(a.AccumulatedValues, AccumulatedValue{ - MesgNum: mesgNum, - DestFieldNum: destFieldNum, - Value: value, - Last: value, + a.values = append(a.values, value{ + mesgNum: mesgNum, + fieldNum: destFieldNum, + value: val, + last: val, }) } -func (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, value uint32, bits byte) uint32 { - for i := range a.AccumulatedValues { - av := &a.AccumulatedValues[i] - if av.MesgNum == mesgNum && av.DestFieldNum == destFieldNum { - return av.Accumulate(value, bits) +// Accumulate calculates the accumulated value and update accordingly. It returns the original value +// when the corresponding value does not exist. +func (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, val uint32, bits byte) uint32 { + for i := range a.values { + av := &a.values[i] + if av.mesgNum == mesgNum && av.fieldNum == destFieldNum { + return av.accumulate(val, bits) } } - return value + return val } -func (a *Accumulator) Reset() { a.AccumulatedValues = a.AccumulatedValues[:0] } +// Reset resets the accumulator. Tt retains the underlying storage for use by +// future use to reduce memory allocs. +func (a *Accumulator) Reset() { a.values = a.values[:0] } -type AccumulatedValue struct { - MesgNum typedef.MesgNum - DestFieldNum byte - Last uint32 - Value uint32 +type value struct { + mesgNum typedef.MesgNum + fieldNum byte + last uint32 + value uint32 } -func (a *AccumulatedValue) Accumulate(value uint32, bits byte) uint32 { +func (a *value) accumulate(val uint32, bits byte) uint32 { var mask uint32 = (1 << bits) - 1 - a.Value += (value - a.Last) & mask - a.Last = value - return a.Value + a.value += (val - a.last) & mask + a.last = val + return a.value } diff --git a/decoder/accumulator_test.go b/decoder/accumulator_test.go index 85d7399..a9db1bb 100644 --- a/decoder/accumulator_test.go +++ b/decoder/accumulator_test.go @@ -90,13 +90,13 @@ func TestAccumulatorReset(t *testing.T) { accumu := NewAccumulator() accumu.Collect(mesgnum.Record, fieldnum.RecordSpeed, 1000) - if len(accumu.AccumulatedValues) != 1 { - t.Fatalf("expected AccumulatedValues is 1, got: %d", len(accumu.AccumulatedValues)) + if len(accumu.values) != 1 { + t.Fatalf("expected AccumulatedValues is 1, got: %d", len(accumu.values)) } accumu.Reset() - if len(accumu.AccumulatedValues) != 0 { - t.Fatalf("expected AccumulatedValues is 0 after reset, got: %d", len(accumu.AccumulatedValues)) + if len(accumu.values) != 0 { + t.Fatalf("expected AccumulatedValues is 0 after reset, got: %d", len(accumu.values)) } } diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index d254f41..dd629c7 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -887,8 +887,8 @@ func TestNext(t *testing.T) { t.Fatalf("should have next, return false") } - if len(dec.accumulator.AccumulatedValues) != 0 { - t.Fatalf("expected accumulator's AccumulatedValues is 0, got: %d", len(dec.accumulator.AccumulatedValues)) + if len(dec.accumulator.values) != 0 { + t.Fatalf("expected accumulator's AccumulatedValues is 0, got: %d", len(dec.accumulator.values)) } if dec.crc16.Sum16() != 0 { // not necessary since reset every decode header anyway, but let's just add it @@ -2584,6 +2584,7 @@ func TestReset(t *testing.T) { dec.Reset(buf, tc.opts...) if diff := cmp.Diff(dec, tc.dec, + cmp.AllowUnexported(Accumulator{}), cmp.AllowUnexported(options{}), cmp.AllowUnexported(Decoder{}), cmp.AllowUnexported(readBuffer{}), From b6becfcc1afe6b125d33ad7f5ade425444884d07 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 21:32:43 +0700 Subject: [PATCH 09/22] encoder: allocate mesgDef fields and developerFields upfront size 255 --- encoder/encoder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index 746a16d..e29f949 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -192,8 +192,8 @@ func New(w io.Writer, opts ...Option) *Encoder { localMesgNumLRU: new(lru), buf: make([]byte, 0, 1536), mesgDef: proto.MessageDefinition{ - FieldDefinitions: make([]proto.FieldDefinition, 255), - DeveloperFieldDefinitions: make([]proto.DeveloperFieldDefinition, 255), + FieldDefinitions: make([]proto.FieldDefinition, 0, 255), + DeveloperFieldDefinitions: make([]proto.DeveloperFieldDefinition, 0, 255), }, } e.Reset(w, opts...) @@ -539,8 +539,9 @@ func (e *Encoder) newMessageDefinition(mesg *proto.Message) *proto.MessageDefini e.mesgDef.Reserved = mesg.Reserved e.mesgDef.Architecture = mesg.Architecture e.mesgDef.MesgNum = mesg.Num - e.mesgDef.FieldDefinitions = e.mesgDef.FieldDefinitions[:0] + e.mesgDef.DeveloperFieldDefinitions = e.mesgDef.DeveloperFieldDefinitions[:0] + for i := range mesg.Fields { e.mesgDef.FieldDefinitions = append(e.mesgDef.FieldDefinitions, proto.FieldDefinition{ Num: mesg.Fields[i].Num, @@ -554,7 +555,6 @@ func (e *Encoder) newMessageDefinition(mesg *proto.Message) *proto.MessageDefini } e.mesgDef.Header |= proto.DevDataMask - e.mesgDef.DeveloperFieldDefinitions = e.mesgDef.DeveloperFieldDefinitions[:0] for i := range mesg.DeveloperFields { e.mesgDef.DeveloperFieldDefinitions = append(e.mesgDef.DeveloperFieldDefinitions, proto.DeveloperFieldDefinition{ Num: mesg.DeveloperFields[i].Num, From f39e6168fef64936e37d1a5640e565e62444bdb0 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 22:37:35 +0700 Subject: [PATCH 10/22] proto: remove constant MaxBytesPerMessage and MaxBytesPerMessageDefinition --- proto/proto_marshal.go | 8 +------- proto/proto_marshal_test.go | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/proto/proto_marshal.go b/proto/proto_marshal.go index a512530..297390c 100644 --- a/proto/proto_marshal.go +++ b/proto/proto_marshal.go @@ -13,15 +13,9 @@ const littleEndian = 0 // Marshaler should only do one thing: marshaling to its bytes representation, any validation should be done outside. -// Header + ((max n Fields) * (n value)) + ((max n DeveloperFields) * (n value)) -const MaxBytesPerMessage = 1 + (255*255)*2 - -// Header + Reserved + Architecture + MesgNum (2 bytes) + n Fields + (Max n Fields * 3) + n DevFields + (Max n DevFields * 3). -const MaxBytesPerMessageDefinition = 5 + 1 + (255 * 3) + 1 + (255 * 3) - // MarshalAppend appends the FIT format encoding of FileHeader to b, returning the result. func (h *FileHeader) MarshalAppend(b []byte) ([]byte, error) { - b = append(b, h.Size, h.ProtocolVersion) + b = append(b, h.Size, byte(h.ProtocolVersion)) b = binary.LittleEndian.AppendUint16(b, h.ProfileVersion) b = binary.LittleEndian.AppendUint32(b, h.DataSize) b = append(b, h.DataType[:4]...) diff --git a/proto/proto_marshal_test.go b/proto/proto_marshal_test.go index a8a7792..f195fa7 100644 --- a/proto/proto_marshal_test.go +++ b/proto/proto_marshal_test.go @@ -287,7 +287,7 @@ func BenchmarkMessageDefinitionMarshalAppend(b *testing.B) { if err != nil { b.Fatal(err) } - arr := [proto.MaxBytesPerMessageDefinition]byte{} + arr := make([]byte, 6+len(mesg.Fields)*3+len(mesg.DeveloperFields)*3) b.StartTimer() for i := 0; i < b.N; i++ { From 89cdb2722785b13372bdf00dedefc5f7bdc079ca Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 22:41:15 +0700 Subject: [PATCH 11/22] proto: change FileHeader.ProtocolVersion type to proto.Version instead of byte --- cmd/fitactivity/main.go | 8 ++++---- decoder/decoder.go | 2 +- decoder/decoder_test.go | 2 +- encoder/encoder.go | 8 ++++---- encoder/encoder_test.go | 10 +++++----- proto/proto.go | 12 ++++++------ proto/version.go | 12 ++++++------ proto/version_test.go | 2 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/cmd/fitactivity/main.go b/cmd/fitactivity/main.go index b3004c7..b4e5cdf 100644 --- a/cmd/fitactivity/main.go +++ b/cmd/fitactivity/main.go @@ -344,7 +344,7 @@ loop: return err } - fit.FileHeader.ProtocolVersion = byte(latestProtocolVersion(fits)) + fit.FileHeader.ProtocolVersion = latestProtocolVersion(fits) fit.FileHeader.ProfileVersion = latestProfileVersion(fits) for _, subcommand := range subcommands { @@ -1050,14 +1050,14 @@ func formatThousand(v int) string { return result.String() } -func latestProtocolVersion(fits []*proto.FIT) byte { - var version = byte(proto.V1) +func latestProtocolVersion(fits []*proto.FIT) proto.Version { + var version = proto.V1 for i := range fits { if fits[i].FileHeader.ProtocolVersion > version { version = fits[i].FileHeader.ProtocolVersion } } - return byte(proto.Version(version)) + return version } func latestProfileVersion(fits []*proto.FIT) uint16 { diff --git a/decoder/decoder.go b/decoder/decoder.go index f95ba1f..c78faa3 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -481,7 +481,7 @@ func (d *Decoder) decodeFileHeader() error { d.fileHeader = proto.FileHeader{ Size: size, - ProtocolVersion: b[0], + ProtocolVersion: proto.Version(b[0]), ProfileVersion: binary.LittleEndian.Uint16(b[1:3]), DataSize: binary.LittleEndian.Uint32(b[3:7]), DataType: proto.DataTypeFIT, diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index dd629c7..84bcfed 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -483,7 +483,7 @@ func TestCheckIntegrity(t *testing.T) { r: func() io.Reader { h := proto.FileHeader{ Size: 14, - ProtocolVersion: byte(proto.V2), + ProtocolVersion: proto.V2, ProfileVersion: profile.Version, DataSize: 0, DataType: proto.DataTypeFIT, diff --git a/encoder/encoder.go b/encoder/encoder.go index e29f949..1b53140 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -107,7 +107,7 @@ type Option func(o *options) // proto.V1, proto.V2, etc, which the validity is ensured. func WithProtocolVersion(protocolVersion proto.Version) Option { return func(o *options) { - if proto.Validate(byte(protocolVersion)) == nil { + if proto.Validate(protocolVersion) == nil { o.protocolVersion = protocolVersion } } @@ -318,11 +318,11 @@ func (e *Encoder) encodeFileHeader(header *proto.FileHeader) error { } if e.options.protocolVersion != 0 { // Override regardless the value in FileHeader. - header.ProtocolVersion = byte(e.options.protocolVersion) + header.ProtocolVersion = e.options.protocolVersion } else if header.ProtocolVersion == 0 { // Default when not specified in FileHeader. - header.ProtocolVersion = byte(proto.V1) + header.ProtocolVersion = proto.V1 } - e.protocolValidator.SetProtocolVersion(proto.Version(header.ProtocolVersion)) + e.protocolValidator.SetProtocolVersion(header.ProtocolVersion) header.DataType = proto.DataTypeFIT header.CRC = 0 // recalculated diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index f4af131..3e6a487 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -614,7 +614,7 @@ func TestUpdateHeader(t *testing.T) { name: "writeSeeker using stub", header: proto.FileHeader{ Size: 12, - ProtocolVersion: byte(proto.V1), + ProtocolVersion: proto.V1, ProfileVersion: profile.Version, DataType: proto.DataTypeFIT, }, @@ -628,7 +628,7 @@ func TestUpdateHeader(t *testing.T) { expect: func() []byte { h := proto.FileHeader{ Size: 12, - ProtocolVersion: byte(proto.V1), + ProtocolVersion: proto.V1, ProfileVersion: profile.Version, DataType: proto.DataTypeFIT, DataSize: 2, // updated @@ -641,7 +641,7 @@ func TestUpdateHeader(t *testing.T) { name: "writerAt using stub", header: proto.FileHeader{ Size: 12, - ProtocolVersion: byte(proto.V1), + ProtocolVersion: proto.V1, ProfileVersion: profile.Version, DataType: proto.DataTypeFIT, }, @@ -651,7 +651,7 @@ func TestUpdateHeader(t *testing.T) { expect: func() []byte { h := proto.FileHeader{ Size: 12, - ProtocolVersion: byte(proto.V1), + ProtocolVersion: proto.V1, ProfileVersion: profile.Version, DataType: proto.DataTypeFIT, DataSize: 2, // updated @@ -830,7 +830,7 @@ func TestEncodeHeader(t *testing.T) { }, header: proto.FileHeader{ Size: 14, - ProtocolVersion: byte(proto.V1), + ProtocolVersion: proto.V1, ProfileVersion: 2135, DataSize: 136830, DataType: ".FIT", diff --git a/proto/proto.go b/proto/proto.go index 903ef84..ea26b17 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -55,12 +55,12 @@ type FIT struct { // FileHeader is a FIT's FileHeader with either 12 bytes size without CRC or a 14 bytes size with CRC, while 14 bytes size is the preferred size. type FileHeader struct { - Size byte // File header size either 12 (legacy) or 14. - ProtocolVersion byte // The FIT Protocol version which is being used to encode the FIT file. - ProfileVersion uint16 // The FIT Profile Version (associated with data defined in Global FIT Profile). - DataSize uint32 // The size of the messages in bytes (this field will be automatically updated by the encoder) - DataType string // ".FIT" (a string constant) - CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the file header. (this field will be automatically updated by the encoder) + Size byte // File header size either 12 (legacy) or 14. + ProtocolVersion Version // The FIT Protocol version which is being used to encode the FIT file. + ProfileVersion uint16 // The FIT Profile Version (associated with data defined in Global FIT Profile). + DataSize uint32 // The size of the messages in bytes (this field will be automatically updated by the encoder) + DataType string // ".FIT" (a string constant) + CRC uint16 // Cyclic Redundancy Check 16-bit value to ensure the integrity of the file header. (this field will be automatically updated by the encoder) } // MessageDefinition is the definition of the upcoming data messages. diff --git a/proto/version.go b/proto/version.go index 6ff10b7..9f7dea2 100644 --- a/proto/version.go +++ b/proto/version.go @@ -29,19 +29,19 @@ func CreateVersion(major, minor byte) (Version, bool) { } // Validate checks whether given version is a valid version. -func Validate(version byte) error { - if VersionMajor(version) > VersionMajor(byte(Vmax)) { +func Validate(version Version) error { + if VersionMajor(version) > VersionMajor(Vmax) { return ErrProtocolVersionNotSupported } return nil } // VersionMajor returns major value of given version -func VersionMajor(version byte) byte { - return version >> MajorVersionShift +func VersionMajor(version Version) byte { + return byte(version >> MajorVersionShift) } // VersionMinor returns minor value of given version -func VersionMinor(version byte) byte { - return version & MinorVersionMask +func VersionMinor(version Version) byte { + return byte(version & MinorVersionMask) } diff --git a/proto/version_test.go b/proto/version_test.go index ae2e83e..9180ed3 100644 --- a/proto/version_test.go +++ b/proto/version_test.go @@ -52,7 +52,7 @@ func TestCreateVersion(t *testing.T) { func TestValidateVersion(t *testing.T) { tt := []struct { - version byte + version proto.Version err error }{ {version: 32, err: nil}, From 8ac7805b880fd669a064a66d3cd8a35dce263502 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 22:55:50 +0700 Subject: [PATCH 12/22] chore: update Factory interface code docs --- decoder/decoder.go | 3 ++- encoder/validator.go | 3 ++- profile/mesgdef/mesgdef.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index c78faa3..544973d 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -81,7 +81,8 @@ type Decoder struct { // Factory defines a contract that any Factory containing these method can be used by the Decoder. 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 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 } diff --git a/encoder/validator.go b/encoder/validator.go index a0c2427..2353619 100644 --- a/encoder/validator.go +++ b/encoder/validator.go @@ -59,7 +59,8 @@ func defaultValidatorOptions() validatorOptions { // Factory defines a contract that any Factory containing these method can be used by the Encoder's Validator. 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 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 } diff --git a/profile/mesgdef/mesgdef.go b/profile/mesgdef/mesgdef.go index 4b1cd29..a285c81 100644 --- a/profile/mesgdef/mesgdef.go +++ b/profile/mesgdef/mesgdef.go @@ -12,7 +12,8 @@ import ( // Factory defines a contract that any Factory containing these method can be used by mesgdef's structs. 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 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 } From c787d2d9047dc1f5b8ad8ff9b5f3f8977582f60d Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Wed, 11 Sep 2024 23:57:57 +0700 Subject: [PATCH 13/22] decoder: rename ErrNotAFitFile to ErrNotFITFile --- decoder/decoder.go | 6 +++--- decoder/decoder_test.go | 8 ++++---- decoder/raw.go | 4 ++-- decoder/raw_test.go | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index 544973d..fb2abe5 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -30,7 +30,7 @@ func (e errorString) Error() string { return string(e) } const ( // Integrity errors - ErrNotAFitFile = errorString("not a FIT file") + ErrNotFITFile = errorString("not a FIT file") ErrDataSizeZero = errorString("data size zero") ErrCRCChecksumMismatch = errorString("crc checksum mismatch") @@ -464,7 +464,7 @@ func (d *Decoder) decodeFileHeader() error { size := b[0] if size != 12 && size != 14 { // current spec is either 12 or 14 - return fmt.Errorf("file header size [%d] is invalid: %w", size, ErrNotAFitFile) + return fmt.Errorf("file header size [%d] is invalid: %w", size, ErrNotFITFile) } _, _ = d.crc16.Write(b) @@ -477,7 +477,7 @@ func (d *Decoder) decodeFileHeader() error { // PERF: Neither string(b[7:11]) nor assigning proto.DataTypeFIT constant to a variable escape to the heap. if string(b[7:11]) != proto.DataTypeFIT { - return ErrNotAFitFile + return ErrNotFITFile } d.fileHeader = proto.FileHeader{ diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index 84bcfed..967e10a 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -573,7 +573,7 @@ func TestCheckIntegrity(t *testing.T) { return bytes.NewReader(b) }(), n: 1, - err: ErrNotAFitFile, + err: ErrNotFITFile, }, } @@ -981,7 +981,7 @@ func makeDecodeTableTest() []decodeTestCase { return }) }(), - err: ErrNotAFitFile, + err: ErrNotFITFile, }, { name: "decode messages return error", @@ -1124,7 +1124,7 @@ func TestDecodeFileHeader(t *testing.T) { return }) }(), - err: ErrNotAFitFile, + err: ErrNotFITFile, }, { name: "decode header invalid size", @@ -1184,7 +1184,7 @@ func TestDecodeFileHeader(t *testing.T) { return }) }(), - err: ErrNotAFitFile, + err: ErrNotFITFile, }, { name: "decode crc == 0x000", diff --git a/decoder/raw.go b/decoder/raw.go index 511c917..421c1a8 100644 --- a/decoder/raw.go +++ b/decoder/raw.go @@ -107,7 +107,7 @@ func (d *RawDecoder) Decode(r io.Reader, fn func(flag RawFlag, b []byte) error) fileHeaderSize := d.BytesArray[0] if fileHeaderSize != 12 && fileHeaderSize != 14 { - return n, fmt.Errorf("file header's size [%d]: %w", fileHeaderSize, ErrNotAFitFile) + return n, fmt.Errorf("file header's size [%d]: %w", fileHeaderSize, ErrNotFITFile) } nr, err = io.ReadFull(r, d.BytesArray[1:fileHeaderSize]) @@ -117,7 +117,7 @@ func (d *RawDecoder) Decode(r io.Reader, fn func(flag RawFlag, b []byte) error) } if string(d.BytesArray[8:12]) != proto.DataTypeFIT { - return n, ErrNotAFitFile + return n, ErrNotFITFile } fileHeaderDataSize := binary.LittleEndian.Uint32(d.BytesArray[4:8]) diff --git a/decoder/raw_test.go b/decoder/raw_test.go index 58275a4..e6e1fc0 100644 --- a/decoder/raw_test.go +++ b/decoder/raw_test.go @@ -114,7 +114,7 @@ func TestRawDecoderDecode(t *testing.T) { }) }(), fn: fnDecodeRawOK, - err: ErrNotAFitFile, + err: ErrNotFITFile, }, { name: "unexpected EOF when decode header", @@ -146,7 +146,7 @@ func TestRawDecoderDecode(t *testing.T) { }) }(), fn: fnDecodeRawOK, - err: ErrNotAFitFile, + err: ErrNotFITFile, }, { name: "fn FileHeader returns io.EOF", From f053e10b9704f819c25048f16523ad874131c7ea Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 00:41:50 +0700 Subject: [PATCH 14/22] decoder: remove idempotent guarantee for PeekFileHeader and PeekFileId as we might change the behavior later --- decoder/decoder.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index fb2abe5..bc874e5 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -328,8 +328,7 @@ func (d *Decoder) discardMessages() (err 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. +// If we choose to continue, Decode picks up where this left then continue decoding next messages instead of starting from zero. func (d *Decoder) PeekFileHeader() (*proto.FileHeader, error) { if d.err != nil { return nil, d.err @@ -343,8 +342,7 @@ func (d *Decoder) PeekFileHeader() (*proto.FileHeader, 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. +// If we choose to continue, Decode picks up where this left then continue decoding next messages instead of starting from zero. func (d *Decoder) PeekFileId() (*mesgdef.FileId, error) { if d.err != nil { return nil, d.err From fdce53aa290e2ea3e4f5d33ff693709c70929bc6 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 00:44:15 +0700 Subject: [PATCH 15/22] docs: update documentation --- cmd/fitactivity/README.md | 3 +- docs/usage.md | 60 +++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/cmd/fitactivity/README.md b/cmd/fitactivity/README.md index 53e4bd7..4bc8508 100644 --- a/cmd/fitactivity/README.md +++ b/cmd/fitactivity/README.md @@ -167,7 +167,7 @@ Output: ```sh About: - fitactivity is a program to handle FIT files based on provided command. + fitactivity is a program to manage FIT files based on provided command. Usage: fitactivity [command] @@ -228,6 +228,7 @@ Subcommand Flags (only if subcommand is provided): --rdp float64 reduce method: RDP [Ramer-Douglas-Peucker] based on GPS points, epsilon > 0 --distance float64 reduce method: distance interval in meters --time uint32 reduce method: time interval in seconds + remove: (select at least one) --unknown bool remove unknown messages --nums string remove message numbers (value separated by comma) diff --git a/docs/usage.md b/docs/usage.md index a6cd28c..f68bc59 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -344,7 +344,7 @@ You can also use [PeekFileHeader()](#-Peek-FileHeader), [PeekFileId()](#Peek-Fil ### Peek FileHeader -We can verify whether the given file is a FIT file by checking the File Header (first 12-14 bytes). 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. +We can verify whether the given file is a FIT file by checking the File Header (first 12-14 bytes). 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. ```go package main @@ -849,35 +849,32 @@ func main() { now := time.Now() fit := proto.FIT{ Messages: []proto.Message{ - // Use factory.CreateMesg if performance is not your main concern, - // it's slightly slower as it allocates all of the message's fields. - factory.CreateMesg(mesgnum.FileId).WithFieldValues(map[byte]any{ - fieldnum.FileIdType: typedef.FileActivity, - fieldnum.FileIdTimeCreated: datetime.ToUint32(now), - fieldnum.FileIdManufacturer: typedef.ManufacturerBryton, - fieldnum.FileIdProduct: uint16(1901), // Bryton Rider 420 - fieldnum.FileIdProductName: "Bryton Rider 420", - }), - // For better performance, consider using factory.CreateMesgOnly - // or other alternatives listed below, as these only allocate the - // specified fields. However, you can not use WithFieldValues method. - factory.CreateMesgOnly(mesgnum.Activity).WithFields( - factory.CreateField(mesgnum.Activity, fieldnum.ActivityType).WithValue(typedef.ActivityManual.Byte()), - factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), - factory.CreateField(mesgnum.Activity, fieldnum.ActivityNumSessions).WithValue(uint16(1)), - ), - // Alternative #1: Directly compose like this, which is the same as above. - proto.Message{Num: mesgnum.Session}.WithFields( + {Num: mesgnum.FileId, Fields: []proto.Field{ + factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity.Byte()), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerBryton.Uint16()), + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProduct).WithValue(uint16(1901)), // Bryton Rider 420 + factory.CreateField(mesgnum.FileId, fieldnum.FileIdProductName).WithValue("Bryton Rider 420"), + }}, + {Num: mesgnum.Record, Fields: []proto.Field{ + factory.CreateField(mesgnum.Record, fieldnum.RecordTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Record, fieldnum.RecordDistance).WithValue(uint32(100)), + factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).WithValue(uint16(1000)), + factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).WithValue(uint8(78)), + factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).WithValue(uint8(100)), + }}, + {Num: mesgnum.Session, Fields: []proto.Field{ + factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Session, fieldnum.SessionTotalDistance).WithValue(uint32(100)), factory.CreateField(mesgnum.Session, fieldnum.SessionAvgSpeed).WithValue(uint16(1000)), factory.CreateField(mesgnum.Session, fieldnum.SessionAvgCadence).WithValue(uint8(78)), factory.CreateField(mesgnum.Session, fieldnum.SessionAvgHeartRate).WithValue(uint8(100)), - ), - // Alternative #2: Compose like this, which is as performant as - // the two examples above. - proto.Message{Num: mesgnum.Record, Fields: []proto.Field{ - {FieldBase: factory.CreateField(mesgnum.Record, fieldnum.RecordSpeed).FieldBase, Value: proto.Uint16(1000)}, - {FieldBase: factory.CreateField(mesgnum.Record, fieldnum.RecordCadence).FieldBase, Value: proto.Uint8(78)}, - {FieldBase: factory.CreateField(mesgnum.Record, fieldnum.RecordHeartRate).FieldBase, Value: proto.Uint8(100)}, + }}, + {Num: mesgnum.Activity, Fields: []proto.Field{ + factory.CreateField(mesgnum.Activity, fieldnum.ActivityTimestamp).WithValue(datetime.ToUint32(now)), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityType).WithValue(typedef.ActivityManual.Byte()), + factory.CreateField(mesgnum.Activity, fieldnum.ActivityLocalTimestamp).WithValue(datetime.ToUint32(now.Add(7 * time.Hour))), // GMT+7 + factory.CreateField(mesgnum.Activity, fieldnum.ActivityNumSessions).WithValue(uint16(1)), }}, }, } @@ -1147,8 +1144,10 @@ import ( "github.com/muktihari/fit/encoder" "github.com/muktihari/fit/factory" "github.com/muktihari/fit/kit/datetime" + "github.com/muktihari/fit/profile/typedef" "github.com/muktihari/fit/profile/untyped/fieldnum" "github.com/muktihari/fit/profile/untyped/mesgnum" + "github.com/muktihari/fit/proto" ) func main() { @@ -1164,12 +1163,12 @@ func main() { } // Simplified example, writing only this mesg. - mesg := factory.CreateMesgOnly(mesgnum.FileId).WithFields( + mesg := proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ factory.CreateField(mesgnum.FileId, fieldnum.FileIdType).WithValue(typedef.FileActivity.Byte()), factory.CreateField(mesgnum.FileId, fieldnum.FileIdManufacturer).WithValue(typedef.ManufacturerDevelopment.Uint16()), factory.CreateField(mesgnum.FileId, fieldnum.FileIdProduct).WithValue(uint16(0)), factory.CreateField(mesgnum.FileId, fieldnum.FileIdTimeCreated).WithValue(datetime.ToUint32(time.Now())), - ) + }} // Write per message, we can use this to write message as it arrives. // For example, message retrieved from decoder's Listener can be @@ -1180,8 +1179,7 @@ func main() { /* Write more messages */ - // This should be invoked for every sequence of FIT File - // (not every message) to finalize. + // After all messages have been written, invoke this to finalize. if err := streamEnc.SequenceCompleted(); err != nil { panic(err) } From d919b60d672750d49778231c1e5c59d2ab741d83 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 11:23:39 +0700 Subject: [PATCH 16/22] fuzz: ignore mesgDef reserved --- fuzz_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fuzz_test.go b/fuzz_test.go index 160c9e8..7b1ff89 100644 --- a/fuzz_test.go +++ b/fuzz_test.go @@ -134,9 +134,11 @@ func FuzzDecodeEncodeRoundTrip(f *testing.F) { return } - if diff := cmp.Diff(sanitizeOutput(in), sanitizeOutput(encoded)); diff != "" { - fitdump("in", t, in) - fitdump("encoded", t, encoded) + sanitizedIn := sanitizeOutput(in) + sanitizedEncoded := sanitizeOutput(encoded) + if diff := cmp.Diff(sanitizedIn, sanitizedEncoded); diff != "" { + fitdump("in", t, sanitizedIn) + fitdump("encoded", t, sanitizedEncoded) t.Fatal(diff) } }) @@ -236,6 +238,7 @@ func sanitizeOutput(in []byte) []byte { case decoder.RawFlagMesgDef: // the encoder used for test always use localMesgNum 0 b[0] = proto.MesgDefinitionMask + b[1] = 0 // Reserved case decoder.RawFlagMesgData: // the encoder used for test always use localMesgNum 0 b[0] = 0 From 9990347b48ed1df3b973c2b11a2cada4c38e17ae Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 11:25:21 +0700 Subject: [PATCH 17/22] basetype: remove IsInteger() EndianAbility() method, add EndianAbilityMask --- profile/basetype/basetype.go | 138 ++++++++++-------------------- profile/basetype/basetype_test.go | 68 --------------- 2 files changed, 47 insertions(+), 159 deletions(-) diff --git a/profile/basetype/basetype.go b/profile/basetype/basetype.go index 90d1d96..14000ec 100644 --- a/profile/basetype/basetype.go +++ b/profile/basetype/basetype.go @@ -9,13 +9,26 @@ import ( "strconv" ) -// BaseTypeNumMask used to get the index/order of the constants (start from 0, Enum). -// Example: (Sint16 & BaseTypeNumMask) -> 3. -const BaseTypeNumMask = 0x1F - // BaseType is the base of all types used in FIT. +// +// Bits layout: +// - 7 : Endian Ability (0: for single byte data; 1: if base type has endianness) +// - 5–6: Reserved +// - 0–4: Base Type Number type BaseType byte +const ( + // BaseTypeNumMask is used to get the Base Type Number. + // + // Example: (Sint16 & BaseTypeNumMask) -> 3. + BaseTypeNumMask = 0b00011111 + + // EndianAbilityMask is used to get the Endian Ability. + // + // Example: (Sint32 & EndianAbilityMask == EndianAbilityMask) -> true + EndianAbilityMask = 0b10000000 +) + const ( Enum BaseType = 0x00 Sint8 BaseType = 0x01 // 2’s complement format @@ -97,29 +110,6 @@ func FromString(s string) BaseType { return 255 } -// List returns all constants. -func List() []BaseType { - return []BaseType{ - Enum, - Sint8, - Uint8, - Sint16, - Uint16, - Sint32, - Uint32, - String, - Float32, - Float64, - Uint8z, - Uint16z, - Uint32z, - Byte, - Sint64, - Uint64, - Uint64z, - } -} - // String returns string representation of t. func (t BaseType) String() string { switch t { @@ -186,6 +176,31 @@ func (t BaseType) Size() byte { return sizes[t] // PERF: use array to optimize speed since this method is frequently used. } +// Valid checks whether BaseType is valid or not. +func (t BaseType) Valid() bool { + switch t { + case Enum, + Sint8, + Uint8, + Sint16, + Uint16, + Sint32, + Uint32, + String, + Float32, + Float64, + Uint8z, + Uint16z, + Uint32z, + Byte, + Sint64, + Uint64, + Uint64z: + return true + } + return false +} + // GoType returns go equivalent type in string. func (t BaseType) GoType() string { switch t { @@ -219,63 +234,6 @@ func (t BaseType) GoType() string { return "invalid(" + strconv.Itoa(int(t)) + ")" } -// EndianAbility return whether t have endianness. -func (t BaseType) EndianAbility() byte { - switch t { - case Enum, Byte, Uint8, Uint8z: - return 0 - case Sint8: - return 0 - case Sint16: - return 1 - case Uint16, Uint16z: - return 1 - case Sint32: - return 1 - case Uint32, Uint32z: - return 1 - case String: - return 0 - case Float32: - return 1 - case Float64: - return 1 - case Sint64: - return 1 - case Uint64, Uint64z: - return 1 - } - return 0 -} - -func (t BaseType) IsInteger() bool { - switch t { - case Enum, Byte, Uint8, Uint8z: - return true - case Sint8: - return true - case Sint16: - return true - case Uint16, Uint16z: - return true - case Sint32: - return true - case Uint32, Uint32z: - return true - case String: - return false - case Float32: - return false - case Float64: - return false - case Sint64: - return true - case Uint64, Uint64z: - return true - } - return false -} - // Invalid returns invalid value of t. e.g. Byte is 255 (its highest value). func (t BaseType) Invalid() any { switch t { @@ -317,10 +275,10 @@ func (t BaseType) Invalid() any { return "invalid" } -// Valid checks whether BaseType is valid or not. -func (t BaseType) Valid() bool { - switch t { - case Enum, +// List returns all constants. +func List() []BaseType { + return []BaseType{ + Enum, Sint8, Uint8, Sint16, @@ -336,8 +294,6 @@ func (t BaseType) Valid() bool { Byte, Sint64, Uint64, - Uint64z: - return true + Uint64z, } - return false } diff --git a/profile/basetype/basetype_test.go b/profile/basetype/basetype_test.go index e44d3d3..1c9ac49 100644 --- a/profile/basetype/basetype_test.go +++ b/profile/basetype/basetype_test.go @@ -156,74 +156,6 @@ func TestGoType(t *testing.T) { } } -func TestEndianAbility(t *testing.T) { - tt := []struct { - baseType basetype.BaseType - endianess byte - }{ - {baseType: basetype.Sint8, endianess: 0}, - {baseType: basetype.Enum, endianess: 0}, - {baseType: basetype.Byte, endianess: 0}, - {baseType: basetype.Uint8, endianess: 0}, - {baseType: basetype.Uint8z, endianess: 0}, - {baseType: basetype.String, endianess: 0}, - {baseType: basetype.Sint16, endianess: 1}, - {baseType: basetype.Uint16, endianess: 1}, - {baseType: basetype.Uint16z, endianess: 1}, - {baseType: basetype.Sint32, endianess: 1}, - {baseType: basetype.Uint32, endianess: 1}, - {baseType: basetype.Uint32z, endianess: 1}, - {baseType: basetype.Float32, endianess: 1}, - {baseType: basetype.Sint64, endianess: 1}, - {baseType: basetype.Uint64, endianess: 1}, - {baseType: basetype.Uint64z, endianess: 1}, - {baseType: basetype.Float64, endianess: 1}, - {baseType: 255, endianess: 0}, - } - for _, tc := range tt { - t.Run(tc.baseType.String(), func(t *testing.T) { - endianess := tc.baseType.EndianAbility() - if endianess != tc.endianess { - t.Fatalf("expected: %d, got: %d", tc.endianess, endianess) - } - }) - } -} - -func TestIsInteger(t *testing.T) { - tt := []struct { - baseType basetype.BaseType - isInteger bool - }{ - {baseType: basetype.Sint8, isInteger: true}, - {baseType: basetype.Enum, isInteger: true}, - {baseType: basetype.Byte, isInteger: true}, - {baseType: basetype.Uint8, isInteger: true}, - {baseType: basetype.Uint8z, isInteger: true}, - {baseType: basetype.String, isInteger: false}, - {baseType: basetype.Sint16, isInteger: true}, - {baseType: basetype.Uint16, isInteger: true}, - {baseType: basetype.Uint16z, isInteger: true}, - {baseType: basetype.Sint32, isInteger: true}, - {baseType: basetype.Uint32, isInteger: true}, - {baseType: basetype.Uint32z, isInteger: true}, - {baseType: basetype.Float32, isInteger: false}, - {baseType: basetype.Sint64, isInteger: true}, - {baseType: basetype.Uint64, isInteger: true}, - {baseType: basetype.Uint64z, isInteger: true}, - {baseType: basetype.Float64, isInteger: false}, - {baseType: 255, isInteger: false}, - } - for _, tc := range tt { - t.Run(tc.baseType.String(), func(t *testing.T) { - isInteger := tc.baseType.IsInteger() - if isInteger != tc.isInteger { - t.Fatalf("expected: %t, got: %t", tc.isInteger, isInteger) - } - }) - } -} - func TestInvalid(t *testing.T) { tt := []struct { baseType basetype.BaseType From 097b558d05f7d788a76b9df5ec21de4a3fd93051 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 11:44:02 +0700 Subject: [PATCH 18/22] remove: Reserved and Architecture fields from Message --- cmd/fitprint/printer/printer.go | 64 ++++++++++++++++++++------------- decoder/decoder.go | 6 +--- decoder/decoder_test.go | 10 +++--- decoder/raw_test.go | 4 +-- encoder/encoder.go | 14 +++----- encoder/encoder_test.go | 31 ++++++++++------ encoder/validator.go | 2 -- proto/proto.go | 14 ++++---- proto/proto_internal_test.go | 22 ------------ proto/proto_marshal.go | 10 +++--- proto/proto_marshal_test.go | 4 +-- proto/value_marshal.go | 32 ++++++++--------- proto/value_marshal_test.go | 2 +- proto/value_unmarshal.go | 32 ++++++++--------- 14 files changed, 121 insertions(+), 126 deletions(-) diff --git a/cmd/fitprint/printer/printer.go b/cmd/fitprint/printer/printer.go index cbfe53c..1684813 100644 --- a/cmd/fitprint/printer/printer.go +++ b/cmd/fitprint/printer/printer.go @@ -74,6 +74,7 @@ func Print(path string) error { defer p.Close() dec := decoder.New(f, + decoder.WithMesgDefListener(p), decoder.WithMesgListener(p), decoder.WithBroadcastOnly(), decoder.WithIgnoreChecksum(), @@ -127,16 +128,23 @@ File Header: const channelBuffer = 1000 type printer struct { - w io.Writer - poolc chan proto.Message - mesgc chan proto.Message - done chan struct{} - active bool - count int + w io.Writer + localMessageDefinitions [proto.LocalMesgNumMask + 1]proto.MessageDefinition + poolc chan proto.Message + messagec chan message + done chan struct{} + active bool + count int fieldDescriptions []*mesgdef.FieldDescription } +type message struct { + proto.Message + Reserved byte + Architecture byte +} + func New(w io.Writer) *printer { p := &printer{w: w} p.reset() @@ -151,20 +159,20 @@ func New(w io.Writer) *printer { } func (p *printer) loop() { - for mesg := range p.mesgc { - switch mesg.Num { + for m := range p.messagec { + switch m.Num { case mesgnum.FieldDescription: - p.fieldDescriptions = append(p.fieldDescriptions, mesgdef.NewFieldDescription(&mesg)) + p.fieldDescriptions = append(p.fieldDescriptions, mesgdef.NewFieldDescription(&m.Message)) } - p.print(mesg) - p.poolc <- mesg + p.print(m) + p.poolc <- m.Message p.count++ } close(p.done) } func (p *printer) reset() { - p.mesgc = make(chan proto.Message, channelBuffer) + p.messagec = make(chan message, channelBuffer) p.done = make(chan struct{}) p.count = 0 p.active = true @@ -174,7 +182,7 @@ func (p *printer) Wait() { if !p.active { return } - close(p.mesgc) + close(p.messagec) <-p.done p.active = false } @@ -184,6 +192,12 @@ func (p *printer) Close() { close(p.poolc) } +var _ decoder.MesgDefListener = (*printer)(nil) + +func (p *printer) OnMesgDef(mesgDef proto.MessageDefinition) { + p.localMessageDefinitions[proto.LocalMesgNum(mesgDef.Header)] = mesgDef +} + var _ decoder.MesgListener = (*printer)(nil) func (p *printer) OnMesg(mesg proto.Message) { @@ -192,10 +206,10 @@ func (p *printer) OnMesg(mesg proto.Message) { go p.loop() p.active = true } - p.mesgc <- p.prep(mesg) + p.messagec <- p.prep(mesg) } -func (p *printer) prep(mesg proto.Message) proto.Message { +func (p *printer) prep(mesg proto.Message) message { m := <-p.poolc if cap(m.Fields) < len(mesg.Fields) { @@ -212,20 +226,22 @@ func (p *printer) prep(mesg proto.Message) proto.Message { copy(m.DeveloperFields, mesg.DeveloperFields) mesg.DeveloperFields = m.DeveloperFields - return mesg + mesgDef := p.localMessageDefinitions[proto.LocalMesgNum(mesg.Header)] + + return message{Message: mesg, Reserved: mesgDef.Reserved, Architecture: mesgDef.Architecture} } -func (p *printer) print(mesg proto.Message) { - numstr := mesg.Num.String() +func (p *printer) print(m message) { + numstr := m.Num.String() if strings.HasPrefix(numstr, "MesgNumInvalid") { numstr = factory.NameUnknown } fmt.Fprintf(p.w, "%s (num: %d, arch: %d, fields[-]: %d, developerFields[+]: %d) [%d]:\n", - numstr, mesg.Num, mesg.Architecture, len(mesg.Fields), len(mesg.DeveloperFields), p.count) + numstr, m.Num, m.Architecture, len(m.Fields), len(m.DeveloperFields), p.count) - for j := range mesg.Fields { - field := &mesg.Fields[j] + for j := range m.Fields { + field := &m.Fields[j] var ( isDynamicField = false @@ -237,7 +253,7 @@ func (p *printer) print(mesg proto.Message) { units = field.Units ) - if subField := field.SubFieldSubtitution(&mesg); subField != nil { + if subField := field.SubFieldSubtitution(&m.Message); subField != nil { isDynamicField = true name = subField.Name baseType = subField.Type.BaseType() @@ -291,8 +307,8 @@ func (p *printer) print(mesg proto.Message) { ) } - for i := range mesg.DeveloperFields { - devField := &mesg.DeveloperFields[i] + for i := range m.DeveloperFields { + devField := &m.DeveloperFields[i] fieldDesc := p.getFieldDescription(devField.DeveloperDataIndex, devField.Num) if fieldDesc == nil { continue diff --git a/decoder/decoder.go b/decoder/decoder.go index bc874e5..4a8d7ea 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -40,8 +40,6 @@ const ( ErrInvalidBaseType = errorString("invalid basetype") ) -const littleEndian = 0 - // Decoder is FIT file decoder. See New() for details. type Decoder struct { readBuffer *readBuffer // read from io.Reader with buffer without extra copying. @@ -555,7 +553,7 @@ func (d *Decoder) decodeMessageDefinition(header byte) error { mesgDef.Header = header mesgDef.Reserved = b[0] mesgDef.Architecture = b[1] - if mesgDef.Architecture == littleEndian { + if mesgDef.Architecture == proto.LittleEndian { mesgDef.MesgNum = typedef.MesgNum(binary.LittleEndian.Uint16(b[2:4])) } else { mesgDef.MesgNum = typedef.MesgNum(binary.BigEndian.Uint16(b[2:4])) @@ -633,8 +631,6 @@ func (d *Decoder) decodeMessageData(header byte) (err error) { mesg := proto.Message{Num: mesgDef.MesgNum} mesg.Header = header - mesg.Reserved = mesgDef.Reserved - mesg.Architecture = mesgDef.Architecture mesg.Fields = d.fieldsArray[:0] if (header & proto.MesgCompressedHeaderMask) == proto.MesgCompressedHeaderMask { // Compressed Timestamp Message Data diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index 967e10a..590eabc 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -662,7 +662,7 @@ func createFitForTest() (proto.FIT, []byte) { bytesbuffer.Write(b) crc16checker.Write(b) - b, err := mesg.MarshalAppend(nil) + b, err := mesg.MarshalAppend(nil, proto.LittleEndian) if err != nil { panic(err) } @@ -1296,7 +1296,7 @@ func TestDecodeMessageDefinition(t *testing.T) { }, header: proto.MesgDefinitionMask, mesgDef: func() *proto.MessageDefinition { - mesgDef, _ := proto.NewMessageDefinition(&fit.Messages[0]) // file_id + mesgDef, _ := proto.NewMessageDefinition(&fit.Messages[0]) // file_i, proto.LittleEndiand return mesgDef }(), }, @@ -1709,7 +1709,7 @@ func TestDecodeFields(t *testing.T) { }, }, } - mesgb, _ := mesg.MarshalAppend(nil) + mesgb, _ := mesg.MarshalAppend(nil, proto.LittleEndian) mesgb = mesgb[1:] // splice mesg header cur := 0 return fnReader(func(b []byte) (n int, err error) { @@ -1752,7 +1752,7 @@ func TestDecodeFields(t *testing.T) { }, }, } - mesgb, _ := mesg.MarshalAppend(nil) + mesgb, _ := mesg.MarshalAppend(nil, proto.LittleEndian) mesgb = mesgb[1:] // splice mesg header cur := 0 return fnReader(func(b []byte) (n int, err error) { @@ -2650,7 +2650,7 @@ func BenchmarkDecodeMessageData(b *testing.B) { if err != nil { b.Fatal(err) } - mesgb, err := mesg.MarshalAppend(nil) + mesgb, err := mesg.MarshalAppend(nil, proto.LittleEndian) if err != nil { b.Fatalf("marshal binary: %v", err) } diff --git a/decoder/raw_test.go b/decoder/raw_test.go index e6e1fc0..ebd7ba0 100644 --- a/decoder/raw_test.go +++ b/decoder/raw_test.go @@ -322,7 +322,7 @@ func TestRawDecoderDecode(t *testing.T) { }} h := headerForTest() buf, _ := h.MarshalAppend(nil) - mesgb, _ := mesg.MarshalAppend(nil) + mesgb, _ := mesg.MarshalAppend(nil, proto.LittleEndian) buf = append(buf, mesgb...) cur := 0 @@ -393,7 +393,7 @@ func TestRawDecoderDecode(t *testing.T) { mesgDef, _ := proto.NewMessageDefinition(&mesg) mesgDefb, _ := mesgDef.MarshalAppend(nil) buf = append(buf, mesgDefb...) - mesgb, _ := mesg.MarshalAppend(nil) + mesgb, _ := mesg.MarshalAppend(nil, proto.LittleEndian) buf = append(buf, mesgb...) binary.LittleEndian.PutUint32(buf[4:8], uint32(len(buf)-14)) diff --git a/encoder/encoder.go b/encoder/encoder.go index 1b53140..0be7e60 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -34,9 +34,6 @@ const ( type headerOption byte const ( - littleEndian = 0 - bigEndian = 1 - // headerOptionNormal is the default header option. // This option has two sub-option to select from: // 1. LocalMessageTypeZero [Default] @@ -87,7 +84,7 @@ type options struct { func defaultOptions() options { return options{ - endianness: littleEndian, + endianness: proto.LittleEndian, headerOption: headerOptionNormal, writeBufferSize: defaultWriteBufferSize, } @@ -124,7 +121,7 @@ func WithMessageValidator(validator MessageValidator) Option { // WithBigEndian directs the Encoder to encode values in Big-Endian bytes order (default: Little-Endian). func WithBigEndian() Option { - return func(o *options) { o.endianness = bigEndian } + return func(o *options) { o.endianness = proto.BigEndian } } // WithCompressedTimestampHeader directs the Encoder to compress timestamp in header to reduce file size. @@ -436,7 +433,6 @@ func (e *Encoder) encodeMessages(messages []proto.Message) error { // encodeMessage marshals and encodes message definition and its message into w. func (e *Encoder) encodeMessage(mesg *proto.Message) (err error) { mesg.Header = proto.MesgNormalHeaderMask - mesg.Architecture = e.options.endianness if err = e.options.messageValidator.Validate(mesg); err != nil { return fmt.Errorf("message validation failed: %w", err) @@ -490,7 +486,7 @@ func (e *Encoder) encodeMessage(mesg *proto.Message) (err error) { } // At this point, e.buf may grow. Re-assign e.buf in case slice has grown. - e.buf, err = mesg.MarshalAppend(e.buf[:0]) + e.buf, err = mesg.MarshalAppend(e.buf[:0], mesgDef.Architecture) if err != nil { return fmt.Errorf("marshal mesg failed: %w", err) } @@ -536,8 +532,8 @@ func (e *Encoder) compressTimestampIntoHeader(mesg *proto.Message) { func (e *Encoder) newMessageDefinition(mesg *proto.Message) *proto.MessageDefinition { e.mesgDef.Header = proto.MesgDefinitionMask - e.mesgDef.Reserved = mesg.Reserved - e.mesgDef.Architecture = mesg.Architecture + e.mesgDef.Reserved = 0 + e.mesgDef.Architecture = e.options.endianness e.mesgDef.MesgNum = mesg.Num e.mesgDef.FieldDefinitions = e.mesgDef.FieldDefinitions[:0] e.mesgDef.DeveloperFieldDefinitions = e.mesgDef.DeveloperFieldDefinitions[:0] diff --git a/encoder/encoder_test.go b/encoder/encoder_test.go index 3e6a487..ab184ac 100644 --- a/encoder/encoder_test.go +++ b/encoder/encoder_test.go @@ -242,7 +242,7 @@ func TestOptions(t *testing.T) { opts: nil, expected: options{ multipleLocalMessageType: 0, - endianness: 0, + endianness: proto.LittleEndian, messageValidator: NewMessageValidator(), writeBufferSize: defaultWriteBufferSize, }, @@ -258,7 +258,7 @@ func TestOptions(t *testing.T) { }, expected: options{ multipleLocalMessageType: 15, - endianness: 1, + endianness: proto.BigEndian, protocolVersion: proto.V2, messageValidator: fnValidateOK, headerOption: headerOptionNormal, @@ -275,7 +275,7 @@ func TestOptions(t *testing.T) { }, expected: options{ multipleLocalMessageType: 0, - endianness: 1, + endianness: proto.BigEndian, protocolVersion: proto.V2, messageValidator: fnValidateOK, headerOption: headerOptionCompressedTimestamp, @@ -888,7 +888,7 @@ func TestEncodeMessage(t *testing.T) { }}, w: fnWriteOK, opts: []Option{WithBigEndian()}, - endianness: bigEndian, + endianness: proto.BigEndian, }, { name: "encode message with header normal multiple local message type happy flow", @@ -1008,8 +1008,8 @@ func TestEncodeMessage(t *testing.T) { t.Fatalf("message header should not contain Developer Data Flag") } - if tc.mesg.Architecture != tc.endianness { - t.Fatalf("expected endianness: %d, got: %d", tc.endianness, tc.mesg.Architecture) + if enc.mesgDef.Architecture != tc.endianness { + t.Fatalf("expected endianness: %d, got: %d", tc.endianness, enc.mesgDef.Architecture) } }) } @@ -1304,6 +1304,7 @@ func TestNewMessageDefinition(t *testing.T) { tt := []struct { name string mesg *proto.Message + arch byte mesgDef *proto.MessageDefinition }{ { @@ -1329,12 +1330,12 @@ func TestNewMessageDefinition(t *testing.T) { mesg := &proto.Message{Num: mesgnum.FileId, Fields: []proto.Field{ {FieldBase: &proto.FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: proto.Uint8(typedef.FileActivity.Byte())}, }} - mesg.Architecture = 1 // big-endian return mesg }(), + arch: 1, mesgDef: &proto.MessageDefinition{ Header: proto.MesgDefinitionMask, - Architecture: 1, // big-endian + Architecture: proto.BigEndian, MesgNum: mesgnum.FileId, FieldDefinitions: []proto.FieldDefinition{ { @@ -1464,8 +1465,18 @@ func TestNewMessageDefinition(t *testing.T) { for i, tc := range tt { t.Run(fmt.Sprintf("[%d] %s", i, tc.name), func(t *testing.T) { - mesgDef := (&Encoder{}).newMessageDefinition(tc.mesg) - if diff := cmp.Diff(mesgDef, tc.mesgDef); diff != "" { + enc := New(nil) + enc.options.endianness = tc.arch + mesgDef := enc.newMessageDefinition(tc.mesg) + if diff := cmp.Diff(mesgDef, tc.mesgDef, + cmp.Transformer("DeveloperFieldDefinitions", + func(devFields []proto.DeveloperFieldDefinition) []proto.DeveloperFieldDefinition { + if len(devFields) == 0 { + return nil + } + return devFields + }), + ); diff != "" { t.Fatal(diff) } }) diff --git a/encoder/validator.go b/encoder/validator.go index 2353619..88e233e 100644 --- a/encoder/validator.go +++ b/encoder/validator.go @@ -108,8 +108,6 @@ func (v *messageValidator) Reset() { } func (v *messageValidator) Validate(mesg *proto.Message) error { - mesg.Header = proto.MesgNormalHeaderMask // reset default - var valid int for i := range mesg.Fields { field := &mesg.Fields[i] diff --git a/proto/proto.go b/proto/proto.go index ea26b17..5de5ac0 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -29,6 +29,9 @@ const ( // header is 1 byte -> 0bxxxxxxxx CompressedBitShift = 5 // Used for right-shifting the 5 least significant bits (lsb) of compressed time. + LittleEndian = 0 + BigEndian = 1 + DefaultFileHeaderSize byte = 14 // The preferred size is 14 DataTypeFIT string = ".FIT" // FIT is a constant string ".FIT" @@ -87,6 +90,7 @@ const ( ) // NewMessageDefinition returns a new MessageDefinition based on the given Message or an error if one occurs. +// This will set Reserved and Architecture with 0 value. It's up to the caller to change the returning value. // // This serves as a testing helper and is for documentation purposes only. func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { @@ -98,8 +102,8 @@ func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { mesgDef := &MessageDefinition{ Header: MesgDefinitionMask, - Reserved: mesg.Reserved, - Architecture: mesg.Architecture, + Reserved: 0, + Architecture: LittleEndian, MesgNum: mesg.Num, FieldDefinitions: make([]FieldDefinition, 0, len(mesg.Fields)), } @@ -107,7 +111,7 @@ func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { for i := range mesg.Fields { size := Sizeof(mesg.Fields[i].Value) if size > maxValueSize { - return nil, fmt.Errorf("Fields[%d].Value's size should be < %d: %w", + return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", i, maxValueSize, errValueSizeExceed255) } mesgDef.FieldDefinitions = append(mesgDef.FieldDefinitions, FieldDefinition{ @@ -126,7 +130,7 @@ func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { for i := range mesg.DeveloperFields { size := Sizeof(mesg.DeveloperFields[i].Value) if size > maxValueSize { - return nil, fmt.Errorf("Fields[%d].Value's size should be < %d: %w", + return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", i, maxValueSize, errValueSizeExceed255) } mesgDef.DeveloperFieldDefinitions = append(mesgDef.DeveloperFieldDefinitions, DeveloperFieldDefinition{ @@ -157,8 +161,6 @@ type DeveloperFieldDefinition struct { // 3 bits type Message struct { Header byte // Message Header serves to distinguish whether the message is a Normal Data or a Compressed Timestamp Data. Unlike MessageDefinition, Message's Header should not contain Developer Data Flag. Num typedef.MesgNum // Global Message Number defined in Global FIT Profile, except number within range 0xFF00 - 0xFFFE are manufacturer specific number. - Reserved byte // Currently undetermined; the default value is 0. - Architecture byte // Architecture type / Endianness. Fields []Field // List of Field DeveloperFields []DeveloperField // List of DeveloperField } diff --git a/proto/proto_internal_test.go b/proto/proto_internal_test.go index c3edf0f..c3e187d 100644 --- a/proto/proto_internal_test.go +++ b/proto/proto_internal_test.go @@ -60,28 +60,6 @@ func TestNewMessageDefinition(t *testing.T) { }, }, }, - { - name: "fields only with mesg architecture big-endian", - mesg: func() *Message { - mesg := &Message{Num: mesgnum.FileId, Fields: []Field{ - {FieldBase: &FieldBase{Num: fieldnum.FileIdType, BaseType: basetype.Enum}, Value: Uint8(typedef.FileActivity.Byte())}, - }} - mesg.Architecture = 1 // big-endian - return mesg - }(), - mesgDef: &MessageDefinition{ - Header: MesgDefinitionMask, - Architecture: 1, // big-endian - MesgNum: mesgnum.FileId, - FieldDefinitions: []FieldDefinition{ - { - Num: fieldnum.FileIdType, - Size: 1, - BaseType: basetype.Enum, - }, - }, - }, - }, { name: "fields only with string value", mesg: &Message{Num: mesgnum.FileId, Fields: []Field{ diff --git a/proto/proto_marshal.go b/proto/proto_marshal.go index 297390c..5ff4905 100644 --- a/proto/proto_marshal.go +++ b/proto/proto_marshal.go @@ -9,8 +9,6 @@ import ( "fmt" ) -const littleEndian = 0 - // Marshaler should only do one thing: marshaling to its bytes representation, any validation should be done outside. // MarshalAppend appends the FIT format encoding of FileHeader to b, returning the result. @@ -31,7 +29,7 @@ func (m *MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { b = append(b, m.Reserved) b = append(b, m.Architecture) - if m.Architecture == littleEndian { + if m.Architecture == LittleEndian { b = binary.LittleEndian.AppendUint16(b, uint16(m.MesgNum)) } else { b = binary.BigEndian.AppendUint16(b, uint16(m.MesgNum)) @@ -61,12 +59,12 @@ func (m *MessageDefinition) MarshalAppend(b []byte) ([]byte, error) { } // MarshalAppend appends the FIT format encoding of Message to b, returning the result. -func (m *Message) MarshalAppend(b []byte) ([]byte, error) { +func (m *Message) MarshalAppend(b []byte, arch byte) ([]byte, error) { b = append(b, m.Header) var err error for i := range m.Fields { - b, err = m.Fields[i].Value.MarshalAppend(b, m.Architecture) + b, err = m.Fields[i].Value.MarshalAppend(b, arch) if err != nil { return nil, fmt.Errorf("field: [num: %d, value: %v]: %w", m.Fields[i].Num, m.Fields[i].Value.Any(), err) @@ -74,7 +72,7 @@ func (m *Message) MarshalAppend(b []byte) ([]byte, error) { } for i := range m.DeveloperFields { - b, err = m.DeveloperFields[i].Value.MarshalAppend(b, m.Architecture) + b, err = m.DeveloperFields[i].Value.MarshalAppend(b, arch) if err != nil { return nil, fmt.Errorf("developer field: [num: %d, value: %v]: %w", m.DeveloperFields[i].Num, m.DeveloperFields[i].Value.Any(), err) diff --git a/proto/proto_marshal_test.go b/proto/proto_marshal_test.go index f195fa7..f00ac73 100644 --- a/proto/proto_marshal_test.go +++ b/proto/proto_marshal_test.go @@ -243,7 +243,7 @@ func TestMessageMarshaler(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - b, err := tc.mesg.MarshalAppend(nil) + b, err := tc.mesg.MarshalAppend(nil, proto.LittleEndian) if !errors.Is(err, tc.err) { t.Fatalf("expected err: %v, got: %v", tc.err, err) } @@ -316,7 +316,7 @@ func BenchmarkMessageMarshalAppend(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - _, err := mesg.MarshalAppend(buf[:0]) + _, err := mesg.MarshalAppend(buf[:0], proto.LittleEndian) if err != nil { b.Fatal(err) } diff --git a/proto/value_marshal.go b/proto/value_marshal.go index 47a877a..1f87f07 100644 --- a/proto/value_marshal.go +++ b/proto/value_marshal.go @@ -50,7 +50,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { b = append(b, v.SliceUint8()...) return b, nil case TypeInt16: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint16(b, uint16(v.num)) } else { b = binary.BigEndian.AppendUint16(b, uint16(v.num)) @@ -58,7 +58,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceInt16: vals := v.SliceInt16() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint16(b, uint16(vals[i])) } @@ -69,7 +69,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeUint16: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint16(b, uint16(v.num)) } else { b = binary.BigEndian.AppendUint16(b, uint16(v.num)) @@ -77,7 +77,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceUint16: vals := v.SliceUint16() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint16(b, vals[i]) } @@ -88,7 +88,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeInt32: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint32(b, uint32(v.num)) } else { b = binary.BigEndian.AppendUint32(b, uint32(v.num)) @@ -96,7 +96,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceInt32: vals := v.SliceInt32() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint32(b, uint32(vals[i])) } @@ -107,7 +107,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeUint32: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint32(b, uint32(v.num)) } else { b = binary.BigEndian.AppendUint32(b, uint32(v.num)) @@ -115,7 +115,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceUint32: vals := v.SliceUint32() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint32(b, vals[i]) } @@ -126,7 +126,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeInt64: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint64(b, uint64(v.num)) } else { b = binary.BigEndian.AppendUint64(b, uint64(v.num)) @@ -134,7 +134,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceInt64: vals := v.SliceInt64() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint64(b, uint64(vals[i])) } @@ -145,7 +145,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeUint64: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint64(b, v.num) } else { b = binary.BigEndian.AppendUint64(b, v.num) @@ -153,7 +153,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceUint64: vals := v.SliceUint64() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint64(b, vals[i]) } @@ -164,7 +164,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeFloat32: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint32(b, uint32(v.num)) } else { b = binary.BigEndian.AppendUint32(b, uint32(v.num)) @@ -172,7 +172,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceFloat32: vals := v.SliceFloat32() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint32(b, math.Float32bits(vals[i])) } @@ -183,7 +183,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { } return b, nil case TypeFloat64: - if arch == littleEndian { + if arch == LittleEndian { b = binary.LittleEndian.AppendUint64(b, v.num) } else { b = binary.BigEndian.AppendUint64(b, v.num) @@ -191,7 +191,7 @@ func (v Value) MarshalAppend(b []byte, arch byte) ([]byte, error) { return b, nil case TypeSliceFloat64: vals := v.SliceFloat64() - if arch == littleEndian { + if arch == LittleEndian { for i := range vals { b = binary.LittleEndian.AppendUint64(b, math.Float64bits(vals[i])) } diff --git a/proto/value_marshal_test.go b/proto/value_marshal_test.go index 6d8fb14..81493ca 100644 --- a/proto/value_marshal_test.go +++ b/proto/value_marshal_test.go @@ -155,6 +155,6 @@ func BenchmarkValueMarshalAppend(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - _, _ = value.MarshalAppend(buf, littleEndian) + _, _ = value.MarshalAppend(buf, LittleEndian) } } diff --git a/proto/value_unmarshal.go b/proto/value_unmarshal.go index ca0d31e..0cc5e17 100755 --- a/proto/value_unmarshal.go +++ b/proto/value_unmarshal.go @@ -49,7 +49,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 2 vals := make([]int16, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, int16(binary.LittleEndian.Uint16(b[:n]))) } @@ -60,7 +60,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceInt16(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Int16(int16(binary.LittleEndian.Uint16(b))), nil } return Int16(int16(binary.BigEndian.Uint16(b))), nil @@ -68,7 +68,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 2 vals := make([]uint16, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, binary.LittleEndian.Uint16(b[:n])) } @@ -79,7 +79,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceUint16(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Uint16(binary.LittleEndian.Uint16(b)), nil } return Uint16(binary.BigEndian.Uint16(b)), nil @@ -87,7 +87,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 4 vals := make([]int32, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, int32(binary.LittleEndian.Uint32(b[:n]))) } @@ -98,7 +98,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceInt32(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Int32(int32(binary.LittleEndian.Uint32(b))), nil } return Int32(int32(binary.BigEndian.Uint32(b))), nil @@ -106,7 +106,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 4 vals := make([]uint32, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, binary.LittleEndian.Uint32(b[:n])) } @@ -117,7 +117,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceUint32(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Uint32(binary.LittleEndian.Uint32(b)), nil } return Uint32(binary.BigEndian.Uint32(b)), nil @@ -125,7 +125,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 8 vals := make([]int64, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, int64(binary.LittleEndian.Uint64(b[:n]))) } @@ -136,7 +136,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceInt64(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Int64(int64(binary.LittleEndian.Uint64(b))), nil } return Int64(int64(binary.BigEndian.Uint64(b))), nil @@ -144,7 +144,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 8 vals := make([]uint64, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, binary.LittleEndian.Uint64(b[:n])) } @@ -155,7 +155,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceUint64(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Uint64(binary.LittleEndian.Uint64(b)), nil } return Uint64(binary.BigEndian.Uint64(b)), nil @@ -163,7 +163,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 4 vals := make([]float32, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, math.Float32frombits(binary.LittleEndian.Uint32(b[:n]))) } @@ -174,7 +174,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceFloat32(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Float32(math.Float32frombits(binary.LittleEndian.Uint32(b))), nil } return Float32(math.Float32frombits(binary.BigEndian.Uint32(b))), nil @@ -182,7 +182,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType if isArray { const n = 8 vals := make([]float64, 0, len(b)/n) - if arch == littleEndian { + if arch == LittleEndian { for ; len(b) >= n; b = b[n:] { vals = append(vals, math.Float64frombits(binary.LittleEndian.Uint64(b[:n]))) } @@ -193,7 +193,7 @@ func UnmarshalValue(b []byte, arch byte, baseType basetype.BaseType, profileType } return SliceFloat64(vals), nil } - if arch == littleEndian { + if arch == LittleEndian { return Float64(math.Float64frombits(binary.LittleEndian.Uint64(b))), nil } return Float64(math.Float64frombits(binary.BigEndian.Uint64(b))), nil From 5a6553584103cbb566406985be515b3629464e92 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 11:51:53 +0700 Subject: [PATCH 19/22] proto: move LocalMesgNum and NewMessageDefinition functions to bottom --- proto/proto.go | 134 ++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/proto/proto.go b/proto/proto.go index 5de5ac0..941570e 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -76,73 +76,6 @@ type MessageDefinition struct { DeveloperFieldDefinitions []DeveloperFieldDefinition // List of the developer field definition (only if Developer Data Flag is set in Header) } -// LocalMesgNum extracts LocalMesgNum from message header. -func LocalMesgNum(header byte) byte { - if (header & MesgCompressedHeaderMask) == MesgCompressedHeaderMask { - return (header & CompressedLocalMesgNumMask) >> CompressedBitShift - } - return header & LocalMesgNumMask -} - -const ( - errNilMesg = errorString("mesg is nil") - errValueSizeExceed255 = errorString("value's size exceed 255") -) - -// NewMessageDefinition returns a new MessageDefinition based on the given Message or an error if one occurs. -// This will set Reserved and Architecture with 0 value. It's up to the caller to change the returning value. -// -// This serves as a testing helper and is for documentation purposes only. -func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { - if mesg == nil { - return nil, errNilMesg - } - - const maxValueSize = 255 - - mesgDef := &MessageDefinition{ - Header: MesgDefinitionMask, - Reserved: 0, - Architecture: LittleEndian, - MesgNum: mesg.Num, - FieldDefinitions: make([]FieldDefinition, 0, len(mesg.Fields)), - } - - for i := range mesg.Fields { - size := Sizeof(mesg.Fields[i].Value) - if size > maxValueSize { - return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", - i, maxValueSize, errValueSizeExceed255) - } - mesgDef.FieldDefinitions = append(mesgDef.FieldDefinitions, FieldDefinition{ - Num: mesg.Fields[i].Num, - Size: byte(size), - BaseType: mesg.Fields[i].BaseType, - }) - } - - if len(mesg.DeveloperFields) == 0 { - return mesgDef, nil - } - - mesgDef.DeveloperFieldDefinitions = make([]DeveloperFieldDefinition, 0, len(mesg.DeveloperFields)) - mesgDef.Header |= DevDataMask - for i := range mesg.DeveloperFields { - size := Sizeof(mesg.DeveloperFields[i].Value) - if size > maxValueSize { - return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", - i, maxValueSize, errValueSizeExceed255) - } - mesgDef.DeveloperFieldDefinitions = append(mesgDef.DeveloperFieldDefinitions, DeveloperFieldDefinition{ - Num: mesg.DeveloperFields[i].Num, - Size: byte(size), - DeveloperDataIndex: mesg.DeveloperFields[i].DeveloperDataIndex, - }) - } - - return mesgDef, nil -} - // FieldDefinition is the definition of the upcoming field within the message's structure. type FieldDefinition struct { Num byte // The field definition number @@ -316,3 +249,70 @@ type SubFieldMap struct { RefFieldNum byte RefFieldValue int64 } + +// LocalMesgNum extracts LocalMesgNum from message header. +func LocalMesgNum(header byte) byte { + if (header & MesgCompressedHeaderMask) == MesgCompressedHeaderMask { + return (header & CompressedLocalMesgNumMask) >> CompressedBitShift + } + return header & LocalMesgNumMask +} + +const ( + errNilMesg = errorString("mesg is nil") + errValueSizeExceed255 = errorString("value's size exceed 255") +) + +// NewMessageDefinition returns a new MessageDefinition based on the given Message or an error if one occurs. +// This will set Reserved and Architecture with 0 value. It's up to the caller to change the returning value. +// +// This serves as a testing helper and is for documentation purposes only. +func NewMessageDefinition(mesg *Message) (*MessageDefinition, error) { + if mesg == nil { + return nil, errNilMesg + } + + const maxValueSize = 255 + + mesgDef := &MessageDefinition{ + Header: MesgDefinitionMask, + Reserved: 0, + Architecture: LittleEndian, + MesgNum: mesg.Num, + FieldDefinitions: make([]FieldDefinition, 0, len(mesg.Fields)), + } + + for i := range mesg.Fields { + size := Sizeof(mesg.Fields[i].Value) + if size > maxValueSize { + return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", + i, maxValueSize, errValueSizeExceed255) + } + mesgDef.FieldDefinitions = append(mesgDef.FieldDefinitions, FieldDefinition{ + Num: mesg.Fields[i].Num, + Size: byte(size), + BaseType: mesg.Fields[i].BaseType, + }) + } + + if len(mesg.DeveloperFields) == 0 { + return mesgDef, nil + } + + mesgDef.DeveloperFieldDefinitions = make([]DeveloperFieldDefinition, 0, len(mesg.DeveloperFields)) + mesgDef.Header |= DevDataMask + for i := range mesg.DeveloperFields { + size := Sizeof(mesg.DeveloperFields[i].Value) + if size > maxValueSize { + return nil, fmt.Errorf("Fields[%d].Value's size should be <= %d: %w", + i, maxValueSize, errValueSizeExceed255) + } + mesgDef.DeveloperFieldDefinitions = append(mesgDef.DeveloperFieldDefinitions, DeveloperFieldDefinition{ + Num: mesg.DeveloperFields[i].Num, + Size: byte(size), + DeveloperDataIndex: mesg.DeveloperFields[i].DeveloperDataIndex, + }) + } + + return mesgDef, nil +} From b796c2d8b640c731c1b7e865f8c4d3d927284334 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 11:53:13 +0700 Subject: [PATCH 20/22] chore: clean up proto_marshal_test.go code --- proto/proto_marshal_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proto/proto_marshal_test.go b/proto/proto_marshal_test.go index f00ac73..5142603 100644 --- a/proto/proto_marshal_test.go +++ b/proto/proto_marshal_test.go @@ -254,7 +254,7 @@ func TestMessageMarshaler(t *testing.T) { } } -func BenchmarkHeaderMarshalAppend(b *testing.B) { +func BenchmarkFileHeaderMarshalAppend(b *testing.B) { b.StopTimer() header := proto.FileHeader{ Size: 14, @@ -287,11 +287,11 @@ func BenchmarkMessageDefinitionMarshalAppend(b *testing.B) { if err != nil { b.Fatal(err) } - arr := make([]byte, 6+len(mesg.Fields)*3+len(mesg.DeveloperFields)*3) + buf := make([]byte, 6+len(mesg.Fields)*3+len(mesg.DeveloperFields)*3) b.StartTimer() for i := 0; i < b.N; i++ { - _, _ = mesgDef.MarshalAppend(arr[:0]) + _, _ = mesgDef.MarshalAppend(buf[:0]) } } From b75f50d5a8371ba3b77651b69192b43657869772 Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 12:46:22 +0700 Subject: [PATCH 21/22] encoder: simplify encodeCRC --- encoder/encoder.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index 0be7e60..0d88056 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -563,8 +563,7 @@ func (e *Encoder) newMessageDefinition(mesg *proto.Message) *proto.MessageDefini } func (e *Encoder) encodeCRC() error { - b := e.buf[:2] - binary.LittleEndian.PutUint16(b, e.crc16.Sum16()) + b := binary.LittleEndian.AppendUint16(e.buf[:0], e.crc16.Sum16()) n, err := e.w.Write(b) e.n += int64(n) From 1961084b9b318130425efd76a39942bd7555aeca Mon Sep 17 00:00:00 2001 From: Hikmatulloh Hari Mukti Date: Thu, 12 Sep 2024 13:08:09 +0700 Subject: [PATCH 22/22] decoder: reset on Decode, make Next as optional --- decoder/decoder.go | 24 +++++++++++++++--------- docs/usage.md | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index 4a8d7ea..e6aa1a2 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -182,7 +182,8 @@ func WithReadBufferSize(size int) Option { // 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. +// 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() @@ -364,14 +365,14 @@ func (d *Decoder) Next() bool { if !d.sequenceCompleted { return true } - d.reset() // reset values for the next chained FIT file + d.sequenceCompleted = false // err is saved in the func, any exported will call this func anyway. return d.decodeFileHeaderOnce() == nil } // 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: +// method multiple times. For convenience, we can wrap it with the Next() method as follows (optional): // // for dec.Next() { // fit, err := dec.Decode() @@ -393,12 +394,14 @@ func (d *Decoder) Decode() (*proto.FIT, error) { if d.err = d.decodeCRC(); d.err != nil { return nil, d.err } - d.sequenceCompleted = true - return &proto.FIT{ + fit := &proto.FIT{ FileHeader: d.fileHeader, Messages: d.messages, CRC: d.crc, - }, nil + } + d.reset() + d.sequenceCompleted = true + return fit, nil } // Discard discards a single FIT file sequence and returns any error encountered. This method directs the Decoder to @@ -440,6 +443,7 @@ func (d *Decoder) Discard() error { if _, d.err = d.readN(2); d.err != nil { // Discard File CRC return d.err } + d.reset() d.sequenceCompleted = true return d.err } @@ -1009,12 +1013,14 @@ func (d *Decoder) DecodeWithContext(ctx context.Context) (*proto.FIT, error) { if d.err = d.decodeCRC(); d.err != nil { return nil, d.err } - d.sequenceCompleted = true - return &proto.FIT{ + fit := &proto.FIT{ FileHeader: d.fileHeader, Messages: d.messages, CRC: d.crc, - }, nil + } + d.reset() + d.sequenceCompleted = true + return fit, nil } func checkContext(ctx context.Context) error { diff --git a/docs/usage.md b/docs/usage.md index f68bc59..4a919c2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -300,7 +300,7 @@ func main() { ### Decode Chained FIT Files -A single invocation of `Decode()` will process exactly one FIT sequence. To decode chained FIT files, wrap the decode process with a loop and use `dec.Next()` to check whether next sequence of bytes are still a valid FIT sequence. +A single invocation of `Decode()` will process exactly one FIT sequence. 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): ```go ...