Skip to content

Commit

Permalink
perf: filedef.Listener change fileSets data type to array (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
muktihari authored Aug 13, 2024
1 parent 9c8e76f commit 774e1b1
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 21 deletions.
58 changes: 37 additions & 21 deletions profile/filedef/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() },
Expand All @@ -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)

Expand All @@ -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(),
Expand Down Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions profile/filedef/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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{
Expand Down

0 comments on commit 774e1b1

Please sign in to comment.