commit 251e643799caca0d89c83eb077bb5f299a282bce
parent 9501ba82a3fe8710f7a988cb6d98e1b5df664998
Author: francoispqt <francois@parquet.ninja>
Date: Sat, 28 Apr 2018 20:29:14 +0800
update pooling for encoder, enhance Encode api, add tests for encoding and decoding
Diffstat:
23 files changed, 571 insertions(+), 30 deletions(-)
diff --git a/benchmarks/decoder/Makefile b/benchmarks/decoder/Makefile
@@ -20,6 +20,10 @@ testtrace:
.PHONY: benchgojay
benchgojay:
+ go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms
+
+.PHONY: benchgojaycpu
+benchgojaycpu:
go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms -cpuprofile cpu.out
.PHONY: benchjsoniter
diff --git a/benchmarks/encoder/Makefile b/benchmarks/encoder/Makefile
@@ -16,6 +16,10 @@ testtrace:
.PHONY: benchgojay
benchgojay:
+ go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms
+
+.PHONY: benchgojaycpu
+benchgojaycpu:
go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms -cpuprofile cpu.out
.PHONY: benchjsoniter
diff --git a/decode_bool_test.go b/decode_bool_test.go
@@ -1,6 +1,7 @@
package gojay
import (
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -35,7 +36,7 @@ func TestDecoderBoolNonBooleanJSONFalse(t *testing.T) {
var v bool
err := Unmarshal(json, &v)
assert.Nil(t, err, "Err must be nil")
- assert.Equal(t, false, v, "v must be equal to true")
+ assert.Equal(t, false, v, "v must be equal to false")
}
func TestDecoderBoolInvalidJSON(t *testing.T) {
@@ -45,3 +46,24 @@ func TestDecoderBoolInvalidJSON(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 TestDecoderBoolDecoderAPI(t *testing.T) {
+ var v bool
+ dec := NewDecoder(strings.NewReader("true"))
+ defer dec.Release()
+ err := dec.DecodeBool(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, true, v, "v must be equal to true")
+}
+
+func TestDecoderBoolPoolError(t *testing.T) {
+ v := true
+ 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.DecodeBool(&v)
+ assert.True(t, false, "should not be called as decoder should have panicked")
+}
diff --git a/decode_number_test.go b/decode_number_test.go
@@ -1,6 +1,7 @@
package gojay
import (
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -74,6 +75,14 @@ func TestDecoderIntOverfow2(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 TestDecoderInttDecoderAPI(t *testing.T) {
+ var v int
+ dec := NewDecoder(strings.NewReader(`33`))
+ defer dec.Release()
+ err := dec.DecodeInt(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, int(33), v, "v must be equal to 33")
+}
func TestDecoderInt32Basic(t *testing.T) {
json := []byte(`124`)
@@ -143,6 +152,14 @@ func TestDecoderInt32PoolError(t *testing.T) {
_ = dec.DecodeInt32(&result)
assert.True(t, false, "should not be called as decoder should have panicked")
}
+func TestDecoderInt32tDecoderAPI(t *testing.T) {
+ var v int32
+ dec := NewDecoder(strings.NewReader(`33`))
+ defer dec.Release()
+ err := dec.DecodeInt32(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, int32(33), v, "v must be equal to 33")
+}
func TestDecoderUint32Basic(t *testing.T) {
json := []byte(`124`)
@@ -206,6 +223,14 @@ func TestDecoderUint32PoolError(t *testing.T) {
_ = dec.DecodeUint32(&result)
assert.True(t, false, "should not be called as decoder should have panicked")
}
+func TestDecoderUint32tDecoderAPI(t *testing.T) {
+ var v uint32
+ dec := NewDecoder(strings.NewReader(`33`))
+ defer dec.Release()
+ err := dec.DecodeUint32(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, uint32(33), v, "v must be equal to 33")
+}
func TestDecoderInt64Basic(t *testing.T) {
json := []byte(`124`)
@@ -275,6 +300,14 @@ func TestDecoderInt64PoolError(t *testing.T) {
_ = dec.DecodeInt64(&result)
assert.True(t, false, "should not be called as decoder should have panicked")
}
+func TestDecoderInt64tDecoderAPI(t *testing.T) {
+ var v int64
+ dec := NewDecoder(strings.NewReader(`33`))
+ defer dec.Release()
+ err := dec.DecodeInt64(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, int64(33), v, "v must be equal to 33")
+}
func TestDecoderUint64Basic(t *testing.T) {
json := []byte(`124`)
var v uint64
@@ -336,6 +369,14 @@ func TestDecoderUint64PoolError(t *testing.T) {
_ = dec.DecodeUint64(&result)
assert.True(t, false, "should not be called as decoder should have panicked")
}
+func TestDecoderUint64tDecoderAPI(t *testing.T) {
+ var v uint64
+ dec := NewDecoder(strings.NewReader(`33`))
+ defer dec.Release()
+ err := dec.DecodeUint64(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, uint64(33), v, "v must be equal to 33")
+}
func TestDecoderFloatBasic(t *testing.T) {
json := []byte(`100.11`)
var v float64
@@ -367,6 +408,14 @@ 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 TestDecoderFloatDecoderAPI(t *testing.T) {
+ var v float64
+ dec := NewDecoder(strings.NewReader(`1.25`))
+ defer dec.Release()
+ err := dec.DecodeFloat64(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, 1.25, v, "v must be equal to 1.25")
+}
func TestDecoderFloatPoolError(t *testing.T) {
result := float64(1)
dec := NewDecoder(nil)
diff --git a/decode_pool.go b/decode_pool.go
@@ -19,7 +19,7 @@ func NewDecoder(r io.Reader) *Decoder {
}
}
-// BorrowDecoder borrows a Decoder a decoder from the pool.
+// BorrowDecoder borrows a Decoder from the pool.
// It takes an io.Reader implementation as data input.
// It initiates the done channel returned by Done().
func BorrowDecoder(r io.Reader, bufSize int) *Decoder {
diff --git a/decode_stream_test.go b/decode_stream_test.go
@@ -373,3 +373,17 @@ func TestStreamDecodingErrNotSet(t *testing.T) {
dec := Stream.NewDecoder(&StreamReader{})
assert.Nil(t, dec.Err(), "dec.Err should be nim")
}
+
+func TestStreamDecodingPoolError(t *testing.T) {
+ dec := Stream.BorrowDecoder(nil, 0)
+ dec.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled decoder", err.(InvalidUsagePooledDecoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ testChan := ChannelStreamStrings(make(chan *string))
+ _ = dec.DecodeStream(testChan)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/decode_string_test.go b/decode_string_test.go
@@ -1,6 +1,7 @@
package gojay
import (
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -45,3 +46,25 @@ func TestDecoderStringInvalidType(t *testing.T) {
assert.NotNil(t, err, "Err must not be nil as JSON is invalid")
assert.IsType(t, InvalidTypeError(""), err, "err message must be 'Invalid JSON'")
}
+
+func TestDecoderStringDecoderAPI(t *testing.T) {
+ var v string
+ dec := NewDecoder(strings.NewReader(`"hello world!"`))
+ defer dec.Release()
+ err := dec.DecodeString(&v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, "hello world!", v, "v must be equal to 'hello world!'")
+}
+
+func TestDecoderStringPoolError(t *testing.T) {
+ result := ""
+ 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.DecodeString(&result)
+ assert.True(t, false, "should not be called as decoder should have panicked")
+}
diff --git a/encode.go b/encode.go
@@ -24,12 +24,8 @@ package gojay
// }
func MarshalObject(v MarshalerObject) ([]byte, error) {
enc := NewEncoder()
- enc.grow(200)
- enc.writeByte('{')
- v.MarshalObject(enc)
- enc.writeByte('}')
defer enc.Release()
- return enc.buf, nil
+ return enc.encodeObject(v)
}
// MarshalArray returns the JSON encoding of v.
@@ -98,7 +94,7 @@ func Marshal(v interface{}) ([]byte, error) {
var err error = InvalidTypeError("Unknown type to Marshal")
switch vt := v.(type) {
case MarshalerObject:
- enc := NewEncoder()
+ enc := BorrowEncoder()
enc.writeByte('{')
vt.MarshalObject(enc)
enc.writeByte('}')
@@ -106,7 +102,7 @@ func Marshal(v interface{}) ([]byte, error) {
defer enc.Release()
return b, nil
case MarshalerArray:
- enc := NewEncoder()
+ enc := BorrowEncoder()
enc.writeByte('[')
vt.MarshalArray(enc)
enc.writeByte(']')
@@ -114,56 +110,56 @@ func Marshal(v interface{}) ([]byte, error) {
defer enc.Release()
return b, nil
case string:
- enc := NewEncoder()
+ enc := BorrowEncoder()
b, err = enc.encodeString(vt)
defer enc.Release()
case bool:
- enc := NewEncoder()
+ enc := BorrowEncoder()
err = enc.AddBool(vt)
b = enc.buf
defer enc.Release()
case int:
- enc := NewEncoder()
+ enc := BorrowEncoder()
b, err = enc.encodeInt(int64(vt))
defer enc.Release()
case int64:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(vt)
case int32:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case int16:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case int8:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case uint64:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case uint32:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case uint16:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeInt(int64(vt))
case uint8:
- enc := NewEncoder()
+ enc := BorrowEncoder()
b, err = enc.encodeInt(int64(vt))
defer enc.Release()
case float64:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeFloat(vt)
case float32:
- enc := NewEncoder()
+ enc := BorrowEncoder()
defer enc.Release()
return enc.encodeFloat(float64(vt))
}
@@ -184,7 +180,8 @@ type MarshalerArray interface {
// An Encoder writes JSON values to an output stream.
type Encoder struct {
- buf []byte
+ buf []byte
+ isPooled byte
}
func (enc *Encoder) getPreviousRune() (byte, bool) {
diff --git a/encode_array.go b/encode_array.go
@@ -1,5 +1,20 @@
package gojay
+// EncodeArray encodes an implementation of MarshalerArray to JSON
+func (enc *Encoder) EncodeArray(v MarshalerArray) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeArray(v)
+}
+func (enc *Encoder) encodeArray(v MarshalerArray) ([]byte, error) {
+ enc.grow(200)
+ enc.writeByte('[')
+ v.MarshalArray(enc)
+ enc.writeByte(']')
+ return enc.buf, nil
+}
+
// AddArray adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
// value must implement Marshaler
func (enc *Encoder) AddArray(value MarshalerArray) error {
diff --git a/encode_array_test.go b/encode_array_test.go
@@ -6,6 +6,14 @@ import (
"github.com/stretchr/testify/assert"
)
+type TestEncodingArrStrings []string
+
+func (t TestEncodingArrStrings) MarshalArray(enc *Encoder) {
+ for _, e := range t {
+ enc.AddString(e)
+ }
+}
+
type TestEncodingArr []*TestEncoding
func (t TestEncodingArr) MarshalArray(enc *Encoder) {
@@ -104,3 +112,51 @@ func TestEncoderArrayInterfaces(t *testing.T) {
string(r),
"Result of marshalling is different as the one expected")
}
+
+func TestEncoderArrayInterfacesEncoderAPI(t *testing.T) {
+ v := &testEncodingArrInterfaces{
+ 1,
+ int64(1),
+ int32(1),
+ int16(1),
+ int8(1),
+ uint64(1),
+ uint32(1),
+ uint16(1),
+ uint8(1),
+ float64(1.31),
+ // float32(1.31),
+ &TestEncodingArr{},
+ true,
+ "test",
+ &TestEncoding{
+ test: "hello world",
+ test2: "foobar",
+ testInt: 1,
+ testBool: true,
+ },
+ }
+ enc := BorrowEncoder()
+ defer enc.Release()
+ r, err := enc.EncodeArray(v)
+ assert.Nil(t, err, "Error should be nil")
+ assert.Equal(
+ t,
+ `[1,1,1,1,1,1,1,1,1.31,[],true,"test",{"test":"hello world","test2":"foobar","testInt":1,"testBool":true,"testArr":[],"testF64":0,"testF32":0}]`,
+ string(r),
+ "Result of marshalling is different as the one expected")
+}
+
+func TestEncoderArrayPooledError(t *testing.T) {
+ v := &testEncodingArrInterfaces{}
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _, _ = enc.EncodeArray(v)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/encode_bool.go b/encode_bool.go
@@ -2,6 +2,24 @@ package gojay
import "strconv"
+// EncodeBool encodes a bool to JSON
+func (enc *Encoder) EncodeBool(v bool) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeBool(v)
+}
+
+// encodeBool encodes a bool to JSON
+func (enc *Encoder) encodeBool(v bool) ([]byte, error) {
+ if v {
+ enc.writeString("true")
+ } else {
+ enc.writeString("false")
+ }
+ return enc.buf, nil
+}
+
// AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) AddBool(value bool) error {
r, ok := enc.getPreviousRune()
@@ -16,7 +34,7 @@ func (enc *Encoder) AddBool(value bool) error {
return nil
}
-// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key
+// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
func (enc *Encoder) AddBoolKey(key string, value bool) error {
r, ok := enc.getPreviousRune()
if ok && r != '{' && r != '[' {
diff --git a/encode_bool_test.go b/encode_bool_test.go
@@ -0,0 +1,36 @@
+package gojay
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncoderBoolTrue(t *testing.T) {
+ enc := BorrowEncoder()
+ defer enc.Release()
+ b, err := enc.EncodeBool(true)
+ assert.Nil(t, err, "err must be nil")
+ assert.Equal(t, "true", string(b), "string(b) must be equal to 'true'")
+}
+
+func TestEncoderBoolFalse(t *testing.T) {
+ enc := BorrowEncoder()
+ defer enc.Release()
+ b, err := enc.EncodeBool(false)
+ assert.Nil(t, err, "err must be nil")
+ assert.Equal(t, "false", string(b), "string(b) must be equal to 'false'")
+}
+
+func TestEncoderBoolPoolError(t *testing.T) {
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledEncoderError")
+ }()
+ _, _ = enc.EncodeBool(false)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/encode_interface.go b/encode_interface.go
@@ -1,5 +1,52 @@
package gojay
+import (
+ "fmt"
+ "reflect"
+)
+
+// Encode encodes a value to JSON.
+//
+// If Encode cannot find a way to encode the type to JSON
+// it will return an InvalidMarshalError.
+func (enc *Encoder) Encode(v interface{}) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ switch vt := v.(type) {
+ case string:
+ return enc.encodeString(vt)
+ case bool:
+ return enc.encodeBool(vt)
+ case MarshalerArray:
+ return enc.encodeArray(vt)
+ case MarshalerObject:
+ return enc.encodeObject(vt)
+ case int:
+ return enc.encodeInt(int64(vt))
+ case int64:
+ return enc.encodeInt(vt)
+ case int32:
+ return enc.encodeInt(int64(vt))
+ case int8:
+ return enc.encodeInt(int64(vt))
+ case uint64:
+ return enc.encodeInt(int64(vt))
+ case uint32:
+ return enc.encodeInt(int64(vt))
+ case uint16:
+ return enc.encodeInt(int64(vt))
+ case uint8:
+ return enc.encodeInt(int64(vt))
+ case float64:
+ return enc.encodeFloat(vt)
+ case float32:
+ return enc.encodeFloat(float64(vt))
+ default:
+ return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String()))
+ }
+}
+
// AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) AddInterface(value interface{}) error {
switch value.(type) {
diff --git a/encode_interface_test.go b/encode_interface_test.go
@@ -0,0 +1,113 @@
+package gojay
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var encoderTestCases = []struct {
+ v interface{}
+ expectations func(t *testing.T, b []byte, err error)
+}{
+ {
+ v: int64(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: int32(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: int8(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: uint64(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: uint32(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: uint8(100),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100", string(b), "string(b) should equal 100")
+ },
+ },
+ {
+ v: float64(100.12),
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "100.12", string(b), "string(b) should equal 100.12")
+ },
+ },
+ {
+ v: true,
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, "true", string(b), "string(b) should equal true")
+ },
+ },
+ {
+ v: "hello world",
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, `"hello world"`, string(b), `string(b) should equal "hello world"`)
+ },
+ },
+ {
+ v: "hello world",
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, `"hello world"`, string(b), `string(b) should equal "hello world"`)
+ },
+ },
+ {
+ v: &TestEncodingArrStrings{"hello world", "foo bar"},
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, `["hello world","foo bar"]`, string(b), `string(b) should equal ["hello world","foo bar"]`)
+ },
+ },
+ {
+ v: &testObject{"漢字", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.1, 1.1, true},
+ expectations: func(t *testing.T, b []byte, err error) {
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, string(b), `string(b) should equal {"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`)
+ },
+ },
+}
+
+func TestEncoderInterfaceAllTypesDecoderAPI(t *testing.T) {
+ for _, test := range encoderTestCases {
+ enc := BorrowEncoder()
+ b, err := enc.Encode(test.v)
+ enc.Release()
+ test.expectations(t, b, err)
+ }
+}
+
+func TestEncoderInterfaceAllTypesMarshalAPI(t *testing.T) {
+ for _, test := range encoderTestCases {
+ b, err := Marshal(test.v)
+ test.expectations(t, b, err)
+ }
+}
diff --git a/encode_number.go b/encode_number.go
@@ -2,6 +2,14 @@ package gojay
import "strconv"
+// EncodeInt encodes an int to JSON
+func (enc *Encoder) EncodeInt(n int64) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeInt(n)
+}
+
// encodeInt encodes an int to JSON
func (enc *Encoder) encodeInt(n int64) ([]byte, error) {
s := strconv.Itoa(int(n))
@@ -9,6 +17,14 @@ func (enc *Encoder) encodeInt(n int64) ([]byte, error) {
return enc.buf, nil
}
+// EncodeFloat encodes a float64 to JSON
+func (enc *Encoder) EncodeFloat(n float64) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeFloat(n)
+}
+
// encodeFloat encodes a float64 to JSON
func (enc *Encoder) encodeFloat(n float64) ([]byte, error) {
s := strconv.FormatFloat(n, 'f', -1, 64)
diff --git a/encode_number_test.go b/encode_number_test.go
@@ -101,3 +101,31 @@ func TestEncoderFloat(t *testing.T) {
string(r),
"Result of marshalling is different as the one expected")
}
+
+func TestEncoderIntPooledError(t *testing.T) {
+ v := 1
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _, _ = enc.EncodeInt(int64(v))
+ assert.True(t, false, "should not be called as it should have panicked")
+}
+
+func TestEncoderFloatPooledError(t *testing.T) {
+ v := 1.1
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _, _ = enc.EncodeFloat(v)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/encode_object.go b/encode_object.go
@@ -5,6 +5,21 @@ var objKeyObj = []byte(`":{`)
var objKeyArr = []byte(`":[`)
var objKey = []byte(`":`)
+// EncodeObject encodes an object to JSON
+func (enc *Encoder) EncodeObject(v MarshalerObject) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeObject(v)
+}
+func (enc *Encoder) encodeObject(v MarshalerObject) ([]byte, error) {
+ enc.grow(200)
+ enc.writeByte('{')
+ v.MarshalObject(enc)
+ enc.writeByte('}')
+ return enc.buf, nil
+}
+
// AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
// value must implement Marshaler
func (enc *Encoder) AddObject(value MarshalerObject) error {
diff --git a/encode_object_test.go b/encode_object_test.go
@@ -42,7 +42,7 @@ func (t *testObject) MarshalObject(enc *Encoder) {
enc.AddBoolKey("testBool", t.testBool)
}
-func TestEncodeBasicObject(t *testing.T) {
+func TestEncoderObjectBasic(t *testing.T) {
r, err := Marshal(&testObject{"漢字", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.1, 1.1, true})
assert.Nil(t, err, "Error should be nil")
assert.Equal(
@@ -101,7 +101,7 @@ func (t *SubObject) MarshalObject(enc *Encoder) {
enc.AddObjectKey("sub", t.sub)
}
-func TestEncoderComplexObject(t *testing.T) {
+func TestEncoderObjectComplex(t *testing.T) {
v := &TestEncoding{
test: "hello world",
test2: "foobar",
@@ -149,7 +149,7 @@ func (t *testEncodingObjInterfaces) MarshalObject(enc *Encoder) {
enc.AddInterfaceKey("interfaceVal", t.interfaceVal)
}
-func TestObjInterfaces(t *testing.T) {
+func TestEncoderObjectInterfaces(t *testing.T) {
v := testEncodingObjInterfaces{"string"}
r, err := Marshal(&v)
assert.Nil(t, err, "Error should be nil")
@@ -247,3 +247,17 @@ func TestObjInterfaces(t *testing.T) {
string(r),
"Result of marshalling is different as the one expected")
}
+
+func TestEncoderObjectPooledError(t *testing.T) {
+ v := &TestEncoding{}
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _, _ = enc.EncodeObject(v)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/encode_pool.go b/encode_pool.go
@@ -4,18 +4,29 @@ var encObjPool = make(chan *Encoder, 16)
// NewEncoder returns a new encoder or borrows one from the pool
func NewEncoder() *Encoder {
+ return &Encoder{}
+}
+func newEncoder() *Encoder {
+ return &Encoder{}
+}
+
+// BorrowEncoder borrows an Encoder from the pool.
+func BorrowEncoder() *Encoder {
select {
case enc := <-encObjPool:
+ enc.isPooled = 0
return enc
default:
return &Encoder{}
}
}
+// Release sends back a Encoder to the pool.
func (enc *Encoder) Release() {
enc.buf = nil
select {
case encObjPool <- enc:
+ enc.isPooled = 1
default:
}
}
diff --git a/encode_pool_test.go b/encode_pool_test.go
@@ -0,0 +1,20 @@
+package gojay
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncoderNewFromPool(t *testing.T) {
+ // reset pool
+ encObjPool = make(chan *Encoder, 16)
+ // get new Encoder
+ enc := NewEncoder()
+ // add to pool
+ enc.Release()
+ // borrow encoder
+ nEnc := BorrowEncoder()
+ // make sure it's the same
+ assert.Equal(t, enc, nEnc, "enc and nEnc from pool should be the same")
+}
diff --git a/encode_string.go b/encode_string.go
@@ -1,6 +1,14 @@
package gojay
-// encodeString encodes a string to
+// EncodeString encodes a string to
+func (enc *Encoder) EncodeString(s string) ([]byte, error) {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ return enc.encodeString(s)
+}
+
+// encodeString encodes a string to
func (enc *Encoder) encodeString(s string) ([]byte, error) {
enc.writeByte('"')
enc.writeString(s)
@@ -35,4 +43,4 @@ func (enc *Encoder) AddStringKey(key, value string) error {
enc.writeByte('"')
return nil
-}
-\ No newline at end of file
+}
diff --git a/encode_string_test.go b/encode_string_test.go
@@ -25,3 +25,17 @@ func TestEncoderStringUTF8(t *testing.T) {
string(r),
"Result of marshalling is different as the one expected")
}
+
+func TestEncoderStringPooledError(t *testing.T) {
+ v := ""
+ enc := BorrowEncoder()
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _, _ = enc.EncodeString(v)
+ assert.True(t, false, "should not be called as it should have panicked")
+}
diff --git a/errors.go b/errors.go
@@ -26,6 +26,16 @@ func (err InvalidUnmarshalError) Error() string {
return string(err)
}
+const invalidMarshalErrorMsg = "Invalid type %s provided to Marshal"
+
+// InvalidMarshalError is a type representing an error returned when
+// Encoding did not find the proper way to encode
+type InvalidMarshalError string
+
+func (err InvalidMarshalError) Error() string {
+ return string(err)
+}
+
// NoReaderError is a type representing an error returned when
// decoding requires a reader and none was given
type NoReaderError string
@@ -41,3 +51,11 @@ type InvalidUsagePooledDecoderError string
func (err InvalidUsagePooledDecoderError) Error() string {
return string(err)
}
+
+// InvalidUsagePooledEncoderError is a type representing an error returned
+// when decoding is called on a still pooled Encoder
+type InvalidUsagePooledEncoderError string
+
+func (err InvalidUsagePooledEncoderError) Error() string {
+ return string(err)
+}