Skip to content

Commit

Permalink
feat: add cache in decoder group
Browse files Browse the repository at this point in the history
  • Loading branch information
siyul-park committed Jan 5, 2025
1 parent f07d3a0 commit dd540fc
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 42 deletions.
32 changes: 22 additions & 10 deletions pkg/encoding/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,18 @@ func (a *EncodeAssembler[S, T]) Compile(typ reflect.Type) (Encoder[S, T], error)
return nil, errors.WithStack(ErrUnsupportedType)
}

group := NewEncoderGroup[S, T]()
for _, enc := range encoders {
group.Add(enc)
var enc Encoder[S, T]
if len(encoders) == 1 {
enc = encoders[0]
} else {
group := NewEncoderGroup[S, T]()
for _, enc := range encoders {
group.Add(enc)
}
enc = group
}
a.encoders.Store(typ, group)
return group, nil
a.encoders.Store(typ, enc)
return enc, nil
}

// NewDecodeAssembler creates a new DecodeAssembler instance.
Expand Down Expand Up @@ -138,10 +144,16 @@ func (a *DecodeAssembler[S, T]) Compile(typ reflect.Type) (Decoder[S, unsafe.Poi
return nil, errors.WithStack(ErrUnsupportedType)
}

group := NewDecoderGroup[S, unsafe.Pointer]()
for _, dec := range decoders {
group.Add(dec)
var dec Decoder[S, unsafe.Pointer]
if len(decoders) == 1 {
dec = decoders[0]
} else {
group := NewDecoderGroup[S, unsafe.Pointer]()
for _, dec := range decoders {
group.Add(dec)
}
dec = group
}
a.decoders.Store(typ, group)
return group, nil
a.decoders.Store(typ, dec)
return dec, nil
}
14 changes: 14 additions & 0 deletions pkg/encoding/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package encoding

import (
"errors"
"reflect"
"sync"
)

Expand All @@ -14,6 +15,7 @@ type EncoderGroup[S, T any] struct {
// DecoderGroup manages a group of decoders.
type DecoderGroup[S, T any] struct {
decoders []Decoder[S, T]
cache sync.Map
mu sync.RWMutex
}

Expand Down Expand Up @@ -99,9 +101,21 @@ func (g *DecoderGroup[S, T]) Decode(source S, target T) error {
g.mu.RLock()
defer g.mu.RUnlock()

typ := reflect.TypeOf(source)

var err error
cache, ok := g.cache.Load(typ)
if ok {
if err = cache.(Decoder[S, T]).Decode(source, target); err == nil {
return nil
}
}
for _, dec := range g.decoders {
if dec == cache {
continue
}
if err = dec.Decode(source, target); err == nil {
g.cache.Store(typ, dec)
return nil
} else if !errors.Is(err, ErrUnsupportedType) {
return err
Expand Down
125 changes: 93 additions & 32 deletions pkg/types/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,42 +232,91 @@ func TestMap_Decode(t *testing.T) {
dec.Add(newStringDecoder())
dec.Add(newMapDecoder(dec))

t.Run("nil", func(t *testing.T) {
var decoded map[string]string
err := dec.Decode(nil, &decoded)
assert.NoError(t, err)
})
t.Run("static", func(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var decoded map[string]string
err := dec.Decode(nil, &decoded)
assert.NoError(t, err)
})

t.Run("map", func(t *testing.T) {
source := map[string]string{"foo": "bar"}
v := NewMap(NewString("foo"), NewString("bar"))

t.Run("map", func(t *testing.T) {
source := map[string]string{"foo": "bar"}
v := NewMap(NewString("foo"), NewString("bar"))
var decoded map[string]string
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.Equal(t, source, decoded)
})

var decoded map[string]string
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.Equal(t, source, decoded)
t.Run("struct", func(t *testing.T) {
source := struct {
Foo string `map:"foo"`
Bar string `map:"bar"`
}{
Foo: "foo",
Bar: "bar",
}
v := NewMap(
NewString("foo"), NewString("foo"),
NewString("bar"), NewString("bar"),
)

var decoded struct {
Foo string `map:"foo"`
Bar string `map:"bar"`
}
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.EqualValues(t, source, decoded)
})
})

t.Run("struct", func(t *testing.T) {
source := struct {
Foo string `map:"foo"`
Bar string `map:"bar"`
}{
Foo: "foo",
Bar: "bar",
}
v := NewMap(
NewString("foo"), NewString("foo"),
NewString("bar"), NewString("bar"),
)

var decoded struct {
Foo string `map:"foo"`
Bar string `map:"bar"`
}
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.EqualValues(t, source, decoded)
t.Run("dynamic", func(t *testing.T) {
t.Run("map", func(t *testing.T) {
source := map[any]any{"foo": "bar"}
v := NewMap(NewString("foo"), NewString("bar"))

var decoded map[any]any
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.Equal(t, source, decoded)
})

t.Run("struct", func(t *testing.T) {
source := struct {
Foo any `map:"foo"`
Bar any `map:"bar"`
}{
Foo: "foo",
Bar: "bar",
}
v := NewMap(
NewString("foo"), NewString("foo"),
NewString("bar"), NewString("bar"),
)

var decoded struct {
Foo any `map:"foo"`
Bar any `map:"bar"`
}
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.EqualValues(t, source, decoded)
})

t.Run("any", func(t *testing.T) {
source := map[string]string{"foo": "foo", "bar": "bar"}
v := NewMap(
NewString("foo"), NewString("foo"),
NewString("bar"), NewString("bar"),
)

var decoded any
err := dec.Decode(v, &decoded)
assert.NoError(t, err)
assert.EqualValues(t, source, decoded)
})
})
}

Expand Down Expand Up @@ -459,5 +508,17 @@ func BenchmarkMap_Decode(b *testing.B) {
_ = dec.Decode(v, &decoded)
}
})

b.Run("struct", func(b *testing.B) {
v := NewMap(
NewString("foo"), NewString("foo"),
NewString("bar"), NewString("bar"),
)

for i := 0; i < b.N; i++ {
var decoded any
_ = dec.Decode(v, &decoded)
}
})
})
}

0 comments on commit dd540fc

Please sign in to comment.