gojay

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

commit c4759855a621fbf631847cf81265b17c0e1e6990
parent 2f9e3aba9ce5220ddae44a60ef8760c1b4b25440
Author: francoispqt <francois@parquet.ninja>
Date:   Tue,  1 May 2018 13:20:38 +0800

simplify encode API filling enc.err when error encountered

Diffstat:
Mdecode.go | 10+++++-----
Mdecode_stream_test.go | 2+-
Mencode.go | 26+++++++-------------------
Mencode_array.go | 8+++-----
Mencode_bool.go | 8+++-----
Mencode_interface.go | 80++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mencode_number.go | 29+++++++++--------------------
Mencode_object.go | 20+++++++++++---------
Mencode_object_test.go | 20++++++++++++++++++++
Mencode_pool.go | 1+
Mencode_stream.go | 6++----
Mencode_stream_pool.go | 1+
Mencode_stream_test.go | 6++++--
Mencode_string.go | 8++------
14 files changed, 114 insertions(+), 111 deletions(-)

diff --git a/decode.go b/decode.go @@ -149,15 +149,15 @@ type UnmarshalerArray interface { // A Decoder reads and decodes JSON values from an input stream. type Decoder struct { + r io.Reader data []byte + err error + isPooled byte + called byte + child byte cursor int length int keysDone int - called byte - child byte - err error - r io.Reader - isPooled byte } // Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v. diff --git a/decode_stream_test.go b/decode_stream_test.go @@ -338,7 +338,7 @@ func (r *StreamReader) Write() { for r.writeCounter < t { time.Sleep(time.Duration(r.writeCounter*100) * time.Millisecond) currentChunkStart := (chunkSize) * r.writeCounter - lastWrite := currentChunkStart + chunkSize + lastWrite = currentChunkStart + chunkSize r.readChan <- r.data[currentChunkStart:lastWrite] carry = l - lastWrite r.writeCounter++ diff --git a/encode.go b/encode.go @@ -96,38 +96,27 @@ func MarshalArray(v MarshalerArray) ([]byte, error) { // fmt.Println(b) // {"id":123456} // } func Marshal(v interface{}) ([]byte, error) { - var b []byte - var err error = InvalidTypeError("Unknown type to Marshal") switch vt := v.(type) { case MarshalerObject: enc := BorrowEncoder(nil) - enc.writeByte('{') - vt.MarshalObject(enc) - enc.writeByte('}') - b = enc.buf defer enc.Release() - return b, nil + return enc.encodeObject(vt) case MarshalerArray: enc := BorrowEncoder(nil) - enc.writeByte('[') - vt.MarshalArray(enc) - enc.writeByte(']') - b = enc.buf defer enc.Release() - return b, nil + return enc.encodeArray(vt) case string: enc := BorrowEncoder(nil) - b, err = enc.encodeString(vt) defer enc.Release() + return enc.encodeString(vt) case bool: enc := BorrowEncoder(nil) - err = enc.AddBool(vt) - b = enc.buf defer enc.Release() + return enc.encodeBool(vt) case int: enc := BorrowEncoder(nil) - b, err = enc.encodeInt(vt) defer enc.Release() + return enc.encodeInt(vt) case int64: enc := BorrowEncoder(nil) defer enc.Release() @@ -158,8 +147,8 @@ func Marshal(v interface{}) ([]byte, error) { return enc.encodeInt(int(vt)) case uint8: enc := BorrowEncoder(nil) - b, err = enc.encodeInt(int(vt)) defer enc.Release() + return enc.encodeInt(int(vt)) case float64: enc := BorrowEncoder(nil) defer enc.Release() @@ -169,9 +158,8 @@ func Marshal(v interface{}) ([]byte, error) { defer enc.Release() return enc.encodeFloat32(vt) default: - err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String())) + return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String())) } - return b, err } // MarshalerObject is the interface to implement for struct to be encoded diff --git a/encode_array.go b/encode_array.go @@ -18,12 +18,12 @@ func (enc *Encoder) encodeArray(v MarshalerArray) ([]byte, error) { enc.writeByte('[') v.MarshalArray(enc) enc.writeByte(']') - return enc.buf, nil + return enc.buf, enc.err } // 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 { +func (enc *Encoder) AddArray(value MarshalerArray) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') @@ -31,12 +31,11 @@ func (enc *Encoder) AddArray(value MarshalerArray) error { enc.writeByte('[') value.MarshalArray(enc) enc.writeByte(']') - return nil } // AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key // value must implement Marshaler -func (enc *Encoder) AddArrayKey(key string, value MarshalerArray) error { +func (enc *Encoder) AddArrayKey(key string, value MarshalerArray) { // grow to avoid allocs (length of key/value + quotes) r, ok := enc.getPreviousRune() if ok && r != '[' && r != '{' { @@ -47,5 +46,4 @@ func (enc *Encoder) AddArrayKey(key string, value MarshalerArray) error { enc.writeBytes(objKeyArr) value.MarshalArray(enc) enc.writeByte(']') - return nil } diff --git a/encode_bool.go b/encode_bool.go @@ -23,11 +23,11 @@ func (enc *Encoder) encodeBool(v bool) ([]byte, error) { } else { enc.writeString("false") } - return enc.buf, nil + return enc.buf, enc.err } // 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 { +func (enc *Encoder) AddBool(value bool) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') @@ -37,11 +37,10 @@ func (enc *Encoder) AddBool(value bool) error { } else { enc.writeString("false") } - return nil } // 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 { +func (enc *Encoder) AddBoolKey(key string, value bool) { r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { enc.writeByte(',') @@ -50,5 +49,4 @@ func (enc *Encoder) AddBoolKey(key string, value bool) error { enc.writeString(key) enc.writeBytes(objKey) enc.buf = strconv.AppendBool(enc.buf, value) - return nil } diff --git a/encode_interface.go b/encode_interface.go @@ -48,75 +48,85 @@ func (enc *Encoder) Encode(v interface{}) error { } // 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 { +func (enc *Encoder) AddInterface(value interface{}) { switch vt := value.(type) { case string: - return enc.AddString(vt) + enc.AddString(vt) case bool: - return enc.AddBool(vt) + enc.AddBool(vt) case MarshalerArray: - return enc.AddArray(vt) + enc.AddArray(vt) case MarshalerObject: - return enc.AddObject(vt) + enc.AddObject(vt) case int: - return enc.AddInt(vt) + enc.AddInt(vt) case int64: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case int32: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case int8: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case uint64: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case uint32: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case uint16: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case uint8: - return enc.AddInt(int(vt)) + enc.AddInt(int(vt)) case float64: - return enc.AddFloat(vt) + enc.AddFloat(vt) case float32: - return enc.AddFloat32(vt) + enc.AddFloat32(vt) + default: + t := reflect.TypeOf(vt) + if t != nil { + enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, t.String())) + return + } + return } - - return nil } // AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInterfaceKey(key string, value interface{}) error { +func (enc *Encoder) AddInterfaceKey(key string, value interface{}) { switch vt := value.(type) { case string: - return enc.AddStringKey(key, vt) + enc.AddStringKey(key, vt) case bool: - return enc.AddBoolKey(key, vt) + enc.AddBoolKey(key, vt) case MarshalerArray: - return enc.AddArrayKey(key, value.(MarshalerArray)) + enc.AddArrayKey(key, vt) case MarshalerObject: - return enc.AddObjectKey(key, value.(MarshalerObject)) + enc.AddObjectKey(key, vt) case int: - return enc.AddIntKey(key, vt) + enc.AddIntKey(key, vt) case int64: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case int32: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case int16: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case int8: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case uint64: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case uint32: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case uint16: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case uint8: - return enc.AddIntKey(key, int(vt)) + enc.AddIntKey(key, int(vt)) case float64: - return enc.AddFloatKey(key, vt) + enc.AddFloatKey(key, vt) case float32: - return enc.AddFloat32Key(key, vt) + enc.AddFloat32Key(key, vt) + default: + t := reflect.TypeOf(vt) + if t != nil { + enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, t.String())) + return + } + return } - - return nil } diff --git a/encode_number.go b/encode_number.go @@ -55,11 +55,11 @@ func (enc *Encoder) EncodeFloat(n float64) error { // encodeFloat encodes a float64 to JSON func (enc *Encoder) encodeFloat(n float64) ([]byte, error) { - enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 64) + enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64) return enc.buf, nil } -// EncodeFloat encodes a float32 to JSON +// EncodeFloat32 encodes a float32 to JSON func (enc *Encoder) EncodeFloat32(n float32) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) @@ -78,39 +78,34 @@ func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) { } // AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt(value int) error { +func (enc *Encoder) AddInt(value int) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendInt(enc.buf, int64(value), 10) - return nil } // AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat(value float64) error { +func (enc *Encoder) AddFloat(value float64) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64) - - return nil } -// AddFloat adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat32(value float32) error { +// AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddFloat32(value float32) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, float64(value), 'f', -1, 32) - - return nil } // AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddIntKey(key string, value int) error { +func (enc *Encoder) AddIntKey(key string, value int) { r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { enc.writeByte(',') @@ -119,12 +114,10 @@ func (enc *Encoder) AddIntKey(key string, value int) error { enc.writeString(key) enc.writeBytes(objKey) enc.buf = strconv.AppendInt(enc.buf, int64(value), 10) - - return nil } // AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKey(key string, value float64) error { +func (enc *Encoder) AddFloatKey(key string, value float64) { r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { enc.writeByte(',') @@ -133,12 +126,10 @@ func (enc *Encoder) AddFloatKey(key string, value float64) error { enc.writeString(key) enc.writeBytes(objKey) enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64) - - return nil } // AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32Key(key string, value float32) error { +func (enc *Encoder) AddFloat32Key(key string, value float32) { r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { enc.writeByte(',') @@ -148,6 +139,4 @@ func (enc *Encoder) AddFloat32Key(key string, value float32) error { enc.writeByte('"') enc.writeByte(':') enc.buf = strconv.AppendFloat(enc.buf, float64(value), 'f', -1, 32) - - return nil } diff --git a/encode_object.go b/encode_object.go @@ -10,8 +10,12 @@ func (enc *Encoder) EncodeObject(v MarshalerObject) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } - _, _ = enc.encodeObject(v) - _, err := enc.write() + _, err := enc.encodeObject(v) + if err != nil { + enc.err = err + return err + } + _, err = enc.write() if err != nil { enc.err = err return err @@ -23,14 +27,14 @@ func (enc *Encoder) encodeObject(v MarshalerObject) ([]byte, error) { enc.writeByte('{') v.MarshalObject(enc) enc.writeByte('}') - return enc.buf, nil + return enc.buf, enc.err } // AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerObject -func (enc *Encoder) AddObject(value MarshalerObject) error { +func (enc *Encoder) AddObject(value MarshalerObject) { if value.IsNil() { - return nil + return } r, ok := enc.getPreviousRune() if ok && r != '[' { @@ -39,14 +43,13 @@ func (enc *Encoder) AddObject(value MarshalerObject) error { enc.writeByte('{') value.MarshalObject(enc) enc.writeByte('}') - return nil } // AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key // value must implement MarshalerObject -func (enc *Encoder) AddObjectKey(key string, value MarshalerObject) error { +func (enc *Encoder) AddObjectKey(key string, value MarshalerObject) { if value.IsNil() { - return nil + return } r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { @@ -57,5 +60,4 @@ func (enc *Encoder) AddObjectKey(key string, value MarshalerObject) error { enc.writeBytes(objKeyObj) value.MarshalObject(enc) enc.writeByte('}') - return nil } diff --git a/encode_object_test.go b/encode_object_test.go @@ -43,6 +43,18 @@ func (t *testObject) MarshalObject(enc *Encoder) { enc.AddBoolKey("testBool", t.testBool) } +type testObjectWithUnknownType struct { + unknownType struct{} +} + +func (t *testObjectWithUnknownType) IsNil() bool { + return t == nil +} + +func (t *testObjectWithUnknownType) MarshalObject(enc *Encoder) { + enc.AddInterfaceKey("unknownType", t.unknownType) +} + 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") @@ -66,6 +78,14 @@ func TestEncoderObjectBasicEncoderApi(t *testing.T) { ) } +func TestEncoderObjectInterfaceEncoderApiError(t *testing.T) { + builder := &strings.Builder{} + enc := NewEncoder(builder) + err := enc.EncodeObject(&testObjectWithUnknownType{struct{}{}}) + assert.NotNil(t, err, "Error should not be nil") + assert.Equal(t, "Invalid type struct {} provided to Marshal", err.Error(), "err.Error() should be 'Invalid type struct {} provided to Marshal'") +} + func TestEncoderObjectBasicEncoderApiError(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) diff --git a/encode_pool.go b/encode_pool.go @@ -19,6 +19,7 @@ func BorrowEncoder(w io.Writer) *Encoder { case enc := <-encPool: enc.isPooled = 0 enc.w = w + enc.err = nil enc.buf = make([]byte, 0) return enc default: diff --git a/encode_stream.go b/encode_stream.go @@ -117,17 +117,15 @@ func (s *StreamEncoder) AddObject(v MarshalerObject) error { } // AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (s *StreamEncoder) AddInt(value int) error { +func (s *StreamEncoder) AddInt(value int) { s.buf = strconv.AppendInt(s.buf, int64(value), 10) s.Encoder.writeByte(s.delimiter) - return nil } // AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (s *StreamEncoder) AddFloat(value float64) error { +func (s *StreamEncoder) AddFloat(value float64) { s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64) s.Encoder.writeByte(s.delimiter) - return nil } // Non exposed diff --git a/encode_stream_pool.go b/encode_stream_pool.go @@ -20,6 +20,7 @@ func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder { case streamEnc := <-streamEncPool: streamEnc.isPooled = 0 streamEnc.w = w + streamEnc.err = nil streamEnc.done = make(chan struct{}, 1) streamEnc.Encoder.buf = make([]byte, 0, 512) streamEnc.nConsumer = 1 diff --git a/encode_stream_test.go b/encode_stream_test.go @@ -26,7 +26,8 @@ func (s StreamChanInt) MarshalStream(enc *StreamEncoder) error { case <-enc.Done(): return enc.Err() case o := <-s: - return enc.AddInt(o) + enc.AddInt(o) + return nil } } @@ -37,7 +38,8 @@ func (s StreamChanFloat) MarshalStream(enc *StreamEncoder) error { case <-enc.Done(): return enc.Err() case o := <-s: - return enc.AddFloat(o) + enc.AddFloat(o) + return nil } } diff --git a/encode_string.go b/encode_string.go @@ -23,7 +23,7 @@ func (enc *Encoder) encodeString(s string) ([]byte, error) { } // AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddString(value string) error { +func (enc *Encoder) AddString(value string) { r, ok := enc.getPreviousRune() if ok && r != '[' { enc.writeByte(',') @@ -31,12 +31,10 @@ func (enc *Encoder) AddString(value string) error { enc.writeByte('"') enc.writeString(value) enc.writeByte('"') - - return nil } // AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKey(key, value string) error { +func (enc *Encoder) AddStringKey(key, value string) { // grow to avoid allocs (length of key/value + quotes) r, ok := enc.getPreviousRune() if ok && r != '{' && r != '[' { @@ -47,6 +45,4 @@ func (enc *Encoder) AddStringKey(key, value string) error { enc.writeBytes(objKeyStr) enc.writeString(value) enc.writeByte('"') - - return nil }