commit cef72a4f0bf03972e0264c36d0d1bad4884f8784
parent 0d1a893740b14aedd39b5ade2e7567645a40eb0b
Author: francoispqt <francois@parquet.ninja>
Date: Sun, 26 Aug 2018 21:41:15 +0800
add decode object null
Diffstat:
4 files changed, 335 insertions(+), 0 deletions(-)
diff --git a/benchmarks/decoder/decoder_null_test.go b/benchmarks/decoder/decoder_null_test.go
@@ -0,0 +1,61 @@
+package benchmarks
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/francoispqt/gojay"
+)
+
+type ObjNullReflect struct {
+ O *ObjNullReflect
+}
+
+func (o *ObjNullReflect) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "o":
+ return dec.ObjectNullReflect(&o.O)
+ }
+ return nil
+}
+
+func (o *ObjNullReflect) NKeys() int {
+ return 0
+}
+
+type ObjNullFactory struct {
+ O *ObjNullFactory
+}
+
+func (o *ObjNullFactory) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "o":
+ return dec.ObjectNullFactory(func() gojay.UnmarshalerJSONObject {
+ o.O = &ObjNullFactory{}
+ return o.O
+ })
+ }
+ return nil
+}
+
+func (o *ObjNullFactory) NKeys() int {
+ return 0
+}
+
+var objNullJSON = []byte(`{"o":{}}`)
+
+func BenchmarkJSONDecodeObjNullReflection(b *testing.B) {
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ result := &ObjNullReflect{}
+ json.Unmarshal(objNullJSON, &result)
+ }
+}
+
+func BenchmarkJSONDecodeObjNullFactory(b *testing.B) {
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ result := &ObjNullFactory{}
+ json.Unmarshal(objNullJSON, &result)
+ }
+}
diff --git a/decode.go b/decode.go
@@ -870,6 +870,42 @@ func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
return nil
}
+// ObjectNull decodes the next key to a UnmarshalerJSONObject.
+func (dec *Decoder) ObjectNullFactory(factory func() UnmarshalerJSONObject) error {
+ initialKeysDone := dec.keysDone
+ initialChild := dec.child
+ dec.keysDone = 0
+ dec.called = 0
+ dec.child |= 1
+ newCursor, err := dec.decodeObjectNull(factory)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.keysDone = initialKeysDone
+ dec.child = initialChild
+ dec.called |= 1
+ return nil
+}
+
+// ObjectNull decodes the next key to a UnmarshalerJSONObject.
+func (dec *Decoder) ObjectNullReflect(v interface{}) error {
+ initialKeysDone := dec.keysDone
+ initialChild := dec.child
+ dec.keysDone = 0
+ dec.called = 0
+ dec.child |= 1
+ newCursor, err := dec.decodeObjectNullReflect(v)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.keysDone = initialKeysDone
+ dec.child = initialChild
+ dec.called |= 1
+ return nil
+}
+
// Array decodes the next key to a UnmarshalerJSONArray.
func (dec *Decoder) Array(value UnmarshalerJSONArray) error {
newCursor, err := dec.decodeArray(value)
@@ -891,6 +927,17 @@ func (dec *Decoder) Interface(value *interface{}) error {
return nil
}
+// Array decodes the next key to a UnmarshalerJSONArray.
+// func (dec *Decoder) ArrayNull(factory func() UnmarshalerJSONArray) error {
+// newCursor, err := dec.decodeArrayNull(factory)
+// if err != nil {
+// return err
+// }
+// dec.cursor = newCursor
+// dec.called |= 1
+// return nil
+// }
+
// Non exported
func isDigit(b byte) bool {
diff --git a/decode_array.go b/decode_array.go
@@ -62,6 +62,56 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
+// func (dec *Decoder) decodeArrayNull(factory func() UnmarshalerJSONArray) (int, error) {
+// // not an array not an error, but do not know what to do
+// // do not check syntax
+// for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+// switch dec.data[dec.cursor] {
+// case ' ', '\n', '\t', '\r', ',':
+// continue
+// case '[':
+// n := 0
+// dec.cursor = dec.cursor + 1
+// // array is open, char is not space start readings
+// for dec.nextChar() != 0 {
+// // closing array
+// if dec.data[dec.cursor] == ']' {
+// dec.cursor = dec.cursor + 1
+// return dec.cursor, nil
+// }
+// // calling unmarshall function for each element of the slice
+// err := arr.UnmarshalJSONArray(dec)
+// if err != nil {
+// return 0, err
+// }
+// n++
+// }
+// return 0, dec.raiseInvalidJSONErr(dec.cursor)
+// case 'n':
+// // is null
+// dec.cursor++
+// err := dec.assertNull()
+// if err != nil {
+// return 0, err
+// }
+// dec.cursor++
+// return dec.cursor, nil
+// case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+// // can't unmarshall to struct
+// // we skip array and set Error
+// dec.err = dec.makeInvalidUnmarshalErr(arr)
+// err := dec.skipData()
+// if err != nil {
+// return 0, err
+// }
+// return dec.cursor, nil
+// default:
+// return 0, dec.raiseInvalidJSONErr(dec.cursor)
+// }
+// }
+// return 0, dec.raiseInvalidJSONErr(dec.cursor)
+// }
+
func (dec *Decoder) skipArray() (int, error) {
var arraysOpen = 1
var arraysClosed = 0
diff --git a/decode_object.go b/decode_object.go
@@ -1,9 +1,14 @@
package gojay
import (
+ "reflect"
"unsafe"
)
+type IsNiler interface {
+ IsNil() bool
+}
+
// DecodeObject reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
//
// v must implement UnmarshalerJSONObject.
@@ -99,6 +104,178 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) {
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
+func (dec *Decoder) decodeObjectNull(factory func() UnmarshalerJSONObject) (int, error) {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ case '{':
+ var j = factory()
+ keys := j.NKeys()
+ dec.cursor = dec.cursor + 1
+ // if keys is zero we will parse all keys
+ // we run two loops for micro optimization
+ if keys == 0 {
+ for dec.cursor < dec.length || dec.read() {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ } else {
+ for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ }
+ // will get to that point when keysDone is not lower than keys anymore
+ // in that case, we make sure cursor goes to the end of object, but we skip
+ // unmarshalling
+ if dec.child&1 != 0 {
+ end, err := dec.skipObject()
+ dec.cursor = end
+ return dec.cursor, err
+ }
+ return dec.cursor, nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ dec.cursor++
+ return dec.cursor, nil
+ default:
+ // can't unmarshal to struct
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) decodeObjectNullReflect(v interface{}) (int, error) {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ case '{':
+ var vv = reflect.ValueOf(v).Elem()
+ var n = reflect.New(vv.Type().Elem())
+ vv.Set(n)
+ var j = n.Interface().(UnmarshalerJSONObject)
+ keys := j.NKeys()
+ dec.cursor = dec.cursor + 1
+ // if keys is zero we will parse all keys
+ // we run two loops for micro optimization
+ if keys == 0 {
+ for dec.cursor < dec.length || dec.read() {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ } else {
+ for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ }
+ // will get to that point when keysDone is not lower than keys anymore
+ // in that case, we make sure cursor goes to the end of object, but we skip
+ // unmarshalling
+ if dec.child&1 != 0 {
+ end, err := dec.skipObject()
+ dec.cursor = end
+ return dec.cursor, err
+ }
+ return dec.cursor, nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ dec.cursor++
+ return dec.cursor, nil
+ default:
+ // can't unmarshal to struct
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
func (dec *Decoder) skipObject() (int, error) {
var objectsOpen = 1