You've already forked json-iterator
mirror of
https://github.com/json-iterator/go.git
synced 2025-06-15 22:50:24 +02:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
f7279a603e | |||
9f088cbcc4 | |||
3c0e5762c4 | |||
d394a135a1 | |||
9fddff05f0 | |||
b1b003864e | |||
aed5a81f09 | |||
f1258b01aa | |||
fbd210edfc | |||
640251ab91 | |||
06b2a7cf1d | |||
5fffb9b8f7 | |||
7e3b776024 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
.idea
|
.idea
|
||||||
/coverage.txt
|
/coverage.txt
|
||||||
/profile.out
|
/profile.out
|
||||||
|
/bug_test.go
|
||||||
|
40
compatible_test.go
Normal file
40
compatible_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package jsoniter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Standard Encoder has trailing newline.
|
||||||
|
func TestEncoderHasTrailingNewline(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
var buf, stdbuf bytes.Buffer
|
||||||
|
enc := ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||||
|
enc.Encode(1)
|
||||||
|
stdenc := json.NewEncoder(&stdbuf)
|
||||||
|
stdenc.Encode(1)
|
||||||
|
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-nil but empty map should be ignored.
|
||||||
|
func TestOmitempty(t *testing.T) {
|
||||||
|
o := struct {
|
||||||
|
A string `json:"a,omitempty"`
|
||||||
|
B string `json:"b,omitempty"`
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
}{
|
||||||
|
A: "a",
|
||||||
|
B: "b",
|
||||||
|
Annotations: map[string]string{},
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
var buf, stdbuf bytes.Buffer
|
||||||
|
enc := ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||||
|
enc.Encode(o)
|
||||||
|
stdenc := json.NewEncoder(&stdbuf)
|
||||||
|
stdenc.Encode(o)
|
||||||
|
should.Equal(string(stdbuf.Bytes()), string(buf.Bytes()))
|
||||||
|
}
|
@ -110,6 +110,7 @@ type Encoder struct {
|
|||||||
// Encode encode interface{} as JSON to io.Writer
|
// Encode encode interface{} as JSON to io.Writer
|
||||||
func (adapter *Encoder) Encode(val interface{}) error {
|
func (adapter *Encoder) Encode(val interface{}) error {
|
||||||
adapter.stream.WriteVal(val)
|
adapter.stream.WriteVal(val)
|
||||||
|
adapter.stream.WriteRaw("\n")
|
||||||
adapter.stream.Flush()
|
adapter.stream.Flush()
|
||||||
return adapter.stream.Error
|
return adapter.stream.Error
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -157,6 +158,8 @@ func (iter *Iterator) readAny() Any {
|
|||||||
return iter.readArrayAny()
|
return iter.readArrayAny()
|
||||||
case '-':
|
case '-':
|
||||||
return iter.readNumberAny(false)
|
return iter.readNumberAny(false)
|
||||||
|
case 0:
|
||||||
|
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
||||||
default:
|
default:
|
||||||
return iter.readNumberAny(true)
|
return iter.readNumberAny(true)
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,14 @@ type Config struct {
|
|||||||
UseNumber bool
|
UseNumber bool
|
||||||
TagKey string
|
TagKey string
|
||||||
ValidateJsonRawMessage bool
|
ValidateJsonRawMessage bool
|
||||||
|
ObjectFieldMustBeSimpleString bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type frozenConfig struct {
|
type frozenConfig struct {
|
||||||
configBeforeFrozen Config
|
configBeforeFrozen Config
|
||||||
sortMapKeys bool
|
sortMapKeys bool
|
||||||
indentionStep int
|
indentionStep int
|
||||||
|
objectFieldMustBeSimpleString bool
|
||||||
decoderCache unsafe.Pointer
|
decoderCache unsafe.Pointer
|
||||||
encoderCache unsafe.Pointer
|
encoderCache unsafe.Pointer
|
||||||
extensions []Extension
|
extensions []Extension
|
||||||
@ -63,7 +65,8 @@ var ConfigCompatibleWithStandardLibrary = Config{
|
|||||||
// ConfigFastest marshals float with only 6 digits precision
|
// ConfigFastest marshals float with only 6 digits precision
|
||||||
var ConfigFastest = Config{
|
var ConfigFastest = Config{
|
||||||
EscapeHTML: false,
|
EscapeHTML: false,
|
||||||
MarshalFloatWith6Digits: true,
|
MarshalFloatWith6Digits: true, // will lose precession
|
||||||
|
ObjectFieldMustBeSimpleString: true, // do not unescape object field
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
||||||
// Froze forge API from config
|
// Froze forge API from config
|
||||||
@ -72,6 +75,7 @@ func (cfg Config) Froze() API {
|
|||||||
frozenConfig := &frozenConfig{
|
frozenConfig := &frozenConfig{
|
||||||
sortMapKeys: cfg.SortMapKeys,
|
sortMapKeys: cfg.SortMapKeys,
|
||||||
indentionStep: cfg.IndentionStep,
|
indentionStep: cfg.IndentionStep,
|
||||||
|
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
||||||
streamPool: make(chan *Stream, 16),
|
streamPool: make(chan *Stream, 16),
|
||||||
iteratorPool: make(chan *Iterator, 16),
|
iteratorPool: make(chan *Iterator, 16),
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ type Iterator struct {
|
|||||||
captureStartedAt int
|
captureStartedAt int
|
||||||
captured []byte
|
captured []byte
|
||||||
Error error
|
Error error
|
||||||
|
Attachment interface{} // open for customized decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterator creates an empty Iterator instance
|
// NewIterator creates an empty Iterator instance
|
||||||
@ -167,7 +168,7 @@ func (iter *Iterator) isObjectEnd() bool {
|
|||||||
if c == '}' {
|
if c == '}' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
iter.ReportError("isObjectEnd", "object ended prematurely")
|
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,11 @@ func (iter *Iterator) ReadUint32() (ret uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||||
|
defer func() {
|
||||||
|
if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' {
|
||||||
|
iter.ReportError("readUint32", "can not decode float as int")
|
||||||
|
}
|
||||||
|
}()
|
||||||
ind := intDigits[c]
|
ind := intDigits[c]
|
||||||
if ind == 0 {
|
if ind == 0 {
|
||||||
return 0 // single zero
|
return 0 // single zero
|
||||||
@ -224,6 +229,11 @@ func (iter *Iterator) ReadUint64() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||||
|
defer func() {
|
||||||
|
if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' {
|
||||||
|
iter.ReportError("readUint64", "can not decode float as int")
|
||||||
|
}
|
||||||
|
}()
|
||||||
ind := intDigits[c]
|
ind := intDigits[c]
|
||||||
if ind == 0 {
|
if ind == 0 {
|
||||||
return 0 // single zero
|
return 0 // single zero
|
||||||
|
@ -19,7 +19,16 @@ func (iter *Iterator) ReadObject() (ret string) {
|
|||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
return string(iter.readObjectFieldAsBytes())
|
return string(iter.readObjectFieldAsBytes())
|
||||||
|
} else {
|
||||||
|
field := iter.ReadString()
|
||||||
|
c = iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if c == '}' {
|
if c == '}' {
|
||||||
return "" // end of object
|
return "" // end of object
|
||||||
@ -27,7 +36,16 @@ func (iter *Iterator) ReadObject() (ret string) {
|
|||||||
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
|
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
|
||||||
return
|
return
|
||||||
case ',':
|
case ',':
|
||||||
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
return string(iter.readObjectFieldAsBytes())
|
return string(iter.readObjectFieldAsBytes())
|
||||||
|
} else {
|
||||||
|
field := iter.ReadString()
|
||||||
|
c = iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
case '}':
|
case '}':
|
||||||
return "" // end of object
|
return "" // end of object
|
||||||
default:
|
default:
|
||||||
@ -44,17 +62,34 @@ func (iter *Iterator) readFieldHash() int32 {
|
|||||||
for i := iter.head; i < iter.tail; i++ {
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
// require ascii string and no escape
|
// require ascii string and no escape
|
||||||
b := iter.buf[i]
|
b := iter.buf[i]
|
||||||
|
if !iter.cfg.objectFieldMustBeSimpleString && b == '\\' {
|
||||||
|
iter.head = i
|
||||||
|
for _, b := range iter.readStringSlowPath() {
|
||||||
if 'A' <= b && b <= 'Z' {
|
if 'A' <= b && b <= 'Z' {
|
||||||
b += 'a' - 'A'
|
b += 'a' - 'A'
|
||||||
}
|
}
|
||||||
|
hash ^= int64(b)
|
||||||
|
hash *= 0x1000193
|
||||||
|
}
|
||||||
|
c = iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int32(hash)
|
||||||
|
}
|
||||||
if b == '"' {
|
if b == '"' {
|
||||||
iter.head = i + 1
|
iter.head = i + 1
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c != ':' {
|
if c != ':' {
|
||||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
return int32(hash)
|
return int32(hash)
|
||||||
}
|
}
|
||||||
|
if 'A' <= b && b <= 'Z' {
|
||||||
|
b += 'a' - 'A'
|
||||||
|
}
|
||||||
hash ^= int64(b)
|
hash ^= int64(b)
|
||||||
hash *= 0x1000193
|
hash *= 0x1000193
|
||||||
}
|
}
|
||||||
@ -80,18 +115,38 @@ func calcHash(str string) int32 {
|
|||||||
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
||||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
|
var fieldBytes []byte
|
||||||
|
var field string
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
field := iter.readObjectFieldAsBytes()
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
|
fieldBytes = iter.readObjectFieldAsBytes()
|
||||||
|
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
||||||
|
} else {
|
||||||
|
field = iter.ReadString()
|
||||||
|
c = iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !callback(iter, field) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
for c == ',' {
|
for c == ',' {
|
||||||
field = iter.readObjectFieldAsBytes()
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
|
fieldBytes = iter.readObjectFieldAsBytes()
|
||||||
|
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
||||||
|
} else {
|
||||||
|
field = iter.ReadString()
|
||||||
|
c = iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !callback(iter, field) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//+build jsoniter-sloppy
|
//+build jsoniter_sloppy
|
||||||
|
|
||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//+build !jsoniter-sloppy
|
//+build !jsoniter_sloppy
|
||||||
|
|
||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
|
|||||||
|
|
||||||
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
|
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
|
||||||
stream.Error = nil
|
stream.Error = nil
|
||||||
|
stream.Attachment = nil
|
||||||
select {
|
select {
|
||||||
case cfg.streamPool <- stream:
|
case cfg.streamPool <- stream:
|
||||||
return
|
return
|
||||||
@ -48,6 +49,7 @@ func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator {
|
|||||||
|
|
||||||
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
|
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
|
||||||
iter.Error = nil
|
iter.Error = nil
|
||||||
|
iter.Attachment = nil
|
||||||
select {
|
select {
|
||||||
case cfg.iteratorPool <- iter:
|
case cfg.iteratorPool <- iter:
|
||||||
return
|
return
|
||||||
|
@ -72,24 +72,24 @@ func init() {
|
|||||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
type optionalDecoder struct {
|
type OptionalDecoder struct {
|
||||||
valueType reflect.Type
|
ValueType reflect.Type
|
||||||
valueDecoder ValDecoder
|
ValueDecoder ValDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (decoder *optionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
if iter.ReadNil() {
|
if iter.ReadNil() {
|
||||||
*((*unsafe.Pointer)(ptr)) = nil
|
*((*unsafe.Pointer)(ptr)) = nil
|
||||||
} else {
|
} else {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
//pointer to null, we have to allocate memory to hold the value
|
//pointer to null, we have to allocate memory to hold the value
|
||||||
value := reflect.New(decoder.valueType)
|
value := reflect.New(decoder.ValueType)
|
||||||
newPtr := extractInterface(value.Interface()).word
|
newPtr := extractInterface(value.Interface()).word
|
||||||
decoder.valueDecoder.Decode(newPtr, iter)
|
decoder.ValueDecoder.Decode(newPtr, iter)
|
||||||
*((*uintptr)(ptr)) = uintptr(newPtr)
|
*((*uintptr)(ptr)) = uintptr(newPtr)
|
||||||
} else {
|
} else {
|
||||||
//reuse existing instance
|
//reuse existing instance
|
||||||
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,11 +113,31 @@ func (decoder *deferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type optionalEncoder struct {
|
type OptionalEncoder struct {
|
||||||
|
ValueEncoder ValEncoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *OptionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
|
stream.WriteNil()
|
||||||
|
} else {
|
||||||
|
encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *OptionalEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||||
|
WriteToStream(val, stream, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *OptionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
|
return *((*unsafe.Pointer)(ptr)) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalMapEncoder struct {
|
||||||
valueEncoder ValEncoder
|
valueEncoder ValEncoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *optionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (encoder *optionalMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
stream.WriteNil()
|
stream.WriteNil()
|
||||||
} else {
|
} else {
|
||||||
@ -125,15 +145,13 @@ func (encoder *optionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *optionalEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
func (encoder *optionalMapEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||||
WriteToStream(val, stream, encoder)
|
WriteToStream(val, stream, encoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
func (encoder *optionalMapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
p := *((*unsafe.Pointer)(ptr))
|
||||||
return true
|
return p == nil || encoder.valueEncoder.IsEmpty(p)
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type placeholderEncoder struct {
|
type placeholderEncoder struct {
|
||||||
@ -146,7 +164,7 @@ func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *placeholderEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
func (encoder *placeholderEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||||
WriteToStream(val, stream, encoder)
|
encoder.getRealEncoder().EncodeInterface(val, stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
@ -289,7 +307,7 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
|
|||||||
templateInterface := reflect.New(typ).Elem().Interface()
|
templateInterface := reflect.New(typ).Elem().Interface()
|
||||||
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
|
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
decoder = &optionalDecoder{typ.Elem(), decoder}
|
decoder = &OptionalDecoder{typ.Elem(), decoder}
|
||||||
}
|
}
|
||||||
return decoder, nil
|
return decoder, nil
|
||||||
}
|
}
|
||||||
@ -302,7 +320,7 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
|
|||||||
templateInterface := reflect.New(typ).Elem().Interface()
|
templateInterface := reflect.New(typ).Elem().Interface()
|
||||||
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
|
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
decoder = &optionalDecoder{typ.Elem(), decoder}
|
decoder = &OptionalDecoder{typ.Elem(), decoder}
|
||||||
}
|
}
|
||||||
return decoder, nil
|
return decoder, nil
|
||||||
}
|
}
|
||||||
@ -462,7 +480,7 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
|
|||||||
checkIsEmpty: checkIsEmpty,
|
checkIsEmpty: checkIsEmpty,
|
||||||
}
|
}
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
return encoder, nil
|
return encoder, nil
|
||||||
}
|
}
|
||||||
@ -489,7 +507,7 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
|
|||||||
checkIsEmpty: checkIsEmpty,
|
checkIsEmpty: checkIsEmpty,
|
||||||
}
|
}
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
return encoder, nil
|
return encoder, nil
|
||||||
}
|
}
|
||||||
@ -549,7 +567,7 @@ func createCheckIsEmpty(typ reflect.Type) (checkIsEmpty, error) {
|
|||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return &mapEncoder{}, nil
|
return &mapEncoder{}, nil
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return &optionalEncoder{}, nil
|
return &OptionalEncoder{}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||||
}
|
}
|
||||||
@ -660,7 +678,7 @@ func decoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &optionalDecoder{elemType, decoder}, nil
|
return &OptionalDecoder{elemType, decoder}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||||
@ -669,9 +687,9 @@ func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encoder := &optionalEncoder{elemEncoder}
|
encoder := &OptionalEncoder{elemEncoder}
|
||||||
if elemType.Kind() == reflect.Map {
|
if elemType.Kind() == reflect.Map {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
return encoder, nil
|
return encoder, nil
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func encoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if typ.Elem().Kind() == reflect.Map {
|
if typ.Elem().Kind() == reflect.Map {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
return &arrayEncoder{typ, typ.Elem(), encoder}, nil
|
return &arrayEncoder{typ, typ.Elem(), encoder}, nil
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
|
|||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
decoder := typeDecoders[typ.Elem().String()]
|
decoder := typeDecoders[typ.Elem().String()]
|
||||||
if decoder != nil {
|
if decoder != nil {
|
||||||
return &optionalDecoder{typ.Elem(), decoder}
|
return &OptionalDecoder{typ.Elem(), decoder}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -216,7 +216,7 @@ func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
|
|||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
encoder := typeEncoders[typ.Elem().String()]
|
encoder := typeEncoders[typ.Elem().String()]
|
||||||
if encoder != nil {
|
if encoder != nil {
|
||||||
return &optionalEncoder{encoder}
|
return &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -254,7 +254,7 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
|
|||||||
for _, binding := range structDescriptor.Fields {
|
for _, binding := range structDescriptor.Fields {
|
||||||
binding.levels = append([]int{i}, binding.levels...)
|
binding.levels = append([]int{i}, binding.levels...)
|
||||||
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
||||||
binding.Encoder = &optionalEncoder{binding.Encoder}
|
binding.Encoder = &OptionalEncoder{binding.Encoder}
|
||||||
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
|
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
|
||||||
binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
|
binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
|
||||||
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
|
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
|
||||||
@ -280,9 +280,10 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
|
|||||||
if len(fieldNames) > 0 && err != nil {
|
if len(fieldNames) > 0 && err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// map is stored as pointer in the struct
|
// map is stored as pointer in the struct,
|
||||||
|
// and treat nil or empty map as empty field
|
||||||
if encoder != nil && field.Type.Kind() == reflect.Map {
|
if encoder != nil && field.Type.Kind() == reflect.Map {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &optionalMapEncoder{encoder}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding := &Binding{
|
binding := &Binding{
|
||||||
|
@ -21,7 +21,7 @@ func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if typ.Elem().Kind() == reflect.Map {
|
if typ.Elem().Kind() == reflect.Map {
|
||||||
encoder = &optionalEncoder{encoder}
|
encoder = &OptionalEncoder{encoder}
|
||||||
}
|
}
|
||||||
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
|
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
|
||||||
}
|
}
|
||||||
@ -127,12 +127,10 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
|
|||||||
newVal := reflect.MakeSlice(sliceType, newLen, newCap)
|
newVal := reflect.MakeSlice(sliceType, newLen, newCap)
|
||||||
dst := unsafe.Pointer(newVal.Pointer())
|
dst := unsafe.Pointer(newVal.Pointer())
|
||||||
// copy old array into new array
|
// copy old array into new array
|
||||||
originalBytesCount := uintptr(slice.Len) * elementType.Size()
|
originalBytesCount := slice.Len * int(elementType.Size())
|
||||||
srcPtr := (*[1 << 30]byte)(slice.Data)
|
srcSliceHeader := (unsafe.Pointer)(&sliceHeader{slice.Data, originalBytesCount, originalBytesCount})
|
||||||
dstPtr := (*[1 << 30]byte)(dst)
|
dstSliceHeader := (unsafe.Pointer)(&sliceHeader{dst, originalBytesCount, originalBytesCount})
|
||||||
for i := uintptr(0); i < originalBytesCount; i++ {
|
copy(*(*[]byte)(dstSliceHeader), *(*[]byte)(srcSliceHeader))
|
||||||
dstPtr[i] = srcPtr[i]
|
|
||||||
}
|
|
||||||
slice.Data = dst
|
slice.Data = dst
|
||||||
slice.Len = newLen
|
slice.Len = newLen
|
||||||
slice.Cap = newCap
|
slice.Cap = newCap
|
||||||
|
@ -427,8 +427,18 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fieldBytes := iter.readObjectFieldAsBytes()
|
var fieldBytes []byte
|
||||||
field := *(*string)(unsafe.Pointer(&fieldBytes))
|
var field string
|
||||||
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
|
fieldBytes = iter.readObjectFieldAsBytes()
|
||||||
|
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
||||||
|
} else {
|
||||||
|
field = iter.ReadString()
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
}
|
||||||
fieldDecoder := decoder.fields[strings.ToLower(field)]
|
fieldDecoder := decoder.fields[strings.ToLower(field)]
|
||||||
if fieldDecoder == nil {
|
if fieldDecoder == nil {
|
||||||
iter.Skip()
|
iter.Skip()
|
||||||
@ -436,8 +446,16 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
fieldDecoder.Decode(ptr, iter)
|
fieldDecoder.Decode(ptr, iter)
|
||||||
}
|
}
|
||||||
for iter.nextToken() == ',' {
|
for iter.nextToken() == ',' {
|
||||||
fieldBytes = iter.readObjectFieldAsBytes()
|
if iter.cfg.objectFieldMustBeSimpleString {
|
||||||
|
fieldBytes := iter.readObjectFieldAsBytes()
|
||||||
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
||||||
|
} else {
|
||||||
|
field = iter.ReadString()
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c != ':' {
|
||||||
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
}
|
||||||
|
}
|
||||||
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
||||||
if fieldDecoder == nil {
|
if fieldDecoder == nil {
|
||||||
iter.Skip()
|
iter.Skip()
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stream is a io.Writer like object, with JSON specific write functions.
|
// stream is a io.Writer like object, with JSON specific write functions.
|
||||||
// Error is not returned as return value, but stored as Error member on this stream instance.
|
// Error is not returned as return value, but stored as Error member on this stream instance.
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
cfg *frozenConfig
|
cfg *frozenConfig
|
||||||
@ -13,6 +13,7 @@ type Stream struct {
|
|||||||
n int
|
n int
|
||||||
Error error
|
Error error
|
||||||
indention int
|
indention int
|
||||||
|
Attachment interface{} // open for customized encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStream create new stream instance.
|
// NewStream create new stream instance.
|
||||||
|
@ -22,7 +22,7 @@ func Test_new_encoder(t *testing.T) {
|
|||||||
encoder2 := NewEncoder(buf2)
|
encoder2 := NewEncoder(buf2)
|
||||||
encoder2.SetEscapeHTML(false)
|
encoder2.SetEscapeHTML(false)
|
||||||
encoder2.Encode([]int{1})
|
encoder2.Encode([]int{1})
|
||||||
should.Equal("[1]", buf2.String())
|
should.Equal("[1]\n", buf2.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
||||||
|
@ -530,6 +530,12 @@ func Test_null_as_number(t *testing.T) {
|
|||||||
should.Equal("", string(v2))
|
should.Equal("", string(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_float_as_int(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
var i int
|
||||||
|
should.NotNil(Unmarshal([]byte(`1.1`), &i))
|
||||||
|
}
|
||||||
|
|
||||||
func Benchmark_jsoniter_encode_int(b *testing.B) {
|
func Benchmark_jsoniter_encode_int(b *testing.B) {
|
||||||
stream := NewStream(ConfigDefault, ioutil.Discard, 64)
|
stream := NewStream(ConfigDefault, ioutil.Discard, 64)
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
|
@ -7,8 +7,24 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Test_write_empty_interface_via_placeholder(t *testing.T) {
|
||||||
|
fmt.Println(^uint(0) >> 1)
|
||||||
|
should := require.New(t)
|
||||||
|
m := map[uint32]interface{}{1: "hello"}
|
||||||
|
inf := reflect.ValueOf(m).MapIndex(reflect.ValueOf(uint32(1))).Interface()
|
||||||
|
encoder := &placeholderEncoder{
|
||||||
|
cfg: ConfigFastest.(*frozenConfig),
|
||||||
|
cacheKey: reflect.TypeOf(m).Elem(),
|
||||||
|
}
|
||||||
|
stream := ConfigFastest.BorrowStream(nil)
|
||||||
|
encoderOfType(ConfigFastest.(*frozenConfig), reflect.TypeOf(m).Elem())
|
||||||
|
encoder.EncodeInterface(inf, stream)
|
||||||
|
should.Equal(`"hello"`, string(stream.Buffer()))
|
||||||
|
}
|
||||||
|
|
||||||
func Test_write_array_of_interface(t *testing.T) {
|
func Test_write_array_of_interface(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
array := []interface{}{"hello"}
|
array := []interface{}{"hello"}
|
||||||
|
@ -328,3 +328,15 @@ func Test_decode_nested(t *testing.T) {
|
|||||||
t.Fatal(slice[2])
|
t.Fatal(slice[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_decode_field_with_escape(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
type TestObject struct {
|
||||||
|
Field1 string
|
||||||
|
}
|
||||||
|
var obj TestObject
|
||||||
|
should.Nil(ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(`{"Field\"1":"hello"}`), &obj))
|
||||||
|
should.Equal("", obj.Field1)
|
||||||
|
should.Nil(ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(`{"\u0046ield1":"hello"}`), &obj))
|
||||||
|
should.Equal("hello", obj.Field1)
|
||||||
|
}
|
||||||
|
@ -104,6 +104,11 @@ func Test_skip_and_return_bytes_with_reader(t *testing.T) {
|
|||||||
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
|
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_skip_empty(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
should.NotNil(Get([]byte("")).LastError())
|
||||||
|
}
|
||||||
|
|
||||||
type TestResp struct {
|
type TestResp struct {
|
||||||
Code uint64
|
Code uint64
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user