From 774e1b1c6e56123cb28c7c21f35853ef33744f38 Mon Sep 17 00:00:00 2001 From: Mukti Date: Tue, 13 Aug 2024 15:47:39 +0700 Subject: [PATCH] perf: filedef.Listener change fileSets data type to array (#354) --- profile/filedef/listener.go | 58 ++++++++++++++++++++------------ profile/filedef/listener_test.go | 26 ++++++++++++++ 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/profile/filedef/listener.go b/profile/filedef/listener.go index fe7ee034..439e9cb2 100644 --- a/profile/filedef/listener.go +++ b/profile/filedef/listener.go @@ -12,7 +12,7 @@ import ( "github.com/muktihari/fit/proto" ) -// Listener is Message Listener. +// Listener is a common file types listener that implement decoder.MesgListener type Listener struct { options options file File @@ -27,20 +27,12 @@ type Listener struct { type FileSets = map[typedef.File]func() File type options struct { - fileSets FileSets + fileSets [256]func() File channelBuffer uint } -func defaultOptions() options { - return options{ - fileSets: PredefinedFileSet(), - channelBuffer: 128, - } -} - -// PredefinedFileSet is a list of default filesets used in listener, it's exported so user can append their own types and register it as an option. -func PredefinedFileSet() FileSets { - return FileSets{ +func defaultFileSets() [256]func() File { + return [256]func() File{ typedef.FileActivity: func() File { return NewActivity() }, typedef.FileActivitySummary: func() File { return NewActivitySummary() }, typedef.FileBloodPressure: func() File { return NewBloodPressure() }, @@ -61,6 +53,22 @@ func PredefinedFileSet() FileSets { } } +func defaultOptions() options { + return options{ + fileSets: defaultFileSets(), + channelBuffer: 128, + } +} + +// PredefinedFileSet is a list of default filesets used in listener, it's exported so user can append their own types and register it as an option. +func PredefinedFileSet() FileSets { + m := make(map[typedef.File]func() File) + for i, v := range defaultFileSets() { + m[typedef.File(i)] = v + } + return m +} + // Option is Listener's option. type Option func(o *options) @@ -70,19 +78,27 @@ func WithChannelBuffer(size uint) Option { } // WithFileSets sets what kind of file listener should listen to, when we encounter a file type that is not listed in fileset, -// that file type will be skipped. This will replace the default filesets registered in listener, if you intend to append your own -// file types, please call PredefinedFileSet() and add your file types. +// that file type will be skipped. This will replace the default listener's filesets, if you intend to append your own +// file types, please call PredefinedFileSet() and add your file type before using this option; or use WithFileFunc instead. func WithFileSets(fileSets FileSets) Option { return func(o *options) { - if o.fileSets != nil { - o.fileSets = fileSets + o.fileSets = [256]func() File{} // Clear all. + for file, fn := range fileSets { + o.fileSets[file] = fn } } } +// WithFileFunc sets File with its File creator function. It overrides the default options. +func WithFileFunc(f typedef.File, fn func() File) Option { + return func(o *options) { o.fileSets[f] = fn } +} + var _ decoder.MesgListener = (*Listener)(nil) -// NewListener creates mesg listener. +// NewListener creates new common file types listener that implement decoder.MesgListener. +// This will handle message conversion from proto.Message received from Decoder into +// mesgdef's structure and group it by its correspoding defined file types. func NewListener(opts ...Option) *Listener { l := &Listener{ options: defaultOptions(), @@ -114,12 +130,12 @@ func (l *Listener) loop() { func (l *Listener) processMesg(mesg proto.Message) { if mesg.Num == mesgnum.FileId { - fileType := typedef.File(mesg.FieldValueByNum(fieldnum.FileIdType).Uint8()) - fnNew, ok := l.options.fileSets[fileType] - if !ok { + fileType := mesg.FieldValueByNum(fieldnum.FileIdType).Uint8() + fn := l.options.fileSets[fileType] + if fn == nil { return } - l.file = fnNew() + l.file = fn() } if l.file == nil { return // No file is created since not defined in fileSets. Skip. diff --git a/profile/filedef/listener_test.go b/profile/filedef/listener_test.go index 2cb66aa8..900eb4a1 100644 --- a/profile/filedef/listener_test.go +++ b/profile/filedef/listener_test.go @@ -14,6 +14,7 @@ import ( "github.com/muktihari/fit/factory" "github.com/muktihari/fit/kit/datetime" "github.com/muktihari/fit/profile/filedef" + "github.com/muktihari/fit/profile/mesgdef" "github.com/muktihari/fit/profile/typedef" "github.com/muktihari/fit/profile/untyped/fieldnum" "github.com/muktihari/fit/profile/untyped/mesgnum" @@ -34,6 +35,13 @@ func createFloat64Comparer() cmp.Option { }) } +type dummyFile struct{} + +func (dummyFile) Add(mesg proto.Message) {} +func (dummyFile) ToFIT(o *mesgdef.Options) proto.FIT { return proto.FIT{} } + +var _ filedef.File = (*dummyFile)(nil) + func TestListenerForSingleFitFile(t *testing.T) { type table struct { name string @@ -129,6 +137,24 @@ func TestListenerForSingleFitFile(t *testing.T) { mesgs: newWorkoutMessageForTest(now), result: filedef.NewWorkout(newWorkoutMessageForTest(now)...), }, + { + name: "replace activity with dummy file; PredefinedFileSet", + mesgs: newActivityMessageForTest(now), + result: new(dummyFile), + options: func() []filedef.Option { + sets := filedef.PredefinedFileSet() + sets[typedef.FileActivity] = func() filedef.File { return new(dummyFile) } + return []filedef.Option{filedef.WithFileSets(sets)} + }(), + }, + { + name: "replace activity with dummy file; WithFileFunc", + mesgs: newActivityMessageForTest(now), + result: new(dummyFile), + options: []filedef.Option{ + filedef.WithFileFunc(typedef.FileActivity, func() filedef.File { return new(dummyFile) }), + }, + }, { name: "listener for not specified fileset, course", options: []filedef.Option{