gojay

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

commit cef72a4f0bf03972e0264c36d0d1bad4884f8784
parent 0d1a893740b14aedd39b5ade2e7567645a40eb0b
Author: francoispqt <francois@parquet.ninja>
Date:   Sun, 26 Aug 2018 21:41:15 +0800

add decode object null

Diffstat:
Abenchmarks/decoder/decoder_null_test.go | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode.go | 47+++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_array.go | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_object.go | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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