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

25 Commits

Author SHA1 Message Date
36b14963da #153 fix invalid utf8 using same implementation as the standard library 2017-08-29 23:58:51 +08:00
f706335302 #153 fix critical bug: infinite loop when write string is invalid utf8 2017-08-29 23:39:43 +08:00
2dc0031b26 #152 gofmt 2017-08-25 12:53:23 +08:00
cdbd2ed810 #145 interface {} customizatoin is recursive 2017-08-22 10:39:01 +08:00
39e9d67807 Merge branch 'master' of https://github.com/json-iterator/go 2017-08-22 00:12:18 +08:00
2066b01acb #146 support config TagKey 2017-08-22 00:12:09 +08:00
ac3b3cd160 test []interface{} 2017-08-21 22:43:51 +08:00
887789156a Merge pull request #147 from thockin/output_tests
Add tests for int64
2017-08-11 12:55:48 +08:00
7df5a67d0d Add tests for int64 2017-08-10 20:58:49 -07:00
9c358632dc #144 make []byte support Unmarshaler&Marshaler 2017-08-09 13:59:40 +08:00
1cfa233923 #143 make jsoniter.Number same meaning as json.Number, however UseNumber still returns json.Number. 1.9 alias support should be added later 2017-08-05 07:22:53 +08:00
d249b05a85 rename ValueType, to avoid collision with json.Number 2017-08-05 07:10:15 +08:00
abbd16da6c #140 blind fix 2017-08-02 09:20:43 +08:00
b67201557a avoid gc issue 2017-08-01 08:34:38 +08:00
5124683f24 #140 try fix: maybe memory collected before assigned to existing object graph 2017-07-31 23:24:58 +08:00
4892de725b add ad 2017-07-31 21:49:02 +08:00
34a2174be3 #142 decode struct field should be case insensitiveyet another fix 2017-07-31 21:48:22 +08:00
24ecaff2a1 #142 decode struct field should be case insensitive, the bug only happen for struct with more than 10 fields 2017-07-31 20:50:07 +08:00
c15b4d116c #139 unmarshal non base64 into []byte 2017-07-19 12:04:22 +08:00
12cd299fa8 add benchmark for Skip() 2017-07-19 00:22:41 +08:00
60ba332980 acknowledge @mattn for #138 #137 #136 #135 2017-07-19 00:09:50 +08:00
f705934fbf #138 fix - without following digits; fix 1.e1 2017-07-18 23:48:40 +08:00
17a26a6e20 remove debug print 2017-07-18 23:24:21 +08:00
156284b028 #137 fix unicode surrogate incompatibility 2017-07-18 23:17:52 +08:00
6b6938829d #136 strconv.ParseFloat can not validate 1. , added extra validation for this special case 2017-07-18 22:19:52 +08:00
53 changed files with 1450 additions and 513 deletions

View File

@ -8,6 +8,10 @@
A high-performance 100% compatible drop-in replacement of "encoding/json" A high-performance 100% compatible drop-in replacement of "encoding/json"
```
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
```
# Benchmark # Benchmark
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
@ -70,6 +74,7 @@ go get github.com/json-iterator/go
Contributors Contributors
* [thockin](https://github.com/thockin) * [thockin](https://github.com/thockin)
* [mattn](https://github.com/mattn)
* [cch123](https://github.com/cch123) * [cch123](https://github.com/cch123)
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)

View File

@ -158,7 +158,7 @@ type tolerateEmptyArrayDecoder struct {
} }
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if iter.WhatIsNext() == jsoniter.Array { if iter.WhatIsNext() == jsoniter.ArrayValue {
iter.Skip() iter.Skip()
newIter := iter.Pool().BorrowIterator([]byte("{}")) newIter := iter.Pool().BorrowIterator([]byte("{}"))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
@ -174,11 +174,11 @@ type fuzzyStringDecoder struct {
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
*((*string)(ptr)) = string(number) *((*string)(ptr)) = string(number)
case jsoniter.String: case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString() *((*string)(ptr)) = iter.ReadString()
default: default:
iter.ReportError("fuzzyStringDecoder", "not number or string") iter.ReportError("fuzzyStringDecoder", "not number or string")
@ -193,11 +193,11 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
str = string(number) str = string(number)
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
default: default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string") iter.ReportError("fuzzyIntegerDecoder", "not number or string")
@ -218,9 +218,9 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float32)(ptr)) = iter.ReadFloat32() *((*float32)(ptr)) = iter.ReadFloat32()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
@ -240,9 +240,9 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float64)(ptr)) = iter.ReadFloat64() *((*float64)(ptr)) = iter.ReadFloat64()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)

View File

@ -90,7 +90,7 @@ func (adapter *Decoder) Buffered() io.Reader {
return bytes.NewReader(remaining) return bytes.NewReader(remaining)
} }
// UseNumber for number JSON element, use float64 or json.Number (alias of string) // UseNumber for number JSON element, use float64 or json.NumberValue (alias of string)
func (adapter *Decoder) UseNumber() { func (adapter *Decoder) UseNumber() {
origCfg := adapter.iter.cfg.configBeforeFrozen origCfg := adapter.iter.cfg.configBeforeFrozen
origCfg.UseNumber = true origCfg.UseNumber = true

View File

@ -13,7 +13,7 @@ type arrayLazyAny struct {
} }
func (any *arrayLazyAny) ValueType() ValueType { func (any *arrayLazyAny) ValueType() ValueType {
return Array return ArrayValue
} }
func (any *arrayLazyAny) MustBeValid() Any { func (any *arrayLazyAny) MustBeValid() Any {
@ -117,7 +117,7 @@ func (any *arrayLazyAny) Get(path ...interface{}) Any {
arr := make([]Any, 0) arr := make([]Any, 0)
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *Iterator) bool {
found := iter.readAny().Get(path[1:]...) found := iter.readAny().Get(path[1:]...)
if found.ValueType() != Invalid { if found.ValueType() != InvalidValue {
arr = append(arr, found) arr = append(arr, found)
} }
return true return true
@ -162,7 +162,7 @@ func wrapArray(val interface{}) *arrayAny {
} }
func (any *arrayAny) ValueType() ValueType { func (any *arrayAny) ValueType() ValueType {
return Array return ArrayValue
} }
func (any *arrayAny) MustBeValid() Any { func (any *arrayAny) MustBeValid() Any {
@ -253,7 +253,7 @@ func (any *arrayAny) Get(path ...interface{}) Any {
mappedAll := make([]Any, 0) mappedAll := make([]Any, 0)
for i := 0; i < any.val.Len(); i++ { for i := 0; i < any.val.Len(); i++ {
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...) mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll = append(mappedAll, mapped) mappedAll = append(mappedAll, mapped)
} }
} }

View File

@ -61,7 +61,7 @@ func (any *trueAny) GetInterface() interface{} {
} }
func (any *trueAny) ValueType() ValueType { func (any *trueAny) ValueType() ValueType {
return Bool return BoolValue
} }
func (any *trueAny) MustBeValid() Any { func (any *trueAny) MustBeValid() Any {
@ -129,7 +129,7 @@ func (any *falseAny) GetInterface() interface{} {
} }
func (any *falseAny) ValueType() ValueType { func (any *falseAny) ValueType() ValueType {
return Bool return BoolValue
} }
func (any *falseAny) MustBeValid() Any { func (any *falseAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *floatAny) Parse() *Iterator {
} }
func (any *floatAny) ValueType() ValueType { func (any *floatAny) ValueType() ValueType {
return Number return NumberValue
} }
func (any *floatAny) MustBeValid() Any { func (any *floatAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *int32Any) LastError() error {
} }
func (any *int32Any) ValueType() ValueType { func (any *int32Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *int32Any) MustBeValid() Any { func (any *int32Any) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *int64Any) LastError() error {
} }
func (any *int64Any) ValueType() ValueType { func (any *int64Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *int64Any) MustBeValid() Any { func (any *int64Any) MustBeValid() Any {

View File

@ -16,7 +16,7 @@ func (any *invalidAny) LastError() error {
} }
func (any *invalidAny) ValueType() ValueType { func (any *invalidAny) ValueType() ValueType {
return Invalid return InvalidValue
} }
func (any *invalidAny) MustBeValid() Any { func (any *invalidAny) MustBeValid() Any {

View File

@ -9,7 +9,7 @@ func (any *nilAny) LastError() error {
} }
func (any *nilAny) ValueType() ValueType { func (any *nilAny) ValueType() ValueType {
return Nil return NilValue
} }
func (any *nilAny) MustBeValid() Any { func (any *nilAny) MustBeValid() Any {

View File

@ -10,7 +10,7 @@ type numberLazyAny struct {
} }
func (any *numberLazyAny) ValueType() ValueType { func (any *numberLazyAny) ValueType() ValueType {
return Number return NumberValue
} }
func (any *numberLazyAny) MustBeValid() Any { func (any *numberLazyAny) MustBeValid() Any {

View File

@ -13,7 +13,7 @@ type objectLazyAny struct {
} }
func (any *objectLazyAny) ValueType() ValueType { func (any *objectLazyAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *objectLazyAny) MustBeValid() Any { func (any *objectLazyAny) MustBeValid() Any {
@ -91,7 +91,7 @@ func (any *objectLazyAny) Get(path ...interface{}) Any {
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
iter.ReadMapCB(func(iter *Iterator, field string) bool { iter.ReadMapCB(func(iter *Iterator, field string) bool {
mapped := locatePath(iter, path[1:]) mapped := locatePath(iter, path[1:])
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[field] = mapped mappedAll[field] = mapped
} }
return true return true
@ -149,7 +149,7 @@ func wrapStruct(val interface{}) *objectAny {
} }
func (any *objectAny) ValueType() ValueType { func (any *objectAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *objectAny) MustBeValid() Any { func (any *objectAny) MustBeValid() Any {
@ -224,7 +224,7 @@ func (any *objectAny) Get(path ...interface{}) Any {
field := any.val.Field(i) field := any.val.Field(i)
if field.CanInterface() { if field.CanInterface() {
mapped := Wrap(field.Interface()).Get(path[1:]...) mapped := Wrap(field.Interface()).Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[any.val.Type().Field(i).Name] = mapped mappedAll[any.val.Type().Field(i).Name] = mapped
} }
} }
@ -268,7 +268,7 @@ func wrapMap(val interface{}) *mapAny {
} }
func (any *mapAny) ValueType() ValueType { func (any *mapAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *mapAny) MustBeValid() Any { func (any *mapAny) MustBeValid() Any {
@ -337,7 +337,7 @@ func (any *mapAny) Get(path ...interface{}) Any {
keyAsStr := key.String() keyAsStr := key.String()
element := Wrap(any.val.MapIndex(key).Interface()) element := Wrap(any.val.MapIndex(key).Interface())
mapped := element.Get(path[1:]...) mapped := element.Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[keyAsStr] = mapped mappedAll[keyAsStr] = mapped
} }
} }

View File

@ -22,7 +22,7 @@ func (any *stringAny) Parse() *Iterator {
} }
func (any *stringAny) ValueType() ValueType { func (any *stringAny) ValueType() ValueType {
return String return StringValue
} }
func (any *stringAny) MustBeValid() Any { func (any *stringAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *uint32Any) LastError() error {
} }
func (any *uint32Any) ValueType() ValueType { func (any *uint32Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *uint32Any) MustBeValid() Any { func (any *uint32Any) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *uint64Any) LastError() error {
} }
func (any *uint64Any) ValueType() ValueType { func (any *uint64Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *uint64Any) MustBeValid() Any { func (any *uint64Any) MustBeValid() Any {

View File

@ -17,6 +17,7 @@ type Config struct {
EscapeHTML bool EscapeHTML bool
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
TagKey string
} }
type frozenConfig struct { type frozenConfig struct {
@ -88,13 +89,20 @@ func (cfg Config) Froze() API {
func (cfg *frozenConfig) useNumber() { func (cfg *frozenConfig) useNumber() {
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == Number { if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) *((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else { } else {
*((*interface{})(ptr)) = iter.Read() *((*interface{})(ptr)) = iter.Read()
} }
}}) }})
} }
func (cfg *frozenConfig) getTagKey() string {
tagKey := cfg.configBeforeFrozen.TagKey
if tagKey == "" {
return "json"
}
return tagKey
}
func (cfg *frozenConfig) registerExtension(extension Extension) { func (cfg *frozenConfig) registerExtension(extension Extension) {
cfg.extensions = append(cfg.extensions, extension) cfg.extensions = append(cfg.extensions, extension)

View File

@ -10,20 +10,20 @@ import (
type ValueType int type ValueType int
const ( const (
// Invalid invalid JSON element // InvalidValue invalid JSON element
Invalid ValueType = iota InvalidValue ValueType = iota
// String JSON element "string" // StringValue JSON element "string"
String StringValue
// Number JSON element 100 or 0.10 // NumberValue JSON element 100 or 0.10
Number NumberValue
// Nil JSON element null // NilValue JSON element null
Nil NilValue
// Bool JSON element true or false // BoolValue JSON element true or false
Bool BoolValue
// Array JSON element [] // ArrayValue JSON element []
Array ArrayValue
// Object JSON element {} // ObjectValue JSON element {}
Object ObjectValue
) )
var hexDigits []byte var hexDigits []byte
@ -45,25 +45,25 @@ func init() {
} }
valueTypes = make([]ValueType, 256) valueTypes = make([]ValueType, 256)
for i := 0; i < len(valueTypes); i++ { for i := 0; i < len(valueTypes); i++ {
valueTypes[i] = Invalid valueTypes[i] = InvalidValue
} }
valueTypes['"'] = String valueTypes['"'] = StringValue
valueTypes['-'] = Number valueTypes['-'] = NumberValue
valueTypes['0'] = Number valueTypes['0'] = NumberValue
valueTypes['1'] = Number valueTypes['1'] = NumberValue
valueTypes['2'] = Number valueTypes['2'] = NumberValue
valueTypes['3'] = Number valueTypes['3'] = NumberValue
valueTypes['4'] = Number valueTypes['4'] = NumberValue
valueTypes['5'] = Number valueTypes['5'] = NumberValue
valueTypes['6'] = Number valueTypes['6'] = NumberValue
valueTypes['7'] = Number valueTypes['7'] = NumberValue
valueTypes['8'] = Number valueTypes['8'] = NumberValue
valueTypes['9'] = Number valueTypes['9'] = NumberValue
valueTypes['t'] = Bool valueTypes['t'] = BoolValue
valueTypes['f'] = Bool valueTypes['f'] = BoolValue
valueTypes['n'] = Nil valueTypes['n'] = NilValue
valueTypes['['] = Array valueTypes['['] = ArrayValue
valueTypes['{'] = Object valueTypes['{'] = ObjectValue
} }
// Iterator is a io.Reader like object, with JSON specific read functions. // Iterator is a io.Reader like object, with JSON specific read functions.
@ -270,29 +270,33 @@ func (iter *Iterator) unreadByte() {
func (iter *Iterator) Read() interface{} { func (iter *Iterator) Read() interface{} {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case String: case StringValue:
return iter.ReadString() return iter.ReadString()
case Number: case NumberValue:
if iter.cfg.configBeforeFrozen.UseNumber { if iter.cfg.configBeforeFrozen.UseNumber {
return json.Number(iter.readNumberAsString()) return json.Number(iter.readNumberAsString())
} }
return iter.ReadFloat64() return iter.ReadFloat64()
case Nil: case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l') iter.skipFourBytes('n', 'u', 'l', 'l')
return nil return nil
case Bool: case BoolValue:
return iter.ReadBool() return iter.ReadBool()
case Array: case ArrayValue:
arr := []interface{}{} arr := []interface{}{}
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *Iterator) bool {
arr = append(arr, iter.Read()) var elem interface{}
iter.ReadVal(&elem)
arr = append(arr, elem)
return true return true
}) })
return arr return arr
case Object: case ObjectValue:
obj := map[string]interface{}{} obj := map[string]interface{}{}
iter.ReadMapCB(func(Iter *Iterator, field string) bool { iter.ReadMapCB(func(Iter *Iterator, field string) bool {
obj[field] = iter.Read() var elem interface{}
iter.ReadVal(&elem)
obj[field] = elem
return true return true
}) })
return obj return obj

View File

@ -4,6 +4,7 @@ import (
"io" "io"
"math/big" "math/big"
"strconv" "strconv"
"strings"
"unsafe" "unsafe"
) )
@ -129,6 +130,9 @@ non_decimal_loop:
if c == '.' { if c == '.' {
i++ i++
decimalPlaces := 0 decimalPlaces := 0
if i == iter.tail {
return iter.readFloat32SlowPath()
}
for ; i < iter.tail; i++ { for ; i < iter.tail; i++ {
c = iter.buf[i] c = iter.buf[i]
ind := floatDigits[c] ind := floatDigits[c]
@ -189,12 +193,9 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
return return
} }
if len(str) == 0 { errMsg := validateFloat(str)
iter.ReportError("readFloat32SlowPath", "empty number") if errMsg != "" {
return iter.ReportError("readFloat32SlowPath", errMsg)
}
if str[0] == '-' {
iter.ReportError("readFloat32SlowPath", "-- is not valid")
return return
} }
val, err := strconv.ParseFloat(str, 32) val, err := strconv.ParseFloat(str, 32)
@ -270,6 +271,9 @@ non_decimal_loop:
if c == '.' { if c == '.' {
i++ i++
decimalPlaces := 0 decimalPlaces := 0
if i == iter.tail {
return iter.readFloat64SlowPath()
}
for ; i < iter.tail; i++ { for ; i < iter.tail; i++ {
c = iter.buf[i] c = iter.buf[i]
ind := floatDigits[c] ind := floatDigits[c]
@ -301,12 +305,9 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
return return
} }
if len(str) == 0 { errMsg := validateFloat(str)
iter.ReportError("readFloat64SlowPath", "empty number") if errMsg != "" {
return iter.ReportError("readFloat64SlowPath", errMsg)
}
if str[0] == '-' {
iter.ReportError("readFloat64SlowPath", "-- is not valid")
return return
} }
val, err := strconv.ParseFloat(str, 64) val, err := strconv.ParseFloat(str, 64)
@ -316,3 +317,25 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
} }
return val return val
} }
func validateFloat(str string) string {
// strconv.ParseFloat is not validating `1.` or `1.e1`
if len(str) == 0 {
return "empty number"
}
if str[0] == '-' {
return "-- is not valid"
}
dotPos := strings.IndexByte(str, '.')
if dotPos != -1 {
if dotPos == len(str)-1 {
return "dot can not be last character"
}
switch str[dotPos+1] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
return "missing digit after dot"
}
}
return ""
}

View File

@ -14,7 +14,7 @@ func (iter *Iterator) ReadNil() (ret bool) {
return false return false
} }
// ReadBool reads a json object as Bool // ReadBool reads a json object as BoolValue
func (iter *Iterator) ReadBool() (ret bool) { func (iter *Iterator) ReadBool() (ret bool) {
c := iter.nextToken() c := iter.nextToken()
if c == 't' { if c == 't' {

View File

@ -21,12 +21,24 @@ func (iter *Iterator) trySkipNumber() bool {
if dotFound { if dotFound {
iter.ReportError("validateNumber", `more than one dot found in number`) iter.ReportError("validateNumber", `more than one dot found in number`)
return true // already failed return true // already failed
} else {
dotFound = true
} }
if i+1 == iter.tail {
return false
}
c = iter.buf[i+1]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
iter.ReportError("validateNumber", `missing digit after dot`)
return true // already failed
}
dotFound = true
default: default:
switch c { switch c {
case ',', ']', '}', ' ', '\t', '\n', '\r': case ',', ']', '}', ' ', '\t', '\n', '\r':
if iter.head == i {
return false // if - without following digits
}
iter.head = i iter.head = i
return true // must be valid return true // must be valid
} }

View File

@ -42,58 +42,7 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
} }
if c == '\\' { if c == '\\' {
c = iter.readByte() c = iter.readByte()
switch c { str = iter.readEscapedChar(c, str)
case 'u', 'U':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return
}
if c != '\\' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \ not found`)
return
}
c = iter.readByte()
if iter.Error != nil {
return
}
if c != 'u' && c != 'U' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \u not found`)
return
}
r2 := iter.readU4()
if iter.Error != nil {
return
}
combined := utf16.DecodeRune(r, r2)
str = appendRune(str, combined)
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("ReadString",
`invalid escape char after \`)
return
}
} else { } else {
str = append(str, c) str = append(str, c)
} }
@ -102,6 +51,66 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
return return
} }
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
switch c {
case 'u':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != '\\' {
iter.unreadByte()
str = appendRune(str, r)
return str
}
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != 'u' {
str = appendRune(str, r)
return iter.readEscapedChar(c, str)
}
r2 := iter.readU4()
if iter.Error != nil {
return nil
}
combined := utf16.DecodeRune(r, r2)
if combined == '\uFFFD' {
str = appendRune(str, r)
str = appendRune(str, r2)
} else {
str = appendRune(str, combined)
}
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("ReadString",
`invalid escape char after \`)
return nil
}
return str
}
// ReadStringAsSlice read string from iterator without copying into string form. // ReadStringAsSlice read string from iterator without copying into string form.
// The []byte can not be kept, as it will change after next iterator call. // The []byte can not be kept, as it will change after next iterator call.
func (iter *Iterator) ReadStringAsSlice() (ret []byte) { func (iter *Iterator) ReadStringAsSlice() (ret []byte) {

15
feature_json_number.go Normal file
View File

@ -0,0 +1,15 @@
package jsoniter
import "encoding/json"
type Number string
func CastJsonNumber(val interface{}) (string, bool) {
switch typedVal := val.(type) {
case json.Number:
return string(typedVal), true
case Number:
return string(typedVal), true
}
return "", false
}

View File

@ -51,6 +51,7 @@ func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
} }
var jsonNumberType reflect.Type var jsonNumberType reflect.Type
var jsoniterNumberType reflect.Type
var jsonRawMessageType reflect.Type var jsonRawMessageType reflect.Type
var jsoniterRawMessageType reflect.Type var jsoniterRawMessageType reflect.Type
var anyType reflect.Type var anyType reflect.Type
@ -61,6 +62,7 @@ var textUnmarshalerType reflect.Type
func init() { func init() {
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem() jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
jsoniterNumberType = reflect.TypeOf((*Number)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem() jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem() jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem()
anyType = reflect.TypeOf((*Any)(nil)).Elem() anyType = reflect.TypeOf((*Any)(nil)).Elem()
@ -280,8 +282,8 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
if typ.AssignableTo(jsonNumberType) { if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil return &jsonNumberCodec{}, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { if typ.AssignableTo(jsoniterNumberType) {
return &base64Codec{}, nil return &jsoniterNumberCodec{}, nil
} }
if typ.Implements(unmarshalerType) { if typ.Implements(unmarshalerType) {
templateInterface := reflect.New(typ).Elem().Interface() templateInterface := reflect.New(typ).Elem().Interface()
@ -309,6 +311,13 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)} var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
return decoder, nil return decoder, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
sliceDecoder, err := prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ))
if err != nil {
return nil, err
}
return &base64Codec{sliceDecoder: sliceDecoder}, nil
}
if typ.Implements(anyType) { if typ.Implements(anyType) {
return &anyCodec{}, nil return &anyCodec{}, nil
} }
@ -439,8 +448,8 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
if typ.AssignableTo(jsonNumberType) { if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil return &jsonNumberCodec{}, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { if typ.AssignableTo(jsoniterNumberType) {
return &base64Codec{typ}, nil return &jsoniterNumberCodec{}, nil
} }
if typ.Implements(marshalerType) { if typ.Implements(marshalerType) {
checkIsEmpty, err := createCheckIsEmpty(typ) checkIsEmpty, err := createCheckIsEmpty(typ)
@ -472,6 +481,9 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
} }
return encoder, nil return encoder, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
return &base64Codec{}, nil
}
if typ.Implements(anyType) { if typ.Implements(anyType) {
return &anyCodec{}, nil return &anyCodec{}, nil
} }

View File

@ -227,7 +227,7 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
bindings := []*Binding{} bindings := []*Binding{}
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
tag := field.Tag.Get("json") tag := field.Tag.Get(cfg.getTagKey())
tagParts := strings.Split(tag, ",") tagParts := strings.Split(tag, ",")
if tag == "-" { if tag == "-" {
continue continue
@ -373,7 +373,7 @@ func (bindings sortableBindings) Swap(i, j int) {
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) { func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
shouldOmitEmpty := false shouldOmitEmpty := false
tagParts := strings.Split(binding.Field.Tag.Get("json"), ",") tagParts := strings.Split(binding.Field.Tag.Get(cfg.getTagKey()), ",")
for _, tagPart := range tagParts[1:] { for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" { if tagPart == "omitempty" {
shouldOmitEmpty = true shouldOmitEmpty = true

View File

@ -4,7 +4,6 @@ import (
"encoding" "encoding"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"reflect"
"unsafe" "unsafe"
) )
@ -386,6 +385,25 @@ func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.Number)(ptr))) == 0 return len(*((*json.Number)(ptr))) == 0
} }
type jsoniterNumberCodec struct {
}
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
}
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*Number)(ptr))))
}
func (codec *jsoniterNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(Number)))
}
func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*Number)(ptr))) == 0
}
type jsonRawMessageCodec struct { type jsonRawMessageCodec struct {
} }
@ -425,7 +443,7 @@ func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
} }
type base64Codec struct { type base64Codec struct {
actualType reflect.Type sliceDecoder ValDecoder
} }
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
@ -436,21 +454,28 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
ptrSlice.Data = nil ptrSlice.Data = nil
return return
} }
encoding := base64.StdEncoding switch iter.WhatIsNext() {
src := iter.SkipAndReturnBytes() case StringValue:
src = src[1 : len(src)-1] encoding := base64.StdEncoding
decodedLen := encoding.DecodedLen(len(src)) src := iter.SkipAndReturnBytes()
dst := make([]byte, decodedLen) src = src[1 : len(src)-1]
len, err := encoding.Decode(dst, src) decodedLen := encoding.DecodedLen(len(src))
if err != nil { dst := make([]byte, decodedLen)
iter.ReportError("decode base64", err.Error()) len, err := encoding.Decode(dst, src)
} else { if err != nil {
dst = dst[:len] iter.ReportError("decode base64", err.Error())
dstSlice := (*sliceHeader)(unsafe.Pointer(&dst)) } else {
ptrSlice := (*sliceHeader)(ptr) dst = dst[:len]
ptrSlice.Data = dstSlice.Data dstSlice := (*sliceHeader)(unsafe.Pointer(&dst))
ptrSlice.Cap = dstSlice.Cap ptrSlice := (*sliceHeader)(ptr)
ptrSlice.Len = dstSlice.Len ptrSlice.Data = dstSlice.Data
ptrSlice.Cap = dstSlice.Cap
ptrSlice.Len = dstSlice.Len
}
case ArrayValue:
codec.sliceDecoder.Decode(ptr, iter)
default:
iter.ReportError("base64Codec", "invalid input")
} }
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unsafe" "unsafe"
) )
@ -28,7 +29,7 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
if old.toName != toName { if old.toName != toName {
continue continue
} }
old.ignored, new.ignored = resolveConflictBinding(old.binding, new.binding) old.ignored, new.ignored = resolveConflictBinding(cfg, old.binding, new.binding)
} }
orderedBindings = append(orderedBindings, new) orderedBindings = append(orderedBindings, new)
} }
@ -48,9 +49,9 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil
} }
func resolveConflictBinding(old, new *Binding) (ignoreOld, ignoreNew bool) { func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
newTagged := new.Field.Tag.Get("json") != "" newTagged := new.Field.Tag.Get(cfg.getTagKey()) != ""
oldTagged := old.Field.Tag.Get("json") != "" oldTagged := old.Field.Tag.Get(cfg.getTagKey()) != ""
if newTagged { if newTagged {
if oldTagged { if oldTagged {
if len(old.levels) > len(new.levels) { if len(old.levels) > len(new.levels) {
@ -90,7 +91,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
bindings[fromName] = binding bindings[fromName] = binding
continue continue
} }
ignoreOld, ignoreNew := resolveConflictBinding(old, binding) ignoreOld, ignoreNew := resolveConflictBinding(cfg, old, binding)
if ignoreOld { if ignoreOld {
delete(bindings, fromName) delete(bindings, fromName)
} }
@ -101,7 +102,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
} }
fields := map[string]*structFieldDecoder{} fields := map[string]*structFieldDecoder{}
for k, binding := range bindings { for k, binding := range bindings {
fields[k] = binding.Decoder.(*structFieldDecoder) fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
} }
return createStructDecoder(typ, fields) return createStructDecoder(typ, fields)
} }

View File

@ -124,7 +124,8 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
} }
} }
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer()) newVal := reflect.MakeSlice(sliceType, newLen, newCap)
dst := unsafe.Pointer(newVal.Pointer())
// copy old array into new array // copy old array into new array
originalBytesCount := uintptr(slice.Len) * elementType.Size() originalBytesCount := uintptr(slice.Len) * elementType.Size()
srcPtr := (*[1 << 30]byte)(slice.Data) srcPtr := (*[1 << 30]byte)(slice.Data)
@ -132,16 +133,17 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
for i := uintptr(0); i < originalBytesCount; i++ { for i := uintptr(0); i < originalBytesCount; i++ {
dstPtr[i] = srcPtr[i] dstPtr[i] = srcPtr[i]
} }
slice.Data = dst
slice.Len = newLen slice.Len = newLen
slice.Cap = newCap slice.Cap = newCap
slice.Data = dst
} }
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) { func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
if expectedCap <= slice.Cap { if expectedCap <= slice.Cap {
return return
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer()) newVal := reflect.MakeSlice(sliceType, 0, expectedCap)
slice.Cap = expectedCap dst := unsafe.Pointer(newVal.Pointer())
slice.Data = dst slice.Data = dst
slice.Cap = expectedCap
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unsafe" "unsafe"
) )
@ -428,7 +429,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
} }
fieldBytes := iter.readObjectFieldAsBytes() fieldBytes := iter.readObjectFieldAsBytes()
field := *(*string)(unsafe.Pointer(&fieldBytes)) field := *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder := decoder.fields[field] fieldDecoder := decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()
} else { } else {
@ -437,7 +438,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
for iter.nextToken() == ',' { for iter.nextToken() == ',' {
fieldBytes = iter.readObjectFieldAsBytes() fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes)) field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field] fieldDecoder = decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()
} else { } else {
@ -455,7 +456,7 @@ type skipObjectDecoder struct {
func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
if valueType != Object && valueType != Nil { if valueType != ObjectValue && valueType != NilValue {
iter.ReportError("skipObjectDecoder", "expect object or null") iter.ReportError("skipObjectDecoder", "expect object or null")
return return
} }

View File

@ -289,6 +289,8 @@ func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen
if start < i { if start < i {
stream.WriteRaw(s[start:i]) stream.WriteRaw(s[start:i])
} }
stream.WriteRaw(`\ufffd`)
i++
start = i start = i
continue continue
} }

View File

@ -9,10 +9,10 @@ import (
func Test_read_empty_array_as_any(t *testing.T) { func Test_read_empty_array_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Array, any.Get().ValueType()) should.Equal(ArrayValue, any.Get().ValueType())
should.Equal(Invalid, any.Get(0.3).ValueType()) should.Equal(InvalidValue, any.Get(0.3).ValueType())
should.Equal(0, any.Size()) should.Equal(0, any.Size())
should.Equal(Array, any.ValueType()) should.Equal(ArrayValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32()) should.Equal(int32(0), any.ToInt32())
@ -101,7 +101,7 @@ func Test_array_wrapper_any_get_all(t *testing.T) {
{5, 6}, {5, 6},
}) })
should.Equal("[1,3,5]", any.Get('*', 0).ToString()) should.Equal("[1,3,5]", any.Get('*', 0).ToString())
should.Equal(Array, any.ValueType()) should.Equal(ArrayValue, any.ValueType())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(1, any.Get(0, 0).ToInt()) should.Equal(1, any.Get(0, 0).ToInt())
} }
@ -109,14 +109,14 @@ func Test_array_wrapper_any_get_all(t *testing.T) {
func Test_array_lazy_any_get_invalid(t *testing.T) { func Test_array_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Invalid, any.Get(1, 1).ValueType()) should.Equal(InvalidValue, any.Get(1, 1).ValueType())
should.NotNil(any.Get(1, 1).LastError()) should.NotNil(any.Get(1, 1).LastError())
should.Equal(Invalid, any.Get("1").ValueType()) should.Equal(InvalidValue, any.Get("1").ValueType())
should.NotNil(any.Get("1").LastError()) should.NotNil(any.Get("1").LastError())
} }
func Test_invalid_array(t *testing.T) { func Test_invalid_array(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("["), 0) any := Get([]byte("["), 0)
should.Equal(Invalid, any.ValueType()) should.Equal(InvalidValue, any.ValueType())
} }

View File

@ -53,12 +53,12 @@ func Test_write_bool_to_stream(t *testing.T) {
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("true", string(stream.Buffer())) should.Equal("true", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
any = Get([]byte("false")) any = Get([]byte("false"))
stream = NewStream(ConfigDefault, nil, 32) stream = NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("false", string(stream.Buffer())) should.Equal("false", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
} }

View File

@ -96,7 +96,7 @@ func Test_read_float_to_any(t *testing.T) {
should.Equal(uint(0), any2.ToUint()) should.Equal(uint(0), any2.ToUint())
should.Equal(uint32(0), any2.ToUint32()) should.Equal(uint32(0), any2.ToUint32())
should.Equal(uint64(0), any2.ToUint64()) should.Equal(uint64(0), any2.ToUint64())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal("1.23E+01", any.ToString()) should.Equal("1.23E+01", any.ToString())
} }

View File

@ -123,7 +123,7 @@ func Test_read_int64_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -141,7 +141,7 @@ func Test_read_int32_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -160,7 +160,7 @@ func Test_read_uint32_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -179,7 +179,7 @@ func Test_read_uint64_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -193,5 +193,5 @@ func Test_int_lazy_any_get(t *testing.T) {
any := Get([]byte("1234")) any := Get([]byte("1234"))
// panic!! // panic!!
//should.Equal(any.LastError(), io.EOF) //should.Equal(any.LastError(), io.EOF)
should.Equal(Invalid, any.Get(1, "2").ValueType()) should.Equal(InvalidValue, any.Get(1, "2").ValueType())
} }

View File

@ -20,7 +20,7 @@ func Test_read_object_as_any(t *testing.T) {
should.Equal(2, any.Size()) should.Equal(2, any.Size())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(Object, any.ValueType()) should.Equal(ObjectValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
obj := struct { obj := struct {
A string A string
@ -44,8 +44,8 @@ func Test_object_lazy_any_get_all(t *testing.T) {
func Test_object_lazy_any_get_invalid(t *testing.T) { func Test_object_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte(`{}`)) any := Get([]byte(`{}`))
should.Equal(Invalid, any.Get("a", "stream", "c").ValueType()) should.Equal(InvalidValue, any.Get("a", "stream", "c").ValueType())
should.Equal(Invalid, any.Get(1).ValueType()) should.Equal(InvalidValue, any.Get(1).ValueType())
} }
func Test_wrap_map_and_convert_to_any(t *testing.T) { func Test_wrap_map_and_convert_to_any(t *testing.T) {

View File

@ -156,7 +156,7 @@ func Test_encode_byte_array(t *testing.T) {
should.Equal(`"AQID"`, string(bytes)) should.Equal(`"AQID"`, string(bytes))
} }
func Test_decode_byte_array(t *testing.T) { func Test_decode_byte_array_from_base64(t *testing.T) {
should := require.New(t) should := require.New(t)
data := []byte{} data := []byte{}
err := json.Unmarshal([]byte(`"AQID"`), &data) err := json.Unmarshal([]byte(`"AQID"`), &data)
@ -167,6 +167,17 @@ func Test_decode_byte_array(t *testing.T) {
should.Equal([]byte{1, 2, 3}, data) should.Equal([]byte{1, 2, 3}, data)
} }
func Test_decode_byte_array_from_array(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_slice(t *testing.T) { func Test_decode_slice(t *testing.T) {
should := require.New(t) should := require.New(t)
slice := make([]string, 0, 5) slice := make([]string, 0, 5)

View File

@ -285,3 +285,32 @@ func Test_marshal_json_with_time(t *testing.T) {
should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj)) should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
should.NotNil(obj.TF1.F2) should.NotNil(obj.TF1.F2)
} }
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {
Field string `orm:"field"`
}
should := require.New(t)
json := Config{TagKey: "orm"}.Froze()
str, err := json.MarshalToString(TestObject{"hello"})
should.Nil(err)
should.Equal(`{"field":"hello"}`, str)
}
func Test_recursive_empty_interface_customization(t *testing.T) {
t.Skip()
var obj interface{}
RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case NumberValue:
*(*interface{})(ptr) = iter.ReadInt64()
default:
*(*interface{})(ptr) = iter.Read()
}
})
should := require.New(t)
Unmarshal([]byte("[100]"), &obj)
should.Equal([]interface{}{int64(100)}, obj)
}

View File

@ -457,6 +457,17 @@ func Test_json_number(t *testing.T) {
should.Equal(`[1]`, str) should.Equal(`[1]`, str)
} }
func Test_jsoniter_number(t *testing.T) {
should := require.New(t)
var arr []Number
err := Unmarshal([]byte(`[1]`), &arr)
should.Nil(err)
should.Equal(Number("1"), arr[0])
str, isNumber := CastJsonNumber(arr[0])
should.True(isNumber)
should.Equal("1", str)
}
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

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io" "io"
"testing" "testing"
"bytes"
) )
func Test_missing_object_end(t *testing.T) { func Test_missing_object_end(t *testing.T) {
@ -25,7 +26,7 @@ func Test_missing_array_end(t *testing.T) {
func Test_invalid_any(t *testing.T) { func Test_invalid_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Invalid, any.Get(0.3).ValueType()) should.Equal(InvalidValue, any.Get(0.3).ValueType())
// is nil correct ? // is nil correct ?
should.Equal(nil, any.Get(0.3).GetInterface()) should.Equal(nil, any.Get(0.3).GetInterface())
@ -41,7 +42,7 @@ func Test_invalid_any(t *testing.T) {
should.Equal(float64(0), any.ToFloat64()) should.Equal(float64(0), any.ToFloat64())
should.Equal("", any.ToString()) should.Equal("", any.ToString())
should.Equal(Invalid, any.Get(0.1).Get(1).ValueType()) should.Equal(InvalidValue, any.Get(0.1).Get(1).ValueType())
} }
func Test_invalid_struct_input(t *testing.T) { func Test_invalid_struct_input(t *testing.T) {
@ -68,46 +69,64 @@ func Test_invalid_array_input(t *testing.T) {
should.NotNil(Unmarshal(input, &obj)) should.NotNil(Unmarshal(input, &obj))
} }
func Test_double_negative(t *testing.T) { func Test_invalid_float(t *testing.T) {
should := require.New(t) inputs := []string{
var v interface{} `1.e1`, // dot without following digit
should.NotNil(json.Unmarshal([]byte(`--2`), &v)) `1.`, // dot can not be the last char
var vFloat64 float64 ``, // empty number
should.NotNil(UnmarshalFromString(`--2`, &vFloat64)) `01`, // extra leading zero
var vFloat32 float32 `-`, // negative without digit
should.NotNil(UnmarshalFromString(`--2`, &vFloat32)) `--`, // double negative
var vInt int `--2`, // double negative
should.NotNil(UnmarshalFromString(`--2`, &vInt)) }
iter := ParseString(ConfigDefault, `--2`) for _, input := range inputs {
iter.Skip() t.Run(input, func(t *testing.T) {
should.NotEqual(io.EOF, iter.Error) should := require.New(t)
should.NotNil(iter.Error) iter := ParseString(ConfigDefault, input+",")
iter.Skip()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
v := float64(0)
should.NotNil(json.Unmarshal([]byte(input), &v))
iter = ParseString(ConfigDefault, input+",")
iter.ReadFloat64()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
iter = ParseString(ConfigDefault, input+",")
iter.ReadFloat32()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
})
}
} }
func Test_leading_zero(t *testing.T) { func Test_chan(t *testing.T) {
t.Skip("do not support chan")
type TestObject struct {
MyChan chan bool
MyField int
}
should := require.New(t) should := require.New(t)
var v interface{} obj := TestObject{}
should.NotNil(json.Unmarshal([]byte(`01`), &v)) str, err := json.Marshal(obj)
var vFloat64 float64 should.Nil(err)
should.NotNil(UnmarshalFromString(`01`, &vFloat64)) should.Equal(``, str)
var vFloat32 float32
should.NotNil(UnmarshalFromString(`01`, &vFloat32))
var vInt int
should.NotNil(UnmarshalFromString(`01`, &vInt))
iter := ParseString(ConfigDefault, `01,`)
iter.Skip()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
} }
func Test_empty_as_number(t *testing.T) { func Test_invalid_number(t *testing.T) {
type Message struct {
Number int `json:"number"`
}
obj := Message{}
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(bytes.NewBufferString(`{"number":"5"}`))
err := decoder.Decode(&obj)
invalidStr := err.Error()
result, err := ConfigCompatibleWithStandardLibrary.Marshal(invalidStr)
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, `,`) should.Nil(err)
iter.ReadFloat64() result2, err := json.Marshal(invalidStr)
should.NotEqual(io.EOF, iter.Error) should.Nil(err)
should.NotNil(iter.Error) should.Equal(string(result2), string(result))
iter = ParseString(ConfigDefault, `,`)
iter.ReadFloat32()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
} }

View File

@ -23,17 +23,122 @@ import (
// } // }
//} //}
func init() {
ioutil.WriteFile("/tmp/large-file.json", []byte(`[{
"person": {
"id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"name": {
"fullName": "Leonid Bugaev",
"givenName": "Leonid",
"familyName": "Bugaev"
},
"email": "leonsbox@gmail.com",
"gender": "male",
"location": "Saint Petersburg, Saint Petersburg, RU",
"geo": {
"city": "Saint Petersburg",
"state": "Saint Petersburg",
"country": "Russia",
"lat": 59.9342802,
"lng": 30.3350986
},
"bio": "Senior engineer at Granify.com",
"site": "http://flickfaver.com",
"avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"employment": {
"name": "www.latera.ru",
"title": "Software Engineer",
"domain": "gmail.com"
},
"facebook": {
"handle": "leonid.bugaev"
},
"github": {
"handle": "buger",
"id": 14009,
"avatar": "https://avatars.githubusercontent.com/u/14009?v=3",
"company": "Granify",
"blog": "http://leonsbox.com",
"followers": 95,
"following": 10
},
"twitter": {
"handle": "flickfaver",
"id": 77004410,
"bio": null,
"followers": 2,
"following": 1,
"statuses": 5,
"favorites": 0,
"location": "",
"site": "http://flickfaver.com",
"avatar": null
},
"linkedin": {
"handle": "in/leonidbugaev"
},
"googleplus": {
"handle": null
},
"angellist": {
"handle": "leonid-bugaev",
"id": 61541,
"bio": "Senior engineer at Granify.com",
"blog": "http://buger.github.com",
"site": "http://buger.github.com",
"followers": 41,
"avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390"
},
"klout": {
"handle": null,
"score": null
},
"foursquare": {
"handle": null
},
"aboutme": {
"handle": "leonid.bugaev",
"bio": null,
"avatar": null
},
"gravatar": {
"handle": "buger",
"urls": [
],
"avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"avatars": [
{
"url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"type": "thumbnail"
}
]
},
"fuzzy": false
},
"company": "hello"
}]`), 0666)
}
/*
200000 8886 ns/op 4336 B/op 6 allocs/op
50000 34244 ns/op 6744 B/op 14 allocs/op
*/
func Benchmark_jsoniter_large_file(b *testing.B) { func Benchmark_jsoniter_large_file(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json") file, _ := os.Open("/tmp/large-file.json")
iter := Parse(ConfigDefault, file, 4096) iter := Parse(ConfigDefault, file, 4096)
count := 0 count := 0
for iter.ReadArray() { iter.ReadArrayCB(func(iter *Iterator) bool {
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
iter.Skip() iter.Skip()
count++ count++
} return true
})
file.Close() file.Close()
if iter.Error != nil {
b.Error(iter.Error)
}
} }
} }
@ -44,6 +149,9 @@ func Benchmark_json_large_file(b *testing.B) {
bytes, _ := ioutil.ReadAll(file) bytes, _ := ioutil.ReadAll(file)
file.Close() file.Close()
result := []struct{}{} result := []struct{}{}
json.Unmarshal(bytes, &result) err := json.Unmarshal(bytes, &result)
if err != nil {
b.Error(err)
}
} }
} }

View File

@ -95,267 +95,6 @@ func Test_write_object(t *testing.T) {
should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String()) should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String())
} }
func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj))
should.Equal("hello", obj.Field1)
}
func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
}
func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
}
func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
}
func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_six_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
}
func Test_decode_seven_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
}
func Test_decode_eight_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8":"1", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
should.Equal("1", obj.Field8)
}
func Test_decode_nine_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8" : "zzzzzzzzzzz", "Field7": "zz", "Field6" : "xx", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field9":"f"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("xx", obj.Field6)
should.Equal("zz", obj.Field7)
should.Equal("zzzzzzzzzzz", obj.Field8)
should.Equal("f", obj.Field9)
}
func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field10":"x", "Field9": "x", "Field8":"x", "Field7":"x", "Field6":"x", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("x", obj.Field7)
should.Equal("x", obj.Field8)
should.Equal("x", obj.Field9)
should.Equal("x", obj.Field10)
}
func Test_decode_more_than_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
Field11 int
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field11":1, "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal(1, obj.Field11)
}
func Test_decode_struct_field_with_tag(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1"`
Field2 string `json:"-"`
Field3 int `json:",string"`
}
obj := TestObject{Field2: "world"}
UnmarshalFromString(`{"field-1": "hello", "field2": "", "Field3": "100"}`, &obj)
should.Equal("hello", obj.Field1)
should.Equal("world", obj.Field2)
should.Equal(100, obj.Field3)
}
func Test_decode_struct_field_with_tag_string(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int `json:",string"`
}
obj := TestObject{Field1: 100}
should.Nil(UnmarshalFromString(`{"Field1": "100"}`, &obj))
should.Equal(100, obj.Field1)
}
func Test_write_val_zero_field_struct(t *testing.T) { func Test_write_val_zero_field_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {

View File

@ -19,6 +19,8 @@ func Test_read_string(t *testing.T) {
`"\"`, `"\"`,
`"\\\"`, `"\\\"`,
"\"\n\"", "\"\n\"",
`"\U0001f64f"`,
`"\uD83D\u00"`,
} }
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
// control characters are invalid // control characters are invalid
@ -39,6 +41,11 @@ func Test_read_string(t *testing.T) {
{`"a"`, "a"}, {`"a"`, "a"},
{`null`, ""}, {`null`, ""},
{`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"}, {`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"},
{`"\uD83D"`, string([]byte{239, 191, 189})},
{`"\uD83D\\"`, string([]byte{239, 191, 189, '\\'})},
{`"\uD83D\ub000"`, string([]byte{239, 191, 189, 235, 128, 128})},
{`"\uD83D\ude04"`, "😄"},
{`"\uDEADBEEF"`, string([]byte{239, 191, 189, 66, 69, 69, 70})},
} }
for _, tc := range goodInputs { for _, tc := range goodInputs {
@ -111,7 +118,9 @@ func Test_read_exotic_string(t *testing.T) {
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) { t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, input) iter := ParseString(ConfigDefault, input)
should.Equal(output, iter.ReadString()) var v string
should.Nil(json.Unmarshal([]byte(input), &v))
should.Equal(v, iter.ReadString())
}) })
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) { t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t) should := require.New(t)

View File

@ -0,0 +1,267 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj))
should.Equal("hello", obj.Field1)
}
func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
}
func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
}
func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
}
func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_six_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
}
func Test_decode_seven_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
}
func Test_decode_eight_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8":"1", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
should.Equal("1", obj.Field8)
}
func Test_decode_nine_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8" : "zzzzzzzzzzz", "Field7": "zz", "Field6" : "xx", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field9":"f"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("xx", obj.Field6)
should.Equal("zz", obj.Field7)
should.Equal("zzzzzzzzzzz", obj.Field8)
should.Equal("f", obj.Field9)
}
func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field10":"x", "Field9": "x", "Field8":"x", "Field7":"x", "Field6":"x", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("x", obj.Field7)
should.Equal("x", obj.Field8)
should.Equal("x", obj.Field9)
should.Equal("x", obj.Field10)
}
func Test_decode_more_than_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
Field11 int
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field11":1, "field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal(1, obj.Field11)
}
func Test_decode_struct_field_with_tag(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1"`
Field2 string `json:"-"`
Field3 int `json:",string"`
}
obj := TestObject{Field2: "world"}
UnmarshalFromString(`{"field-1": "hello", "field2": "", "Field3": "100"}`, &obj)
should.Equal("hello", obj.Field1)
should.Equal("world", obj.Field2)
should.Equal(100, obj.Field3)
}
func Test_decode_struct_field_with_tag_string(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int `json:",string"`
}
obj := TestObject{Field1: 100}
should.Nil(UnmarshalFromString(`{"Field1": "100"}`, &obj))
should.Equal(100, obj.Field1)
}

View File

@ -15,80 +15,80 @@ func Test_wrap_and_valuetype_everything(t *testing.T) {
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int8(10)) any = Wrap(int8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// get interface is not int8 interface // get interface is not int8 interface
// i = int8(10) // i = int8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(int16(10)) any = Wrap(int16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
//i = int16(10) //i = int16(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(int32(10)) any = Wrap(int32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int32(10) i = int32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int64(10)) any = Wrap(int64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int64(10) i = int64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint(10)) any = Wrap(uint(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = uint(10) //i = uint(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(uint8(10)) any = Wrap(uint8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
// i = uint8(10) // i = uint8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(uint16(10)) any = Wrap(uint16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(uint32(10)) any = Wrap(uint32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint32(10) i = uint32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint64(10)) any = Wrap(uint64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint64(10) i = uint64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(float32(10)) any = Wrap(float32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = float32(10) //i = float32(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(float64(10)) any = Wrap(float64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = float64(10) i = float64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(true) any = Wrap(true)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = true i = true
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(false) any = Wrap(false)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = false i = false
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(nil) any = Wrap(nil)
should.Equal(any.ValueType(), Nil) should.Equal(any.ValueType(), NilValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = nil i = nil
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
@ -99,13 +99,13 @@ func Test_wrap_and_valuetype_everything(t *testing.T) {
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(struct{ age int }{age: 1}) any = Wrap(struct{ age int }{age: 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = struct{ age int }{age: 1} i = struct{ age int }{age: 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(map[string]interface{}{"abc": 1}) any = Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = map[string]interface{}{"abc": 1} i = map[string]interface{}{"abc": 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest int64

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest map[int64]string

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest []int64

View File

@ -8,9 +8,11 @@ type typeForTest struct {
Int8 int8 Int8 int8
Int16 int16 Int16 int16
Int32 int32 Int32 int32
Int64 int64
Uint8 uint8 Uint8 uint8
Uint16 uint16 Uint16 uint16
Uint32 uint32 Uint32 uint32
Uint64 uint64
Float32 float32 Float32 float32
Float64 float64 Float64 float64
String1 string String1 string

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,5 @@
package test
type typeForTest struct {
F int64
}

View File

@ -13,4 +13,5 @@ var inputs = []string{
"1E1", // valid, e or E "1E1", // valid, e or E
"1ee1", // invalid "1ee1", // invalid
"100a", // invalid "100a", // invalid
"10.", // invalid
} }