gojay

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

commit 9501ba82a3fe8710f7a988cb6d98e1b5df664998
parent b9a557e24cfc5fe028d884ea7bfe78e3ed7cee93
Author: francoispqt <francois@parquet.ninja>
Date:   Sat, 28 Apr 2018 16:58:30 +0800

enhance readding from io.Reader, make sure buffer grows when needed, add some tests and avoid calling usless check for pooled decoder

Diffstat:
Mdecode.go | 45++++++++++++++++++++++++---------------------
Mdecode_array.go | 7++++---
Mdecode_array_test.go | 20++++++++++++++++++++
Mdecode_bool.go | 2+-
Mdecode_number.go | 12++++++------
Mdecode_number_test.go | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdecode_object.go | 9+++++----
Mdecode_object_test.go | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdecode_stream.go | 8+++++++-
Adecode_stream_pool_test.go | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_string.go | 2+-
Mdecode_unsafe.go | 28++++++++++++++--------------
12 files changed, 347 insertions(+), 72 deletions(-)

diff --git a/decode.go b/decode.go @@ -14,11 +14,11 @@ import ( // overflows the target type, UnmarshalArray skips that field and completes the unmarshaling as best it can. func UnmarshalArray(data []byte, v UnmarshalerArray) 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) - defer dec.Release() if err != nil { return err } @@ -36,11 +36,11 @@ func UnmarshalArray(data []byte, v UnmarshalerArray) error { // overflows the target type, UnmarshalObject skips that field and completes the unmarshaling as best it can. func UnmarshalObject(data []byte, v UnmarshalerObject) 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) - defer dec.Release() if err != nil { return err } @@ -147,12 +147,6 @@ type UnmarshalerArray interface { UnmarshalArray(*Decoder) error } -// UnmarshalerStream is the interface to implement for a slice, an array or a slice -// to decode a line delimited JSON to. -type UnmarshalerStream interface { - UnmarshalStream(*StreamDecoder) error -} - // A Decoder reads and decodes JSON values from an input stream. type Decoder struct { data []byte @@ -171,30 +165,30 @@ type Decoder struct { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) Decode(v interface{}) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } switch vt := v.(type) { case *string: - return dec.DecodeString(vt) + return dec.decodeString(vt) case *int: - return dec.DecodeInt(vt) + return dec.decodeInt(vt) case *int32: - return dec.DecodeInt32(vt) + return dec.decodeInt32(vt) case *uint32: - return dec.DecodeUint32(vt) + return dec.decodeUint32(vt) case *int64: - return dec.DecodeInt64(vt) + return dec.decodeInt64(vt) case *uint64: - return dec.DecodeUint64(vt) + return dec.decodeUint64(vt) case *float64: - return dec.DecodeFloat64(vt) + return dec.decodeFloat64(vt) case *bool: - return dec.DecodeBool(vt) + return dec.decodeBool(vt) case UnmarshalerObject: - _, err := dec.DecodeObject(vt) + _, err := dec.decodeObject(vt) return err case UnmarshalerArray: - _, err := dec.DecodeArray(vt) + _, err := dec.decodeArray(vt) return err default: return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(vt).String())) @@ -290,9 +284,18 @@ func isDigit(b byte) bool { func (dec *Decoder) read() bool { if dec.r != nil { - // idea is to append data from reader at the end + // if we reach the end, double the buffer to ensure there's always more space + if len(dec.data) == dec.length { + nLen := dec.length * 2 + Buf := make([]byte, nLen, nLen) + copy(Buf, dec.data) + dec.data = Buf + } n, err := dec.r.Read(dec.data[dec.length:]) - if err != nil || n == 0 { + if err != nil { + dec.err = err + return false + } else if n == 0 { return false } dec.length = dec.length + n diff --git a/decode_array.go b/decode_array.go @@ -9,11 +9,12 @@ import ( // v must implement UnmarshalerArray. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeArray(arr UnmarshalerArray) (int, error) { +func (dec *Decoder) DecodeArray(arr UnmarshalerArray) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } - return dec.decodeArray(arr) + _, err := dec.decodeArray(arr) + return err } func (dec *Decoder) decodeArray(arr UnmarshalerArray) (int, error) { // not an array not an error, but do not know what to do diff --git a/decode_array_test.go b/decode_array_test.go @@ -1,6 +1,7 @@ package gojay import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -169,3 +170,22 @@ func TestDecoderSliceInvalidJSON(t *testing.T) { assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } + +func TestDecoderSliceDecoderAPI(t *testing.T) { + json := `["string","string1"]` + testArr := testSliceStrings{} + dec := NewDecoder(strings.NewReader(json)) + err := dec.DecodeArray(&testArr) + assert.Nil(t, err, "Err must be nil") + assert.Len(t, testArr, 2, "testArr should be of len 2") + assert.Equal(t, "string", testArr[0], "testArr[0] should be 'string'") + assert.Equal(t, "string1", testArr[1], "testArr[1] should be 'string1'") +} + +func TestDecoderSliceDecoderAPIError(t *testing.T) { + testArr := testSliceInts{} + dec := NewDecoder(strings.NewReader(`hello`)) + err := dec.DecodeArray(&testArr) + assert.NotNil(t, err, "Err must not be nil as JSON is invalid") + assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") +} diff --git a/decode_bool.go b/decode_bool.go @@ -7,7 +7,7 @@ import "fmt" // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeBool(v *bool) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeBool(v) } diff --git a/decode_number.go b/decode_number.go @@ -58,7 +58,7 @@ func init() { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt(v *int) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt(v) } @@ -108,7 +108,7 @@ func (dec *Decoder) decodeInt(v *int) error { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt32(v *int32) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt32(v) } @@ -158,7 +158,7 @@ func (dec *Decoder) decodeInt32(v *int32) error { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint32(v *uint32) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint32(v) } @@ -210,7 +210,7 @@ func (dec *Decoder) decodeUint32(v *uint32) error { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt64(v *int64) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt64(v) } @@ -261,7 +261,7 @@ func (dec *Decoder) decodeInt64(v *int64) error { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint64(v *uint64) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint64(v) } @@ -312,7 +312,7 @@ func (dec *Decoder) decodeUint64(v *uint64) error { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeFloat64(v *float64) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeFloat64(v) } diff --git a/decode_number_test.go b/decode_number_test.go @@ -55,6 +55,18 @@ func TestDecoderIntOverfow(t *testing.T) { assert.NotNil(t, err, "Err must not be nil as int is overflowing") assert.Equal(t, 0, v, "v must be equal to 0") } +func TestDecoderIntPoolError(t *testing.T) { + result := int(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} func TestDecoderIntOverfow2(t *testing.T) { json := []byte(`92233720368547758089`) var v int @@ -119,6 +131,18 @@ func TestDecoderInt32Overflow2(t *testing.T) { assert.NotNil(t, err, "err must not be nil as int32 overflows") assert.IsType(t, InvalidTypeError(""), err, "err must be of type InvalidTypeError") } +func TestDecoderInt32PoolError(t *testing.T) { + result := int32(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt32(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} func TestDecoderUint32Basic(t *testing.T) { json := []byte(`124`) @@ -170,6 +194,18 @@ func TestDecoderUint32Overflow2(t *testing.T) { assert.NotNil(t, err, "err must not be nil as uint32 overflows") assert.IsType(t, InvalidTypeError(""), err, "err must be of type InvalidTypeError") } +func TestDecoderUint32PoolError(t *testing.T) { + result := uint32(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeUint32(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} func TestDecoderInt64Basic(t *testing.T) { json := []byte(`124`) @@ -227,6 +263,18 @@ func TestDecoderInt64Overflow2(t *testing.T) { assert.NotNil(t, err, "err must not be nil as int64 overflows") assert.IsType(t, InvalidTypeError(""), err, "err must be of type InvalidTypeError") } +func TestDecoderInt64PoolError(t *testing.T) { + result := int64(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt64(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} func TestDecoderUint64Basic(t *testing.T) { json := []byte(`124`) var v uint64 @@ -276,7 +324,18 @@ func TestDecoderUint64Overflow2(t *testing.T) { assert.NotNil(t, err, "err must not be nil as int32 overflows") assert.IsType(t, InvalidTypeError(""), err, "err must be of type InvalidTypeError") } - +func TestDecoderUint64PoolError(t *testing.T) { + result := uint64(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeUint64(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} func TestDecoderFloatBasic(t *testing.T) { json := []byte(`100.11`) var v float64 @@ -308,3 +367,15 @@ func TestDecoderFloatInvalidJSON(t *testing.T) { assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } +func TestDecoderFloatPoolError(t *testing.T) { + result := float64(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeFloat64(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} diff --git a/decode_object.go b/decode_object.go @@ -10,15 +10,16 @@ import ( // v must implement UnmarshalerObject. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeObject(j UnmarshalerObject) (int, error) { +func (dec *Decoder) DecodeObject(j UnmarshalerObject) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } - return dec.decodeObject(j) + _, err := dec.decodeObject(j) + return err } func (dec *Decoder) decodeObject(j UnmarshalerObject) (int, error) { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } keys := j.NKeys() for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { diff --git a/decode_object_test.go b/decode_object_test.go @@ -1,6 +1,7 @@ package gojay import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -75,6 +76,27 @@ func (t *TestObj) NKeys() int { return 8 } +func assertResult(t *testing.T, v *TestObj, err error) { + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, 245, v.test, "v.test must be equal to 245") + assert.Equal(t, 246, v.test2, "v.test2 must be equal to 246") + assert.Equal(t, "string", v.test3, "v.test3 must be equal to 'string'") + assert.Equal(t, "complex string with spaces and some slashes\"", v.test4, "v.test4 must be equal to 'string'") + assert.Equal(t, -1.15657654376543, v.test5, "v.test5 must be equal to 1.15") + assert.Len(t, v.testArr, 2, "v.testArr must be of len 2") + + assert.Equal(t, 121, v.testSubObj.test3, "v.testSubObj.test3 must be equal to 121") + assert.Equal(t, 122, v.testSubObj.test4, "v.testSubObj.test4 must be equal to 122") + assert.Equal(t, "string", v.testSubObj.test5, "v.testSubObj.test5 must be equal to 'string'") + assert.Equal(t, 150, v.testSubObj.testSubSubObj.test3, "v.testSubObj.testSubSubObj.test3 must be equal to 150") + assert.Equal(t, 150, v.testSubObj.testSubSubObj2.test3, "v.testSubObj.testSubSubObj2.test3 must be equal to 150") + + assert.Equal(t, 122, v.testSubObj2.test3, "v.testSubObj2.test3 must be equal to 121") + assert.Equal(t, 123, v.testSubObj2.test4, "v.testSubObj2.test4 must be equal to 122") + assert.Equal(t, "string", v.testSubObj2.test5, "v.testSubObj2.test5 must be equal to 'string'") + assert.Equal(t, 151, v.testSubObj2.testSubSubObj.test3, "v.testSubObj2.testSubSubObj.test must be equal to 150") +} + func TestDecoderObject(t *testing.T) { json := []byte(`{ "test": 245, @@ -118,24 +140,7 @@ func TestDecoderObject(t *testing.T) { }`) v := &TestObj{} err := Unmarshal(json, v) - assert.Nil(t, err, "Err must be nil") - assert.Equal(t, 245, v.test, "v.test must be equal to 245") - assert.Equal(t, 246, v.test2, "v.test2 must be equal to 246") - assert.Equal(t, "string", v.test3, "v.test3 must be equal to 'string'") - assert.Equal(t, "complex string with spaces and some slashes\"", v.test4, "v.test4 must be equal to 'string'") - assert.Equal(t, -1.15657654376543, v.test5, "v.test5 must be equal to 1.15") - assert.Len(t, v.testArr, 2, "v.testArr must be of len 2") - - assert.Equal(t, 121, v.testSubObj.test3, "v.testSubObj.test3 must be equal to 121") - assert.Equal(t, 122, v.testSubObj.test4, "v.testSubObj.test4 must be equal to 122") - assert.Equal(t, "string", v.testSubObj.test5, "v.testSubObj.test5 must be equal to 'string'") - assert.Equal(t, 150, v.testSubObj.testSubSubObj.test3, "v.testSubObj.testSubSubObj.test3 must be equal to 150") - assert.Equal(t, 150, v.testSubObj.testSubSubObj2.test3, "v.testSubObj.testSubSubObj2.test3 must be equal to 150") - - assert.Equal(t, 122, v.testSubObj2.test3, "v.testSubObj2.test3 must be equal to 121") - assert.Equal(t, 123, v.testSubObj2.test4, "v.testSubObj2.test4 must be equal to 122") - assert.Equal(t, "string", v.testSubObj2.test5, "v.testSubObj2.test5 must be equal to 'string'") - assert.Equal(t, 151, v.testSubObj2.testSubSubObj.test3, "v.testSubObj2.testSubSubObj.test must be equal to 150") + assertResult(t, v, err) } func TestDecodeObjectNull(t *testing.T) { @@ -245,7 +250,7 @@ func TestDecodeObjectPartial(t *testing.T) { "testSkipNumber": 123.23 }`) dec.length = len(dec.data) - _, err := dec.DecodeObject(&result) + err := dec.DecodeObject(&result) assert.Nil(t, err, "err should be nil") assert.NotEqual(t, len(dec.data), dec.cursor) } @@ -259,7 +264,75 @@ func TestDecoderObjectInvalidJSON(t *testing.T) { "testSkipString": "testInvalidJSON\\\\ }`) dec.length = len(dec.data) - _, err := dec.DecodeObject(&result) + err := dec.DecodeObject(&result) + assert.NotNil(t, err, "Err must not be nil as JSON is invalid") + assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") +} + +func TestDecoderObjectPoolError(t *testing.T) { + result := jsonDecodePartial{} + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeObject(&result) + assert.True(t, false, "should not be called as decoder should have panicked") +} + +func TestDecoderObjectDecoderAPI(t *testing.T) { + json := `{ + "test": 245, + "test2": 246, + "test3": "string", + "test4": "complex string with spaces and some slashes\"", + "test5": -1.15657654376543, + "testNull": null, + "testArr": [ + { + "test": 245, + "test2": 246 + }, + { + "test": 245, + "test2": 246 + } + ], + "testSubObj": { + "test": 121, + "test2": 122, + "testNull": null, + "testSubSubObj": { + "test": 150, + "testNull": null + }, + "testSubSubObj2": { + "test": 150 + }, + "test3": "string" + "testNull": null, + }, + "testSubObj2": { + "test": 122, + "test3": "string" + "testSubSubObj": { + "test": 151 + }, + "test2": 123 + } + }` + v := &TestObj{} + dec := NewDecoder(strings.NewReader(json)) + err := dec.DecodeObject(v) + assertResult(t, v, err) +} + +func TestDecoderObjectDecoderAPIError(t *testing.T) { + testArr := testSliceInts{} + dec := NewDecoder(strings.NewReader(`hello`)) + err := dec.DecodeArray(&testArr) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } diff --git a/decode_stream.go b/decode_stream.go @@ -4,6 +4,12 @@ import ( "time" ) +// UnmarshalerStream is the interface to implement for a slice, an array or a slice +// to decode a line delimited JSON to. +type UnmarshalerStream interface { + UnmarshalStream(*StreamDecoder) error +} + // Stream is a struct holding the Stream api var Stream = stream{} @@ -25,7 +31,7 @@ type StreamDecoder struct { // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } if dec.r == nil { dec.err = NoReaderError("No reader given to decode stream") diff --git a/decode_stream_pool_test.go b/decode_stream_pool_test.go @@ -0,0 +1,100 @@ +package gojay + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodeStreamBorrow(t *testing.T) { + // we override the pool chan + streamDecPool = make(chan *StreamDecoder, 1) + // add one decoder to the channel + dec := Stream.NewDecoder(nil) + streamDecPool <- dec + // borrow one decoder to the channel + nDec := Stream.BorrowDecoder(nil, 0) + // make sure they are the same + assert.Equal(t, dec, nDec, "decoder added to the pool and new decoder should be the same") +} + +func TestDecodeStreamBorrow1(t *testing.T) { + // we override the pool chan + streamDecPool = make(chan *StreamDecoder, 1) + // add one decoder to the channel + dec := Stream.NewDecoder(nil) + streamDecPool <- dec + // reset streamDecPool + streamDecPool = make(chan *StreamDecoder, 1) + // borrow one decoder to the channel + nDec := Stream.BorrowDecoder(nil, 0) + // make sure they are the same + assert.NotEqual(t, dec, nDec, "decoder added to the pool and new decoder should be the same") +} +func TestDecodeStreamBorrow2(t *testing.T) { + // we override the pool chan + streamDecPool = make(chan *StreamDecoder, 1) + // add one decoder to the channel + dec := Stream.NewDecoder(nil) + dec.data = make([]byte, 128) + streamDecPool <- dec + // borrow one decoder to the channel + nDec := Stream.BorrowDecoder(nil, 512) + // make sure they are the same + assert.Equal(t, dec, nDec, "decoder added to the pool and new decoder should be the same") + assert.Equal(t, 512, len(nDec.data), "len of dec.data should be 512") +} +func TestDecodeStreamBorrow3(t *testing.T) { + // we override the pool chan + streamDecPool = make(chan *StreamDecoder, 16) + // borrow one decoder to the channel + nDec := Stream.BorrowDecoder(nil, 512) + // make sure they are the same + assert.Equal(t, 512, len(nDec.data), "len of dec.data should be 512") +} + +func TestDecodeStreamDecodePooledDecoderError(t *testing.T) { + // we override the pool chan + dec := Stream.NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + var v = 0 + dec.Decode(&v) + // make sure it fails if this is called + assert.True(t, false, "should not be called as decoder should have panicked") +} + +func TestDecodeStreamDecodePooledDecoderError1(t *testing.T) { + // we override the pool chan + dec := Stream.NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + var v = testSliceStrings{} + dec.DecodeArray(&v) + // make sure they are the same + assert.True(t, false, "should not be called as decoder should have panicked") +} + +func TestDecodeStreamDecodePooledDecoderError2(t *testing.T) { + // we override the pool chan + dec := Stream.NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnot be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + assert.Equal(t, "Invalid usage of pooled decoder", err.(InvalidUsagePooledDecoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") + }() + var v = TestObj{} + dec.DecodeObject(&v) + // make sure they are the same + assert.True(t, false, "should not be called as decoder should have panicked") +} diff --git a/decode_string.go b/decode_string.go @@ -10,7 +10,7 @@ import ( // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeString(v *string) error { if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usagee of pooled decoder")) + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeString(v) } diff --git a/decode_unsafe.go b/decode_unsafe.go @@ -15,10 +15,10 @@ type decUnsafe struct{} func (u decUnsafe) UnmarshalArray(data []byte, v UnmarshalerArray) error { dec := BorrowDecoder(nil, 0) + defer dec.Release() dec.data = data dec.length = len(data) - _, err := dec.DecodeArray(v) - dec.Release() + _, err := dec.decodeArray(v) if err != nil { return err } @@ -30,10 +30,10 @@ func (u decUnsafe) UnmarshalArray(data []byte, v UnmarshalerArray) error { func (u decUnsafe) UnmarshalObject(data []byte, v UnmarshalerObject) error { dec := BorrowDecoder(nil, 0) + defer dec.Release() dec.data = data dec.length = len(data) - _, err := dec.DecodeObject(v) - dec.Release() + _, err := dec.decodeObject(v) if err != nil { return err } @@ -51,52 +51,52 @@ func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeString(vt) + err = dec.decodeString(vt) case *int: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeInt(vt) + err = dec.decodeInt(vt) case *int32: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeInt32(vt) + err = dec.decodeInt32(vt) case *uint32: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeUint32(vt) + err = dec.decodeUint32(vt) case *int64: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeInt64(vt) + err = dec.decodeInt64(vt) case *uint64: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeUint64(vt) + err = dec.decodeUint64(vt) case *float64: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeFloat64(vt) + err = dec.decodeFloat64(vt) case *bool: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.DecodeBool(vt) + err = dec.decodeBool(vt) case UnmarshalerObject: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - _, err = dec.DecodeObject(vt) + _, err = dec.decodeObject(vt) case UnmarshalerArray: dec = BorrowDecoder(nil, 0) dec.length = len(data) dec.data = data - _, err = dec.DecodeArray(vt) + _, err = dec.decodeArray(vt) default: return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(vt).String())) }