Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reset decoder when decoding is done to enable reuse of decoder #109

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions benchmarks/decoder/decoder_bench_large_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/mailru/easyjson"
)

func BenchmarkJsonParserDecodeObjLarge(b *testing.B) {
func BenchmarkJSONParserDecodeObjLarge(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
jsonparser.ArrayEach(benchmarks.LargeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
Expand All @@ -26,15 +26,15 @@ func BenchmarkJsonParserDecodeObjLarge(b *testing.B) {
}
}

func BenchmarkJsonIterDecodeObjLarge(b *testing.B) {
func BenchmarkJSONIterDecodeObjLarge(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := benchmarks.LargePayload{}
jsoniter.Unmarshal(benchmarks.LargeFixture, &result)
}
}

func BenchmarkEasyJsonDecodeObjLarge(b *testing.B) {
func BenchmarkEasyJSONDecodeObjLarge(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := benchmarks.LargePayload{}
Expand Down
6 changes: 3 additions & 3 deletions benchmarks/decoder/decoder_bench_medium_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/mailru/easyjson"
)

func BenchmarkJsonIterDecodeObjMedium(b *testing.B) {
func BenchmarkJSONIterDecodeObjMedium(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
result := benchmarks.MediumPayload{}
Expand All @@ -36,14 +36,14 @@ func BenchmarkJSONParserDecodeObjMedium(b *testing.B) {
}
}

func BenchmarkEncodingJsonStructMedium(b *testing.B) {
func BenchmarkEncodingJSONDecodeObjMedium(b *testing.B) {
for i := 0; i < b.N; i++ {
var data = benchmarks.MediumPayload{}
json.Unmarshal(benchmarks.MediumFixture, &data)
}
}

func BenchmarkEasyJsonDecodeObjMedium(b *testing.B) {
func BenchmarkEasyJSONDecodeObjMedium(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := benchmarks.MediumPayload{}
Expand Down
6 changes: 3 additions & 3 deletions benchmarks/decoder/decoder_bench_small_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/mailru/easyjson"
)

func BenchmarkJSONDecodeObjSmall(b *testing.B) {
func BenchmarkEncodingJSONDecodeObjSmall(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
result := benchmarks.SmallPayload{}
Expand All @@ -36,15 +36,15 @@ func BenchmarkJSONParserSmall(b *testing.B) {
}
}

func BenchmarkJsonIterDecodeObjSmall(b *testing.B) {
func BenchmarkJSONIterDecodeObjSmall(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
result := benchmarks.SmallPayload{}
jsoniter.Unmarshal(benchmarks.SmallFixture, &result)
}
}

func BenchmarkEasyJsonDecodeObjSmall(b *testing.B) {
func BenchmarkEasyJSONDecodeObjSmall(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := benchmarks.SmallPayload{}
Expand Down
36 changes: 27 additions & 9 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ import (
// overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can.
func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
dec := borrowDecoder(nil, 0)

defer dec.Release()

dec.data = make([]byte, len(data))
copy(dec.data, data)
dec.length = len(data)

_, err := dec.decodeArray(v)
if err != nil {
return err
}

if dec.err != nil {
return dec.err
}

return nil
}

Expand All @@ -35,14 +40,18 @@ func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
// overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can.
func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error {
dec := borrowDecoder(nil, 0)

defer dec.Release()

dec.data = make([]byte, len(data))
copy(dec.data, data)
dec.length = len(data)

_, err := dec.decodeObject(v)
if err != nil {
return err
}

if dec.err != nil {
return dec.err
}
Expand Down Expand Up @@ -215,11 +224,14 @@ func Unmarshal(data []byte, v interface{}) error {
default:
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
}
defer dec.Release()
if err != nil {
return err

if err == nil {
err = dec.err
}
return dec.err

dec.Release()

return err
}

// UnmarshalerJSONObject is the interface to implement to decode a JSON Object.
Expand Down Expand Up @@ -257,7 +269,9 @@ func (dec *Decoder) Decode(v interface{}) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}

var err error

switch vt := v.(type) {
case *string:
err = dec.decodeString(vt)
Expand Down Expand Up @@ -312,20 +326,24 @@ func (dec *Decoder) Decode(v interface{}) error {
case **bool:
err = dec.decodeBoolNull(vt)
case UnmarshalerJSONObject:
_, err = dec.decodeObject(vt)
dec.cursor, err = dec.decodeObject(vt)
case UnmarshalerJSONArray:
_, err = dec.decodeArray(vt)
dec.cursor, err = dec.decodeArray(vt)
case *EmbeddedJSON:
err = dec.decodeEmbeddedJSON(vt)
case *interface{}:
err = dec.decodeInterface(vt)
default:
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
}
if err != nil {
return err

if err == nil {
err = dec.err
}
return dec.err

dec.reset()

return err
}

// Non exported
Expand Down
7 changes: 6 additions & 1 deletion decode_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
_, err := dec.decodeArray(v)

var err error
dec.cursor, err = dec.decodeArray(v)

dec.reset()

return err
}
func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
Expand Down
6 changes: 5 additions & 1 deletion decode_bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ func (dec *Decoder) DecodeBool(v *bool) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeBool(v)

err := dec.decodeBool(v)
dec.reset()

return err
}
func (dec *Decoder) decodeBool(v *bool) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down
1 change: 1 addition & 0 deletions decode_bool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ func TestDecoderBoolDecoderAPI(t *testing.T) {
func TestDecoderBoolPoolError(t *testing.T) {
v := true
dec := NewDecoder(nil)
dec.reset()
dec.Release()
defer func() {
err := recover()
Expand Down
24 changes: 24 additions & 0 deletions decode_embedded_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type Request struct {
Expand Down Expand Up @@ -178,3 +179,26 @@ func TestDecodeEmbeededJSONNil2(t *testing.T) {
assert.NotNil(t, err, `err should not be nil a nil pointer is given`)
assert.IsType(t, InvalidUnmarshalError(""), err, `err should not be of type InvalidUnmarshalError`)
}

func TestDecoderReuse(t *testing.T) {
r := strings.NewReader(`{"foo":"bar"}{"foo":"baz"}{"foo":"world"}`)

dec := NewDecoder(r)
var ej EmbeddedJSON

dec.Decode(&ej)

require.Equal(t, `{"foo":"bar"}`, string(ej))

ej = ej[:0]

dec.Decode(&ej)

assert.Equal(t, `{"foo":"baz"}`, string(ej))

ej = ej[:0]

dec.Decode(&ej)

assert.Equal(t, `{"foo":"world"}`, string(ej))
}
3 changes: 3 additions & 0 deletions decode_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ func (dec *Decoder) DecodeInterface(i *interface{}) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}

err := dec.decodeInterface(i)
dec.reset()

return err
}

Expand Down
1 change: 1 addition & 0 deletions decode_interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ func TestUnmarshalInterfaceError(t *testing.T) {
func TestDecodeInterfacePoolError(t *testing.T) {
result := interface{}(1)
dec := NewDecoder(nil)
dec.reset()
dec.Release()
defer func() {
err := recover()
Expand Down
12 changes: 10 additions & 2 deletions decode_number_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ func (dec *Decoder) DecodeFloat64(v *float64) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeFloat64(v)

err := dec.decodeFloat64(v)
dec.reset()

return err
}
func (dec *Decoder) decodeFloat64(v *float64) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down Expand Up @@ -215,7 +219,11 @@ func (dec *Decoder) DecodeFloat32(v *float32) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeFloat32(v)

err := dec.decodeFloat32(v)
dec.reset()

return err
}
func (dec *Decoder) decodeFloat32(v *float32) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down
2 changes: 2 additions & 0 deletions decode_number_float_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ func TestDecoderFloat64(t *testing.T) {
t.Run("pool-error", func(t *testing.T) {
result := float64(1)
dec := NewDecoder(nil)
dec.reset()
dec.Release()
defer func() {
err := recover()
Expand Down Expand Up @@ -931,6 +932,7 @@ func TestDecoderFloat32(t *testing.T) {
t.Run("pool-error", func(t *testing.T) {
result := float32(1)
dec := NewDecoder(nil)
dec.reset()
dec.Release()
defer func() {
err := recover()
Expand Down
30 changes: 25 additions & 5 deletions decode_number_int.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ func (dec *Decoder) DecodeInt(v *int) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeInt(v)

err := dec.decodeInt(v)
dec.reset()

return err
}
func (dec *Decoder) decodeInt(v *int) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down Expand Up @@ -119,7 +123,11 @@ func (dec *Decoder) DecodeInt16(v *int16) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeInt16(v)

err := dec.decodeInt16(v)
dec.reset()

return err
}
func (dec *Decoder) decodeInt16(v *int16) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down Expand Up @@ -351,7 +359,11 @@ func (dec *Decoder) DecodeInt8(v *int8) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeInt8(v)

err := dec.decodeInt8(v)
dec.reset()

return err
}
func (dec *Decoder) decodeInt8(v *int8) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down Expand Up @@ -582,7 +594,11 @@ func (dec *Decoder) DecodeInt32(v *int32) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeInt32(v)

err := dec.decodeInt32(v)
dec.reset()

return err
}
func (dec *Decoder) decodeInt32(v *int32) error {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
Expand Down Expand Up @@ -812,7 +828,11 @@ func (dec *Decoder) DecodeInt64(v *int64) error {
if dec.isPooled == 1 {
panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
}
return dec.decodeInt64(v)

err := dec.decodeInt64(v)
dec.reset()

return err
}

func (dec *Decoder) decodeInt64(v *int64) error {
Expand Down
Loading