gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit f2cc13a668caf474b5d5806c7f1adbbe4ce28524
parent 73600a98c556ff2da24a90f556188b676eaaff31
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Thu, 20 Dec 2018 17:31:23 +0800

Merge pull request #93 from francoispqt/update/optimize-alloc-marshal

create new buffer before releasing only when using marshal syntax 
Diffstat:
Mencode.go | 21+++++++++++++++++----
Mencode_bool_test.go | 2+-
Mencode_embedded_json_test.go | 2+-
Mencode_interface_test.go | 2+-
Mencode_number_test.go | 10+++++-----
Mencode_object.go | 2+-
Mencode_object_test.go | 4++--
Mencode_pool.go | 2+-
Mencode_pool_test.go | 31+++++++++++++++++++++++++++++++
Mencode_sqlnull_test.go | 8++++----
Mencode_string_test.go | 2+-
Mencode_time_test.go | 2+-
12 files changed, 66 insertions(+), 22 deletions(-)

diff --git a/encode.go b/encode.go @@ -34,7 +34,12 @@ func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) { enc.writeByte('[') v.(MarshalerJSONArray).MarshalJSONArray(enc) enc.writeByte(']') - defer enc.Release() + + defer func() { + enc.buf = make([]byte, 0, 512) + enc.Release() + }() + return enc.buf, nil } @@ -61,7 +66,12 @@ func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) { func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) { enc := BorrowEncoder(nil) enc.grow(512) - defer enc.Release() + + defer func() { + enc.buf = make([]byte, 0, 512) + enc.Release() + }() + return enc.encodeObject(v) } @@ -91,6 +101,11 @@ func marshal(v interface{}, any bool) ([]byte, error) { err error ) + defer func() { + enc.buf = make([]byte, 0, 512) + enc.Release() + }() + buf, err = func() ([]byte, error) { switch vt := v.(type) { case MarshalerJSONObject: @@ -133,8 +148,6 @@ func marshal(v interface{}, any bool) ([]byte, error) { return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) } }() - - enc.Release() return buf, err } diff --git a/encode_bool_test.go b/encode_bool_test.go @@ -43,7 +43,7 @@ func TestEncoderBoolErrors(t *testing.T) { t.Run("pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") diff --git a/encode_embedded_json_test.go b/encode_embedded_json_test.go @@ -124,7 +124,7 @@ func TestEncodingEmbeddedJSON(t *testing.T) { t.Run("pool-error", func(t *testing.T) { v := EmbeddedJSON([]byte(`"test"`)) enc := BorrowEncoder(nil) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") diff --git a/encode_interface_test.go b/encode_interface_test.go @@ -153,7 +153,7 @@ func TestEncoderInterfaceEncodeAPI(t *testing.T) { v := "" w := TestWriterError("") enc := BorrowEncoder(w) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") diff --git a/encode_number_test.go b/encode_number_test.go @@ -73,7 +73,7 @@ func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-int-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -92,7 +92,7 @@ func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-int64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -112,7 +112,7 @@ func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-uint64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -132,7 +132,7 @@ func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-float64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -151,7 +151,7 @@ func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-float32-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") diff --git a/encode_object.go b/encode_object.go @@ -44,7 +44,7 @@ func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error } func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) { - enc.grow(500) + enc.grow(512) enc.writeByte('{') if !v.IsNil() { v.MarshalJSONObject(enc) diff --git a/encode_object_test.go b/encode_object_test.go @@ -417,7 +417,7 @@ func TestEncoderObjectEncodeAPIError(t *testing.T) { t.Run("pool-error", func(t *testing.T) { v := &TestEncoding{} enc := BorrowEncoder(nil) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") @@ -608,7 +608,7 @@ func TestEncodeObjectWithKeys(t *testing.T) { t.Run("pool-error", func(t *testing.T) { v := &TestEncoding{} enc := BorrowEncoder(nil) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") diff --git a/encode_pool.go b/encode_pool.go @@ -35,7 +35,7 @@ func NewEncoder(w io.Writer) *Encoder { func BorrowEncoder(w io.Writer) *Encoder { enc := encPool.Get().(*Encoder) enc.w = w - enc.buf = make([]byte, 0, 512) + enc.buf = enc.buf[:0] enc.isPooled = 0 enc.err = nil enc.hasKeys = false diff --git a/encode_pool_test.go b/encode_pool_test.go @@ -1 +1,32 @@ package gojay + +import ( + "fmt" + "log" + "strconv" + "testing" + "time" +) + +func TestConcurrencyMarshal(t *testing.T) { + var f = func(num int, t *testing.T) { + for { + b, err := Marshal(num) + if err != nil { + log.Fatal(err) + } + + s := string(b) + if n, err := strconv.Atoi(s); err != nil || n != num { + t.Error(fmt.Errorf( + "caught race: %v %v", s, num, + )) + } + } + } + + for i := 0; i < 100; i++ { + go f(i, t) + } + time.Sleep(2 * time.Second) +} diff --git a/encode_sqlnull_test.go b/encode_sqlnull_test.go @@ -51,7 +51,7 @@ func TestEncoceSQLNullString(t *testing.T) { func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -329,7 +329,7 @@ func TestEncoceSQLNullInt64(t *testing.T) { func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -606,7 +606,7 @@ func TestEncoceSQLNullFloat64(t *testing.T) { func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") @@ -884,7 +884,7 @@ func TestEncoceSQLNullBool(t *testing.T) { func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") diff --git a/encode_string_test.go b/encode_string_test.go @@ -122,7 +122,7 @@ func TestEncoderStringEncodeAPIErrors(t *testing.T) { t.Run("pool-error", func(t *testing.T) { v := "" enc := BorrowEncoder(nil) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") diff --git a/encode_time_test.go b/encode_time_test.go @@ -41,7 +41,7 @@ func TestEncodeTime(t *testing.T) { t.Run("encode-time-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - enc.Release() + enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil")