1
0
mirror of https://github.com/json-iterator/go.git synced 2025-06-15 22:50:24 +02:00

13 Commits
1.0.3 ... 1.0.4

Author SHA1 Message Date
f7279a603e fix out of range 2017-11-15 23:34:21 +08:00
9f088cbcc4 fix #195 when decode float as int, report it clearly 2017-11-15 23:25:12 +08:00
3c0e5762c4 fix #196 do not hard code 1 << 49 2017-11-15 23:15:31 +08:00
d394a135a1 #197 fix place holder encoder to use EncodeInterface, WriteToStream is unsafe when the real encoder is unknown 2017-11-15 22:56:23 +08:00
9fddff05f0 try to fix #194 with larger array 2017-11-11 08:31:44 +08:00
b1b003864e expose OptionalEncoder&OptionalDecoder; add attachment to Stream&Iterator for customized decoder/encoder 2017-11-08 11:41:45 +08:00
aed5a81f09 fix #190 handle empty input 2017-10-31 22:47:02 +08:00
f1258b01aa fix #191 do not always assume the object field is simple string 2017-10-31 22:38:41 +08:00
fbd210edfc Merge pull request #189 from ggaaooppeenngg/compatible-with-map
Fix standard compatiblility
2017-10-26 18:39:38 -05:00
640251ab91 Fix standard compatiblility
Non-nil but empty map with omitempty should be ignored.

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
2017-10-27 01:43:41 +08:00
06b2a7cf1d Merge pull request #188 from ggaaooppeenngg/compatible
Fix standard compatiblility
2017-10-26 06:41:01 -05:00
5fffb9b8f7 Fix standard compatiblility
Encode has trailing newline at the end.

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
2017-10-26 15:15:36 +08:00
7e3b776024 change jsoniter-sloppy to jsoniter_sloppy 2017-10-23 15:03:44 +08:00
22 changed files with 273 additions and 81 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea .idea
/coverage.txt /coverage.txt
/profile.out /profile.out
/bug_test.go

40
compatible_test.go Normal file
View 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()))
}

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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),
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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()

View File

@ -1,4 +1,4 @@
//+build jsoniter-sloppy //+build jsoniter_sloppy
package jsoniter package jsoniter

View File

@ -1,4 +1,4 @@
//+build !jsoniter-sloppy //+build !jsoniter_sloppy
package jsoniter package jsoniter

View File

@ -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

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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{

View File

@ -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

View File

@ -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()

View File

@ -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.

View File

@ -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) {

View File

@ -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++ {

View File

@ -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"}

View File

@ -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)
}

View File

@ -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
} }