mirror of
https://github.com/json-iterator/go.git
synced 2025-05-28 22:17:38 +02:00
Compare commits
311 Commits
jsoniter-g
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
71ac16282d | ||
|
720cd423d0 | ||
|
9b00d50ac0 | ||
|
ffc487c633 | ||
|
10b9ac0971 | ||
|
bf8b920736 | ||
|
a7b8414362 | ||
|
024077e996 | ||
|
08b5e5796b | ||
|
c6661824eb | ||
|
3c55efb1a1 | ||
|
e6b9536d36 | ||
|
a3465d79a9 | ||
|
9b79a3e192 | ||
|
1779031cda | ||
|
6821bec9fa | ||
|
9461257643 | ||
|
5bce16d299 | ||
|
a1ca083078 | ||
|
cd6773e694 | ||
|
55287ed53a | ||
|
8961be9c21 | ||
|
0f8241d334 | ||
|
53b9d06ba7 | ||
|
1f7ee05ef8 | ||
|
b22f393858 | ||
|
bede7b9e40 | ||
|
58aeb59006 | ||
|
7acbb404a4 | ||
|
69f2a91ff4 | ||
|
11a37a0774 | ||
|
91f4a6405d | ||
|
a54d350455 | ||
|
6f4c196d95 | ||
|
8302a17e8c | ||
|
3987001e27 | ||
|
78d9e97b7a | ||
|
49c900ee46 | ||
|
9c0685d8d3 | ||
|
acfec88f7a | ||
|
e88512faf8 | ||
|
b681149eae | ||
|
d1af7639b3 | ||
|
7c9f8c2d20 | ||
|
f814d6c0f1 | ||
|
aba8654400 | ||
|
a1c9557592 | ||
|
44a7e7340d | ||
|
2834c7e43c | ||
|
d296277d5c | ||
|
dc11f49689 | ||
|
83f7b825b3 | ||
|
03217c3e97 | ||
|
908eaed151 | ||
|
eec24895fe | ||
|
1ba732a07d | ||
|
819acad769 | ||
|
695ec2b83b | ||
|
028e2ef2bd | ||
|
976454858b | ||
|
27518f6661 | ||
|
94869abf43 | ||
|
459f0e30ae | ||
|
0039f4ac3d | ||
|
fb5614a4ca | ||
|
f71b9090aa | ||
|
08047c174c | ||
|
68347ec4d6 | ||
|
0fd91468bb | ||
|
1bc9828b4f | ||
|
24c3d57281 | ||
|
0ff49de124 | ||
|
5bc9320502 | ||
|
f64ce68b6e | ||
|
2d42ff74dd | ||
|
3a023a5fbc | ||
|
16aef10b2b | ||
|
ae4c002f78 | ||
|
e4aa2ec063 | ||
|
d05f387f50 | ||
|
a9403d25cd | ||
|
05d041de10 | ||
|
5916df66b3 | ||
|
2433035e51 | ||
|
6dfc0bf2dd | ||
|
b9be8dd373 | ||
|
b8d78b6aaf | ||
|
7109b5e7dd | ||
|
4cc76529e8 | ||
|
c5ddac9dc3 | ||
|
f76d712086 | ||
|
1624edc445 | ||
|
5d789e5e02 | ||
|
0260c89b54 | ||
|
10a568c511 | ||
|
ab8a2e0c74 | ||
|
2fbdfbb595 | ||
|
720ab8dc7f | ||
|
f2b4162afb | ||
|
3830516ed0 | ||
|
7cceb6c2e3 | ||
|
b92cf78708 | ||
|
8744d7c5c7 | ||
|
37cc313d18 | ||
|
2ddf6d7582 | ||
|
6a6742f0a2 | ||
|
6c702ce12a | ||
|
f88871b601 | ||
|
f246f80f14 | ||
|
51dd70305b | ||
|
a949c42748 | ||
|
f89479f5c0 | ||
|
b858ec296c | ||
|
885a41a0a6 | ||
|
9e9a97040e | ||
|
fb4d53e4cc | ||
|
b53656d459 | ||
|
8f27a81d90 | ||
|
4930b053b8 | ||
|
06e0f9391e | ||
|
ca39e5af3e | ||
|
39acec93e0 | ||
|
25fa392355 | ||
|
d51e841de0 | ||
|
3353055b2a | ||
|
455b3f8bb8 | ||
|
2a93f9003e | ||
|
9472474ffd | ||
|
ad83167dc6 | ||
|
fff342fd04 | ||
|
8d6662b81b | ||
|
a377e2656b | ||
|
0ac74bba4a | ||
|
ebe943a4a6 | ||
|
414d0307c9 | ||
|
86e9fd72bc | ||
|
be70f29b04 | ||
|
a3fdd37b9a | ||
|
d346ea6e55 | ||
|
820ec30bd6 | ||
|
df8295a48a | ||
|
99fc16a363 | ||
|
a3866383f5 | ||
|
2fcbb23d96 | ||
|
ea6403326b | ||
|
404d90796f | ||
|
b79587753b | ||
|
63ea5e3131 | ||
|
895a19f2dc | ||
|
6327145300 | ||
|
c99d73acd0 | ||
|
3d39af6dd9 | ||
|
a016e87b9f | ||
|
08218647c3 | ||
|
d6f02cbd48 | ||
|
b53aa13eb0 | ||
|
e322da5531 | ||
|
1d41f3c0ed | ||
|
29604bf5c3 | ||
|
cbc1786a76 | ||
|
5a696808d6 | ||
|
d8e64aa825 | ||
|
577ddede74 | ||
|
43d9384d67 | ||
|
2074f25bd3 | ||
|
ef3038593b | ||
|
9dafbc667f | ||
|
a7a34507ab | ||
|
0e2b54800a | ||
|
e7c7f3b337 | ||
|
75810179f6 | ||
|
6a8f9fa342 | ||
|
24bb2eee9f | ||
|
64cc784089 | ||
|
477be43d00 | ||
|
a8708bca85 | ||
|
658ff9ef15 | ||
|
64c1c67885 | ||
|
e3bc511e5a | ||
|
8fa357ab7b | ||
|
761ce8cce2 | ||
|
c3b6c1e845 | ||
|
0ed9de94f2 | ||
|
6fded6eb5f | ||
|
dc3395f770 | ||
|
bd4e013f98 | ||
|
48a4a1e4db | ||
|
9bc223734a | ||
|
eb9aeccee2 | ||
|
28adca2a14 | ||
|
a9b3f36b2f | ||
|
1e8e785321 | ||
|
002b5ae342 | ||
|
07f99a1124 | ||
|
71f74dc71e | ||
|
7990317be5 | ||
|
9edd73f752 | ||
|
3d5ee1098a | ||
|
ee8cfb7547 | ||
|
bca911dae0 | ||
|
28452fcdec | ||
|
ea8c33040f | ||
|
358cfc3929 | ||
|
c39a632e65 | ||
|
e31252f2e2 | ||
|
807e4a8b20 | ||
|
e78b7e89b6 | ||
|
945d1aaa19 | ||
|
ba3857729b | ||
|
c3ed5e85e0 | ||
|
c27f6f9350 | ||
|
0ab880662f | ||
|
6dad2de6cc | ||
|
11c1cce0d8 | ||
|
96fcb84835 | ||
|
e7a8aea845 | ||
|
60a9df5ebc | ||
|
7b060ec866 | ||
|
25f147f530 | ||
|
a9b9c73b4d | ||
|
e0df39fda2 | ||
|
13f86432b8 | ||
|
d2a7335211 | ||
|
b2a706d14b | ||
|
23078876c5 | ||
|
051434fab7 | ||
|
be6688fc1a | ||
|
ff2b70c1db | ||
|
f7279a603e | ||
|
9f088cbcc4 | ||
|
3c0e5762c4 | ||
|
d394a135a1 | ||
|
9fddff05f0 | ||
|
b1b003864e | ||
|
aed5a81f09 | ||
|
f1258b01aa | ||
|
fbd210edfc | ||
|
640251ab91 | ||
|
06b2a7cf1d | ||
|
5fffb9b8f7 | ||
|
7e3b776024 | ||
|
6240e1e798 | ||
|
0149a5cf4a | ||
|
5068c8baaf | ||
|
16f78601b5 | ||
|
8f50a91be2 | ||
|
73c7bc881e | ||
|
4de15a3a87 | ||
|
14b28b2226 | ||
|
abe3c4016b | ||
|
dbb1ef3f63 | ||
|
46b20bbbec | ||
|
fdfe0b9a69 | ||
|
faa3dcf46a | ||
|
1f58120d43 | ||
|
6ed27152e0 | ||
|
3c298d8a76 | ||
|
9f6e5962a9 | ||
|
c463aa12c4 | ||
|
b5d2607a6d | ||
|
48cc4d965a | ||
|
c59c42fda0 | ||
|
8324374402 | ||
|
2017f3866b | ||
|
ddc5af4512 | ||
|
2f7e5c8dd7 | ||
|
92772579dd | ||
|
ae57d167e8 | ||
|
eef35e549b | ||
|
005d86dc44 | ||
|
e658f6597a | ||
|
f8eb43eda3 | ||
|
18a241d40b | ||
|
0fdf883ac0 | ||
|
34fbec74ad | ||
|
90574c5ca3 | ||
|
6a4ba7bfa9 | ||
|
0828e559d0 | ||
|
2c67d0f68a | ||
|
f29a0391bc | ||
|
374e68a144 | ||
|
b134d86290 | ||
|
bc3221879d | ||
|
8c7fc7584a | ||
|
db32ee8c2d | ||
|
d80309af3b | ||
|
36b14963da | ||
|
f706335302 | ||
|
2dc0031b26 | ||
|
cdbd2ed810 | ||
|
39e9d67807 | ||
|
2066b01acb | ||
|
ac3b3cd160 | ||
|
887789156a | ||
|
7df5a67d0d | ||
|
9c358632dc | ||
|
1cfa233923 | ||
|
d249b05a85 | ||
|
abbd16da6c | ||
|
b67201557a | ||
|
5124683f24 | ||
|
4892de725b | ||
|
34a2174be3 | ||
|
24ecaff2a1 | ||
|
c15b4d116c | ||
|
12cd299fa8 | ||
|
60ba332980 | ||
|
f705934fbf | ||
|
17a26a6e20 | ||
|
156284b028 | ||
|
6b6938829d |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.idea
|
||||
/vendor
|
||||
/bug_test.go
|
||||
/coverage.txt
|
||||
/profile.out
|
||||
/.idea
|
||||
|
@ -2,6 +2,7 @@ language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.x
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
21
Gopkg.lock
generated
Normal file
21
Gopkg.lock
generated
Normal file
@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/concurrent"
|
||||
packages = ["."]
|
||||
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
|
||||
version = "1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/reflect2"
|
||||
packages = ["."]
|
||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||
version = "1.0.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
26
Gopkg.toml
Normal file
26
Gopkg.toml
Normal file
@ -0,0 +1,26 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/modern-go/reflect2"
|
||||
version = "1.0.1"
|
42
README.md
42
README.md
@ -1,5 +1,5 @@
|
||||
[](https://sourcegraph.com/github.com/json-iterator/go?badge)
|
||||
[](http://godoc.org/github.com/json-iterator/go)
|
||||
[](https://pkg.go.dev/github.com/json-iterator/go)
|
||||
[](https://travis-ci.org/json-iterator/go)
|
||||
[](https://codecov.io/gh/json-iterator/go)
|
||||
[](https://goreportcard.com/report/github.com/json-iterator/go)
|
||||
@ -16,14 +16,17 @@ Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/githu
|
||||
|
||||
Raw Result (easyjson requires static code generation)
|
||||
|
||||
| | ns/op | allocation bytes | allocation times |
|
||||
| --- | --- | --- | --- |
|
||||
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
|
||||
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
|
||||
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
|
||||
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
|
||||
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
||||
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
||||
| | ns/op | allocation bytes | allocation times |
|
||||
| --------------- | ----------- | ---------------- | ---------------- |
|
||||
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
|
||||
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
|
||||
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
|
||||
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
|
||||
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
||||
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
||||
|
||||
Always benchmark with your own workload.
|
||||
The result depends heavily on the data input.
|
||||
|
||||
# Usage
|
||||
|
||||
@ -36,11 +39,13 @@ import "encoding/json"
|
||||
json.Marshal(&data)
|
||||
```
|
||||
|
||||
with
|
||||
with
|
||||
|
||||
```go
|
||||
import "github.com/json-iterator/go"
|
||||
jsoniter.Marshal(&data)
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
json.Marshal(&data)
|
||||
```
|
||||
|
||||
Replace
|
||||
@ -53,8 +58,10 @@ json.Unmarshal(input, &data)
|
||||
with
|
||||
|
||||
```go
|
||||
import "github.com/json-iterator/go"
|
||||
jsoniter.Unmarshal(input, &data)
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
json.Unmarshal(input, &data)
|
||||
```
|
||||
|
||||
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
|
||||
@ -69,7 +76,10 @@ go get github.com/json-iterator/go
|
||||
|
||||
Contributors
|
||||
|
||||
* [thockin](https://github.com/thockin)
|
||||
* [cch123](https://github.com/cch123)
|
||||
- [thockin](https://github.com/thockin)
|
||||
- [mattn](https://github.com/mattn)
|
||||
- [cch123](https://github.com/cch123)
|
||||
- [Oleg Shaldybin](https://github.com/olegshaldybin)
|
||||
- [Jason Toffaletti](https://github.com/toffaletti)
|
||||
|
||||
Report issue or pull request, or email taowen@gmail.com, or [](https://gitter.im/json-iterator/Lobby)
|
||||
|
@ -16,16 +16,7 @@ func Unmarshal(data []byte, v interface{}) error {
|
||||
return ConfigDefault.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func lastNotSpacePos(data []byte) int {
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
if data[i] != ' ' && data[i] != '\t' && data[i] != '\r' && data[i] != '\n' {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// UnmarshalFromString convenient method to read from string instead of []byte
|
||||
// UnmarshalFromString is a convenient method to read from string instead of []byte
|
||||
func UnmarshalFromString(str string, v interface{}) error {
|
||||
return ConfigDefault.UnmarshalFromString(str, v)
|
||||
}
|
||||
@ -71,6 +62,11 @@ type Decoder struct {
|
||||
|
||||
// Decode decode JSON into interface{}
|
||||
func (adapter *Decoder) Decode(obj interface{}) error {
|
||||
if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil {
|
||||
if !adapter.iter.loadMore() {
|
||||
return io.EOF
|
||||
}
|
||||
}
|
||||
adapter.iter.ReadVal(obj)
|
||||
err := adapter.iter.Error
|
||||
if err == io.EOF {
|
||||
@ -81,7 +77,16 @@ func (adapter *Decoder) Decode(obj interface{}) error {
|
||||
|
||||
// More is there more?
|
||||
func (adapter *Decoder) More() bool {
|
||||
return adapter.iter.head != adapter.iter.tail
|
||||
iter := adapter.iter
|
||||
if iter.Error != nil {
|
||||
return false
|
||||
}
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return c != ']' && c != '}'
|
||||
}
|
||||
|
||||
// Buffered remaining buffer
|
||||
@ -90,11 +95,21 @@ func (adapter *Decoder) Buffered() io.Reader {
|
||||
return bytes.NewReader(remaining)
|
||||
}
|
||||
|
||||
// UseNumber for number JSON element, use float64 or json.Number (alias of string)
|
||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||
// Number instead of as a float64.
|
||||
func (adapter *Decoder) UseNumber() {
|
||||
origCfg := adapter.iter.cfg.configBeforeFrozen
|
||||
origCfg.UseNumber = true
|
||||
adapter.iter.cfg = origCfg.Froze().(*frozenConfig)
|
||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||
cfg.UseNumber = true
|
||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
func (adapter *Decoder) DisallowUnknownFields() {
|
||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||
cfg.DisallowUnknownFields = true
|
||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// NewEncoder same as json.NewEncoder
|
||||
@ -110,18 +125,26 @@ type Encoder struct {
|
||||
// Encode encode interface{} as JSON to io.Writer
|
||||
func (adapter *Encoder) Encode(val interface{}) error {
|
||||
adapter.stream.WriteVal(val)
|
||||
adapter.stream.WriteRaw("\n")
|
||||
adapter.stream.Flush()
|
||||
return adapter.stream.Error
|
||||
}
|
||||
|
||||
// SetIndent set the indention. Prefix is not supported
|
||||
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
||||
adapter.stream.cfg.indentionStep = len(indent)
|
||||
config := adapter.stream.cfg.configBeforeFrozen
|
||||
config.IndentionStep = len(indent)
|
||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// SetEscapeHTML escape html by default, set to false to disable
|
||||
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
||||
config := adapter.stream.cfg.configBeforeFrozen
|
||||
config.EscapeHTML = escapeHTML
|
||||
adapter.stream.cfg = config.Froze().(*frozenConfig)
|
||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func Valid(data []byte) bool {
|
||||
return ConfigDefault.Valid(data)
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/modern-go/reflect2"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Any generic object representation.
|
||||
@ -24,7 +28,6 @@ type Any interface {
|
||||
ToString() string
|
||||
ToVal(val interface{})
|
||||
Get(path ...interface{}) Any
|
||||
// TODO: add Set
|
||||
Size() int
|
||||
Keys() []string
|
||||
GetInterface() interface{}
|
||||
@ -34,7 +37,7 @@ type Any interface {
|
||||
type baseAny struct{}
|
||||
|
||||
func (any *baseAny) Get(path ...interface{}) Any {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("Get %v from simple value", path)}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
||||
}
|
||||
|
||||
func (any *baseAny) Size() int {
|
||||
@ -88,7 +91,7 @@ func Wrap(val interface{}) Any {
|
||||
if isAny {
|
||||
return asAny
|
||||
}
|
||||
typ := reflect.TypeOf(val)
|
||||
typ := reflect2.TypeOf(val)
|
||||
switch typ.Kind() {
|
||||
case reflect.Slice:
|
||||
return wrapArray(val)
|
||||
@ -99,6 +102,9 @@ func Wrap(val interface{}) Any {
|
||||
case reflect.String:
|
||||
return WrapString(val.(string))
|
||||
case reflect.Int:
|
||||
if strconv.IntSize == 32 {
|
||||
return WrapInt32(int32(val.(int)))
|
||||
}
|
||||
return WrapInt64(int64(val.(int)))
|
||||
case reflect.Int8:
|
||||
return WrapInt32(int32(val.(int8)))
|
||||
@ -109,7 +115,15 @@ func Wrap(val interface{}) Any {
|
||||
case reflect.Int64:
|
||||
return WrapInt64(val.(int64))
|
||||
case reflect.Uint:
|
||||
if strconv.IntSize == 32 {
|
||||
return WrapUint32(uint32(val.(uint)))
|
||||
}
|
||||
return WrapUint64(uint64(val.(uint)))
|
||||
case reflect.Uintptr:
|
||||
if ptrSize == 32 {
|
||||
return WrapUint32(uint32(val.(uintptr)))
|
||||
}
|
||||
return WrapUint64(uint64(val.(uintptr)))
|
||||
case reflect.Uint8:
|
||||
return WrapUint32(uint32(val.(uint8)))
|
||||
case reflect.Uint16:
|
||||
@ -157,6 +171,8 @@ func (iter *Iterator) readAny() Any {
|
||||
return iter.readArrayAny()
|
||||
case '-':
|
||||
return iter.readNumberAny(false)
|
||||
case 0:
|
||||
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
||||
default:
|
||||
return iter.readNumberAny(true)
|
||||
}
|
||||
@ -240,3 +256,70 @@ func locatePath(iter *Iterator, path []interface{}) Any {
|
||||
}
|
||||
return iter.readAny()
|
||||
}
|
||||
|
||||
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
|
||||
|
||||
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
|
||||
if typ == anyType {
|
||||
return &directAnyCodec{}
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{
|
||||
valType: typ,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
|
||||
if typ == anyType {
|
||||
return &directAnyCodec{}
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{
|
||||
valType: typ,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type anyCodec struct {
|
||||
valType reflect2.Type
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
obj := codec.valType.UnsafeIndirect(ptr)
|
||||
any := obj.(Any)
|
||||
any.WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
obj := codec.valType.UnsafeIndirect(ptr)
|
||||
any := obj.(Any)
|
||||
return any.Size() == 0
|
||||
}
|
||||
|
||||
type directAnyCodec struct {
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*(*Any)(ptr) = iter.readAny()
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
any := *(*Any)(ptr)
|
||||
if any == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
any.WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
any := *(*Any)(ptr)
|
||||
return any.Size() == 0
|
||||
}
|
@ -13,7 +13,7 @@ type arrayLazyAny struct {
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ValueType() ValueType {
|
||||
return Array
|
||||
return ArrayValue
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) MustBeValid() Any {
|
||||
@ -117,7 +117,7 @@ func (any *arrayLazyAny) Get(path ...interface{}) Any {
|
||||
arr := make([]Any, 0)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
found := iter.readAny().Get(path[1:]...)
|
||||
if found.ValueType() != Invalid {
|
||||
if found.ValueType() != InvalidValue {
|
||||
arr = append(arr, found)
|
||||
}
|
||||
return true
|
||||
@ -162,7 +162,7 @@ func wrapArray(val interface{}) *arrayAny {
|
||||
}
|
||||
|
||||
func (any *arrayAny) ValueType() ValueType {
|
||||
return Array
|
||||
return ArrayValue
|
||||
}
|
||||
|
||||
func (any *arrayAny) MustBeValid() Any {
|
||||
@ -253,7 +253,7 @@ func (any *arrayAny) Get(path ...interface{}) Any {
|
||||
mappedAll := make([]Any, 0)
|
||||
for i := 0; i < any.val.Len(); i++ {
|
||||
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll = append(mappedAll, mapped)
|
||||
}
|
||||
}
|
@ -61,7 +61,7 @@ func (any *trueAny) GetInterface() interface{} {
|
||||
}
|
||||
|
||||
func (any *trueAny) ValueType() ValueType {
|
||||
return Bool
|
||||
return BoolValue
|
||||
}
|
||||
|
||||
func (any *trueAny) MustBeValid() Any {
|
||||
@ -129,7 +129,7 @@ func (any *falseAny) GetInterface() interface{} {
|
||||
}
|
||||
|
||||
func (any *falseAny) ValueType() ValueType {
|
||||
return Bool
|
||||
return BoolValue
|
||||
}
|
||||
|
||||
func (any *falseAny) MustBeValid() Any {
|
@ -14,7 +14,7 @@ func (any *floatAny) Parse() *Iterator {
|
||||
}
|
||||
|
||||
func (any *floatAny) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *floatAny) MustBeValid() Any {
|
@ -14,7 +14,7 @@ func (any *int32Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *int32Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *int32Any) MustBeValid() Any {
|
@ -14,7 +14,7 @@ func (any *int64Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *int64Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *int64Any) MustBeValid() Any {
|
@ -16,7 +16,7 @@ func (any *invalidAny) LastError() error {
|
||||
}
|
||||
|
||||
func (any *invalidAny) ValueType() ValueType {
|
||||
return Invalid
|
||||
return InvalidValue
|
||||
}
|
||||
|
||||
func (any *invalidAny) MustBeValid() Any {
|
@ -9,7 +9,7 @@ func (any *nilAny) LastError() error {
|
||||
}
|
||||
|
||||
func (any *nilAny) ValueType() ValueType {
|
||||
return Nil
|
||||
return NilValue
|
||||
}
|
||||
|
||||
func (any *nilAny) MustBeValid() Any {
|
@ -1,6 +1,9 @@
|
||||
package jsoniter
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type numberLazyAny struct {
|
||||
baseAny
|
||||
@ -10,7 +13,7 @@ type numberLazyAny struct {
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) MustBeValid() Any {
|
||||
@ -29,7 +32,9 @@ func (any *numberLazyAny) ToInt() int {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -37,7 +42,9 @@ func (any *numberLazyAny) ToInt32() int32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt32()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -45,7 +52,9 @@ func (any *numberLazyAny) ToInt64() int64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt64()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -53,7 +62,9 @@ func (any *numberLazyAny) ToUint() uint {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -61,7 +72,9 @@ func (any *numberLazyAny) ToUint32() uint32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint32()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -69,7 +82,9 @@ func (any *numberLazyAny) ToUint64() uint64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint64()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -77,7 +92,9 @@ func (any *numberLazyAny) ToFloat32() float32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadFloat32()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
@ -85,7 +102,9 @@ func (any *numberLazyAny) ToFloat64() float64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadFloat64()
|
||||
any.err = iter.Error
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ type objectLazyAny struct {
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ValueType() ValueType {
|
||||
return Object
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) MustBeValid() Any {
|
||||
@ -91,7 +91,7 @@ func (any *objectLazyAny) Get(path ...interface{}) Any {
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadMapCB(func(iter *Iterator, field string) bool {
|
||||
mapped := locatePath(iter, path[1:])
|
||||
if mapped.ValueType() != Invalid {
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[field] = mapped
|
||||
}
|
||||
return true
|
||||
@ -149,7 +149,7 @@ func wrapStruct(val interface{}) *objectAny {
|
||||
}
|
||||
|
||||
func (any *objectAny) ValueType() ValueType {
|
||||
return Object
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *objectAny) MustBeValid() Any {
|
||||
@ -224,7 +224,7 @@ func (any *objectAny) Get(path ...interface{}) Any {
|
||||
field := any.val.Field(i)
|
||||
if field.CanInterface() {
|
||||
mapped := Wrap(field.Interface()).Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[any.val.Type().Field(i).Name] = mapped
|
||||
}
|
||||
}
|
||||
@ -268,7 +268,7 @@ func wrapMap(val interface{}) *mapAny {
|
||||
}
|
||||
|
||||
func (any *mapAny) ValueType() ValueType {
|
||||
return Object
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *mapAny) MustBeValid() Any {
|
||||
@ -337,7 +337,7 @@ func (any *mapAny) Get(path ...interface{}) Any {
|
||||
keyAsStr := key.String()
|
||||
element := Wrap(any.val.MapIndex(key).Interface())
|
||||
mapped := element.Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[keyAsStr] = mapped
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ func (any *stringAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("Get %v from simple value", path)}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
||||
}
|
||||
|
||||
func (any *stringAny) Parse() *Iterator {
|
||||
@ -22,7 +22,7 @@ func (any *stringAny) Parse() *Iterator {
|
||||
}
|
||||
|
||||
func (any *stringAny) ValueType() ValueType {
|
||||
return String
|
||||
return StringValue
|
||||
}
|
||||
|
||||
func (any *stringAny) MustBeValid() Any {
|
||||
@ -64,7 +64,6 @@ func (any *stringAny) ToInt64() int64 {
|
||||
|
||||
flag := 1
|
||||
startPos := 0
|
||||
endPos := 0
|
||||
if any.val[0] == '+' || any.val[0] == '-' {
|
||||
startPos = 1
|
||||
}
|
||||
@ -73,6 +72,7 @@ func (any *stringAny) ToInt64() int64 {
|
||||
flag = -1
|
||||
}
|
||||
|
||||
endPos := startPos
|
||||
for i := startPos; i < len(any.val); i++ {
|
||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
||||
endPos = i + 1
|
||||
@ -98,7 +98,6 @@ func (any *stringAny) ToUint64() uint64 {
|
||||
}
|
||||
|
||||
startPos := 0
|
||||
endPos := 0
|
||||
|
||||
if any.val[0] == '-' {
|
||||
return 0
|
||||
@ -107,6 +106,7 @@ func (any *stringAny) ToUint64() uint64 {
|
||||
startPos = 1
|
||||
}
|
||||
|
||||
endPos := startPos
|
||||
for i := startPos; i < len(any.val); i++ {
|
||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
||||
endPos = i + 1
|
@ -1,18 +1,19 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_empty_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[]"))
|
||||
should.Equal(Array, any.Get().ValueType())
|
||||
should.Equal(Invalid, any.Get(0.3).ValueType())
|
||||
any := jsoniter.Get([]byte("[]"))
|
||||
should.Equal(jsoniter.ArrayValue, any.Get().ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(0.3).ValueType())
|
||||
should.Equal(0, any.Size())
|
||||
should.Equal(Array, any.ValueType())
|
||||
should.Equal(jsoniter.ArrayValue, any.ValueType())
|
||||
should.Nil(any.LastError())
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(int32(0), any.ToInt32())
|
||||
@ -26,19 +27,19 @@ func Test_read_empty_array_as_any(t *testing.T) {
|
||||
|
||||
func Test_read_one_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[1]"))
|
||||
any := jsoniter.Get([]byte("[1]"))
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_two_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[1,2]"))
|
||||
any := jsoniter.Get([]byte("[1,2]"))
|
||||
should.Equal(1, any.Get(0).ToInt())
|
||||
should.Equal(2, any.Size())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(1, any.ToInt())
|
||||
should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface())
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("[1,2]", string(stream.Buffer()))
|
||||
arr := []int{}
|
||||
@ -48,8 +49,8 @@ func Test_read_two_element_array_as_any(t *testing.T) {
|
||||
|
||||
func Test_wrap_array_and_convert_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap([]int{1, 2, 3})
|
||||
any2 := Wrap([]int{})
|
||||
any := jsoniter.Wrap([]int{1, 2, 3})
|
||||
any2 := jsoniter.Wrap([]int{})
|
||||
|
||||
should.Equal("[1,2,3]", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
@ -80,43 +81,43 @@ func Test_wrap_array_and_convert_to_any(t *testing.T) {
|
||||
|
||||
func Test_array_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[1,[2,3],4]"))
|
||||
any := jsoniter.Get([]byte("[1,[2,3],4]"))
|
||||
should.Equal(3, any.Get(1, 1).ToInt())
|
||||
should.Equal("[1,[2,3],4]", any.ToString())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[[1],[2],[3,4]]"))
|
||||
any := jsoniter.Get([]byte("[[1],[2],[3,4]]"))
|
||||
should.Equal("[1,2,3]", any.Get('*', 0).ToString())
|
||||
any = Get([]byte("[[[1],[2],[3,4]]]"), 0, '*', 0)
|
||||
any = jsoniter.Get([]byte("[[[1],[2],[3,4]]]"), 0, '*', 0)
|
||||
should.Equal("[1,2,3]", any.ToString())
|
||||
}
|
||||
|
||||
func Test_array_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := wrapArray([][]int{
|
||||
any := jsoniter.Wrap([][]int{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
{5, 6},
|
||||
})
|
||||
should.Equal("[1,3,5]", any.Get('*', 0).ToString())
|
||||
should.Equal(Array, any.ValueType())
|
||||
should.Equal(jsoniter.ArrayValue, any.ValueType())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(1, any.Get(0, 0).ToInt())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[]"))
|
||||
should.Equal(Invalid, any.Get(1, 1).ValueType())
|
||||
any := jsoniter.Get([]byte("[]"))
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1, 1).ValueType())
|
||||
should.NotNil(any.Get(1, 1).LastError())
|
||||
should.Equal(Invalid, any.Get("1").ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get("1").ValueType())
|
||||
should.NotNil(any.Get("1").LastError())
|
||||
}
|
||||
|
||||
func Test_invalid_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("["), 0)
|
||||
should.Equal(Invalid, any.ValueType())
|
||||
any := jsoniter.Get([]byte("["), 0)
|
||||
should.Equal(jsoniter.InvalidValue, any.ValueType())
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -35,9 +36,9 @@ var boolConvertMap = map[string]bool{
|
||||
func Test_read_bool_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
var any Any
|
||||
var any jsoniter.Any
|
||||
for k, v := range boolConvertMap {
|
||||
any = Get([]byte(k))
|
||||
any = jsoniter.Get([]byte(k))
|
||||
if v {
|
||||
should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k))
|
||||
} else {
|
||||
@ -49,16 +50,16 @@ func Test_read_bool_as_any(t *testing.T) {
|
||||
|
||||
func Test_write_bool_to_stream(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("true"))
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
any := jsoniter.Get([]byte("true"))
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("true", string(stream.Buffer()))
|
||||
should.Equal(any.ValueType(), Bool)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
|
||||
any = Get([]byte("false"))
|
||||
stream = NewStream(ConfigDefault, nil, 32)
|
||||
any = jsoniter.Get([]byte("false"))
|
||||
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("false", string(stream.Buffer()))
|
||||
|
||||
should.Equal(any.ValueType(), Bool)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -67,24 +68,22 @@ var floatConvertMap = map[string]float64{
|
||||
func Test_read_any_to_float(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for k, v := range floatConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(float64(v), any.ToFloat64(), "the original val is "+k)
|
||||
}
|
||||
|
||||
for k, v := range floatConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(float32(v), any.ToFloat32(), "the original val is "+k)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_float_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapFloat64(12.3)
|
||||
any := jsoniter.WrapFloat64(12.3)
|
||||
anyFloat64 := float64(12.3)
|
||||
//negaAnyFloat64 := float64(-1.1)
|
||||
any2 := WrapFloat64(-1.1)
|
||||
any2 := jsoniter.WrapFloat64(-1.1)
|
||||
should.Equal(float64(12.3), any.ToFloat64())
|
||||
//should.Equal("12.3", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(float32(anyFloat64), any.ToFloat32())
|
||||
should.Equal(int(anyFloat64), any.ToInt())
|
||||
@ -96,7 +95,7 @@ func Test_read_float_to_any(t *testing.T) {
|
||||
should.Equal(uint(0), any2.ToUint())
|
||||
should.Equal(uint32(0), any2.ToUint32())
|
||||
should.Equal(uint64(0), any2.ToUint64())
|
||||
should.Equal(any.ValueType(), Number)
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
|
||||
should.Equal("1.23E+01", any.ToString())
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -41,19 +42,19 @@ func Test_read_any_to_int(t *testing.T) {
|
||||
|
||||
// int
|
||||
for k, v := range intConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(v, any.ToInt(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
// int32
|
||||
for k, v := range intConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(int32(v), any.ToInt32(), fmt.Sprintf("original val is %v", k))
|
||||
}
|
||||
|
||||
// int64
|
||||
for k, v := range intConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(int64(v), any.ToInt64(), fmt.Sprintf("original val is %v", k))
|
||||
}
|
||||
|
||||
@ -94,17 +95,17 @@ func Test_read_any_to_uint(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint64(v), any.ToUint64(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint32(v), any.ToUint32(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := Get([]byte(k))
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint(v), any.ToUint(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
@ -112,7 +113,7 @@ func Test_read_any_to_uint(t *testing.T) {
|
||||
|
||||
func Test_read_int64_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapInt64(12345)
|
||||
any := jsoniter.WrapInt64(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
@ -123,14 +124,14 @@ func Test_read_int64_to_any(t *testing.T) {
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), Number)
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
func Test_read_int32_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapInt32(12345)
|
||||
any := jsoniter.WrapInt32(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
@ -141,15 +142,15 @@ func Test_read_int32_to_any(t *testing.T) {
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), Number)
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_read_uint32_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapUint32(12345)
|
||||
any := jsoniter.WrapUint32(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
@ -160,15 +161,15 @@ func Test_read_uint32_to_any(t *testing.T) {
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), Number)
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_read_uint64_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapUint64(12345)
|
||||
any := jsoniter.WrapUint64(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
@ -179,19 +180,19 @@ func Test_read_uint64_to_any(t *testing.T) {
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), Number)
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
stream = NewStream(ConfigDefault, nil, 32)
|
||||
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
stream.WriteUint(uint(123))
|
||||
should.Equal("123", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_int_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("1234"))
|
||||
any := jsoniter.Get([]byte("1234"))
|
||||
// panic!!
|
||||
//should.Equal(any.LastError(), io.EOF)
|
||||
should.Equal(Invalid, any.Get(1, "2").ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1, "2").ValueType())
|
||||
}
|
28
any_tests/jsoniter_any_map_test.go
Normal file
28
any_tests/jsoniter_any_map_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_wrap_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal("hello", any.Get("Field1").ToString())
|
||||
any = jsoniter.Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_map_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap(map[string][]int{"Field1": {1, 2}})
|
||||
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
|
||||
// map write to
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0)
|
||||
any.WriteTo(stream)
|
||||
// TODO cannot pass
|
||||
//should.Equal(string(stream.buf), "")
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_read_null_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`null`))
|
||||
any := jsoniter.Get([]byte(`null`))
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(float64(0), any.ToFloat64())
|
||||
should.Equal("", any.ToString())
|
@ -1,26 +1,27 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_object_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
any := jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
should.Equal(`{"a":"stream","c":"d"}`, any.ToString())
|
||||
// partial parse
|
||||
should.Equal("stream", any.Get("a").ToString())
|
||||
should.Equal("d", any.Get("c").ToString())
|
||||
should.Equal(2, len(any.Keys()))
|
||||
any = Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
any = jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
// full parse
|
||||
should.Equal(2, len(any.Keys()))
|
||||
should.Equal(2, any.Size())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(Object, any.ValueType())
|
||||
should.Equal(jsoniter.ObjectValue, any.ValueType())
|
||||
should.Nil(any.LastError())
|
||||
obj := struct {
|
||||
A string
|
||||
@ -31,26 +32,26 @@ func Test_read_object_as_any(t *testing.T) {
|
||||
|
||||
func Test_object_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`{"a":{"stream":{"c":"d"}}}`))
|
||||
any := jsoniter.Get([]byte(`{"a":{"stream":{"c":"d"}}}`))
|
||||
should.Equal("d", any.Get("a", "stream", "c").ToString())
|
||||
}
|
||||
|
||||
func Test_object_lazy_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`{"a":[0],"stream":[1]}`))
|
||||
any := jsoniter.Get([]byte(`{"a":[0],"stream":[1]}`))
|
||||
should.Contains(any.Get('*', 0).ToString(), `"a":0`)
|
||||
}
|
||||
|
||||
func Test_object_lazy_any_get_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`{}`))
|
||||
should.Equal(Invalid, any.Get("a", "stream", "c").ValueType())
|
||||
should.Equal(Invalid, any.Get(1).ValueType())
|
||||
any := jsoniter.Get([]byte(`{}`))
|
||||
should.Equal(jsoniter.InvalidValue, any.Get("a", "stream", "c").ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1).ValueType())
|
||||
}
|
||||
|
||||
func Test_wrap_map_and_convert_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap(map[string]interface{}{"a": 1})
|
||||
any := jsoniter.Wrap(map[string]interface{}{"a": 1})
|
||||
should.True(any.ToBool())
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(int32(0), any.ToInt32())
|
||||
@ -68,9 +69,9 @@ func Test_wrap_object_and_convert_to_any(t *testing.T) {
|
||||
Field1 string
|
||||
field2 string
|
||||
}
|
||||
any := Wrap(TestObject{"hello", "world"})
|
||||
any := jsoniter.Wrap(TestObject{"hello", "world"})
|
||||
should.Equal("hello", any.Get("Field1").ToString())
|
||||
any = Wrap(TestObject{"hello", "world"})
|
||||
any = jsoniter.Wrap(TestObject{"hello", "world"})
|
||||
should.Equal(2, any.Size())
|
||||
should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString())
|
||||
|
||||
@ -96,12 +97,25 @@ func Test_wrap_object_and_convert_to_any(t *testing.T) {
|
||||
func Test_any_within_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 Any
|
||||
Field2 Any
|
||||
Field1 jsoniter.Any
|
||||
Field2 jsoniter.Any
|
||||
}
|
||||
obj := TestObject{}
|
||||
err := UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj)
|
||||
err := jsoniter.UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", obj.Field1.ToString())
|
||||
should.Equal("[1,2,3]", obj.Field2.ToString())
|
||||
}
|
||||
|
||||
func Test_object_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 []int
|
||||
Field2 []int
|
||||
}
|
||||
any := jsoniter.Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
|
||||
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
should.Contains(any.Keys(), "Field2")
|
||||
should.NotContains(any.Keys(), "Field3")
|
||||
}
|
58
any_tests/jsoniter_any_string_test.go
Normal file
58
any_tests/jsoniter_any_string_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var stringConvertMap = map[string]string{
|
||||
"null": "",
|
||||
"321.1": "321.1",
|
||||
`"1.1"`: "1.1",
|
||||
`"-123.1"`: "-123.1",
|
||||
"0.0": "0.0",
|
||||
"0": "0",
|
||||
`"0"`: "0",
|
||||
`"0.0"`: "0.0",
|
||||
`"00.0"`: "00.0",
|
||||
"true": "true",
|
||||
"false": "false",
|
||||
`"true"`: "true",
|
||||
`"false"`: "false",
|
||||
`"true123"`: "true123",
|
||||
`"+1"`: "+1",
|
||||
"[]": "[]",
|
||||
"[1,2]": "[1,2]",
|
||||
"{}": "{}",
|
||||
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
||||
}
|
||||
|
||||
func Test_read_any_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for k, v := range stringConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(v, any.ToString(), "original val "+k)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_string_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`"hello"`))
|
||||
should.Equal("hello", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`" "`))
|
||||
should.False(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`"false"`))
|
||||
should.True(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`"123"`))
|
||||
should.Equal(123, any.ToInt())
|
||||
}
|
||||
|
||||
func Test_wrap_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("-32000")).MustBeValid()
|
||||
should.Equal(-32000, any.ToInt())
|
||||
should.NoError(any.LastError())
|
||||
}
|
@ -1,71 +1,72 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// if must be valid is useless, just drop this test
|
||||
func Test_must_be_valid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("123"))
|
||||
any := jsoniter.Get([]byte("123"))
|
||||
should.Equal(any.MustBeValid().ToInt(), 123)
|
||||
|
||||
any = Wrap(int8(10))
|
||||
any = jsoniter.Wrap(int8(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(int16(10))
|
||||
any = jsoniter.Wrap(int16(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(int32(10))
|
||||
any = jsoniter.Wrap(int32(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(int64(10))
|
||||
any = jsoniter.Wrap(int64(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(uint(10))
|
||||
any = jsoniter.Wrap(uint(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(uint8(10))
|
||||
any = jsoniter.Wrap(uint8(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(uint16(10))
|
||||
any = jsoniter.Wrap(uint16(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(uint32(10))
|
||||
any = jsoniter.Wrap(uint32(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(uint64(10))
|
||||
any = jsoniter.Wrap(uint64(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = Wrap(float32(10))
|
||||
any = jsoniter.Wrap(float32(10))
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
|
||||
|
||||
any = Wrap(float64(10))
|
||||
any = jsoniter.Wrap(float64(10))
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
|
||||
|
||||
any = Wrap(true)
|
||||
any = jsoniter.Wrap(true)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
|
||||
|
||||
any = Wrap(false)
|
||||
any = jsoniter.Wrap(false)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap(nil)
|
||||
any = jsoniter.Wrap(nil)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap(struct{ age int }{age: 1})
|
||||
any = jsoniter.Wrap(struct{ age int }{age: 1})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap(map[string]interface{}{"abc": 1})
|
||||
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap("abc")
|
||||
any = jsoniter.Wrap("abc")
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap([]int{})
|
||||
any = jsoniter.Wrap([]int{})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = Wrap([]int{1, 2})
|
||||
any = jsoniter.Wrap([]int{1, 2})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
|
||||
}
|
@ -1,116 +1,117 @@
|
||||
package jsoniter
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_wrap_and_valuetype_everything(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var i interface{}
|
||||
any := Get([]byte("123"))
|
||||
any := jsoniter.Get([]byte("123"))
|
||||
// default of number type is float64
|
||||
i = float64(123)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(int8(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(int8(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// get interface is not int8 interface
|
||||
// i = int8(10)
|
||||
// should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(int16(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(int16(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
//i = int16(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(int32(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(int32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = int32(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = Wrap(int64(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(int64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = int64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(uint(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(uint(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
//i = uint(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
any = Wrap(uint8(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(uint8(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
// i = uint8(10)
|
||||
// should.Equal(i, any.GetInterface())
|
||||
any = Wrap(uint16(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(uint16(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
any = Wrap(uint32(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(uint32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = uint32(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = Wrap(uint64(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(uint64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = uint64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(float32(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(float32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
//i = float32(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
any = Wrap(float64(10))
|
||||
should.Equal(any.ValueType(), Number)
|
||||
any = jsoniter.Wrap(float64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = float64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(true)
|
||||
should.Equal(any.ValueType(), Bool)
|
||||
any = jsoniter.Wrap(true)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = true
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = Wrap(false)
|
||||
should.Equal(any.ValueType(), Bool)
|
||||
any = jsoniter.Wrap(false)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = false
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(nil)
|
||||
should.Equal(any.ValueType(), Nil)
|
||||
any = jsoniter.Wrap(nil)
|
||||
should.Equal(any.ValueType(), jsoniter.NilValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = nil
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
stream := NewStream(ConfigDefault, nil, 32)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("null", string(stream.Buffer()))
|
||||
should.Equal(any.LastError(), nil)
|
||||
|
||||
any = Wrap(struct{ age int }{age: 1})
|
||||
should.Equal(any.ValueType(), Object)
|
||||
any = jsoniter.Wrap(struct{ age int }{age: 1})
|
||||
should.Equal(any.ValueType(), jsoniter.ObjectValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = struct{ age int }{age: 1}
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap(map[string]interface{}{"abc": 1})
|
||||
should.Equal(any.ValueType(), Object)
|
||||
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
|
||||
should.Equal(any.ValueType(), jsoniter.ObjectValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = map[string]interface{}{"abc": 1}
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = Wrap("abc")
|
||||
any = jsoniter.Wrap("abc")
|
||||
i = "abc"
|
||||
should.Equal(i, any.GetInterface())
|
||||
should.Equal(nil, any.LastError())
|
@ -14,7 +14,7 @@ func (any *uint32Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *uint32Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *uint32Any) MustBeValid() Any {
|
@ -14,7 +14,7 @@ func (any *uint64Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *uint64Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *uint64Any) MustBeValid() Any {
|
229
api_tests/config_test.go
Normal file
229
api_tests/config_test.go
Normal file
@ -0,0 +1,229 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_use_number_for_unmarshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
api := jsoniter.Config{UseNumber: true}.Froze()
|
||||
var obj interface{}
|
||||
should.Nil(api.UnmarshalFromString("123", &obj))
|
||||
should.Equal(json.Number("123"), obj)
|
||||
}
|
||||
|
||||
func Test_customize_float_marshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
json := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze()
|
||||
str, err := json.MarshalToString(float32(1.23456789))
|
||||
should.Nil(err)
|
||||
should.Equal("1.234568", str)
|
||||
}
|
||||
|
||||
func Test_customize_tag_key(t *testing.T) {
|
||||
|
||||
type TestObject struct {
|
||||
Field string `orm:"field"`
|
||||
}
|
||||
|
||||
should := require.New(t)
|
||||
json := jsoniter.Config{TagKey: "orm"}.Froze()
|
||||
str, err := json.MarshalToString(TestObject{"hello"})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_read_large_number_as_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val interface{}
|
||||
err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
|
||||
should.Nil(err)
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`123456789123456789123456789`, output)
|
||||
}
|
||||
|
||||
type caseSensitiveStruct struct {
|
||||
A string `json:"a"`
|
||||
B string `json:"b,omitempty"`
|
||||
C *C `json:"C,omitempty"`
|
||||
}
|
||||
|
||||
type C struct {
|
||||
D int64 `json:"D,omitempty"`
|
||||
E *E `json:"e,omitempty"`
|
||||
}
|
||||
|
||||
type E struct {
|
||||
F string `json:"F,omitempty"`
|
||||
}
|
||||
|
||||
func Test_CaseSensitive(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
caseSensitive bool
|
||||
}{
|
||||
{
|
||||
input: `{"A":"foo","B":"bar"}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","b":"bar"}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","B":"bar","c":{"d":10}}`,
|
||||
expectedOutput: `{"a":"foo"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"d":10}}`,
|
||||
expectedOutput: `{"a":"foo","C":{}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"D":10,"e":{"f":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{}}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"A":"foo","c":{"d":10,"E":{"f":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
val := caseSensitiveStruct{}
|
||||
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||
should.Nil(err)
|
||||
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(tc.expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
type structWithElevenFields struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B string `json:"B,omitempty"`
|
||||
C string `json:"C,omitempty"`
|
||||
D string `json:"d,omitempty"`
|
||||
E string `json:"e,omitempty"`
|
||||
F string `json:"f,omitempty"`
|
||||
G string `json:"g,omitempty"`
|
||||
H string `json:"h,omitempty"`
|
||||
I string `json:"i,omitempty"`
|
||||
J string `json:"j,omitempty"`
|
||||
K string `json:"k,omitempty"`
|
||||
}
|
||||
|
||||
func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
caseSensitive bool
|
||||
}{
|
||||
{
|
||||
input: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"1","b":"2","c":"3","D":"4","E":"5","F":"6"}`,
|
||||
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6"}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
{
|
||||
input: `{"A":"1","b":"2","d":"4","E":"5"}`,
|
||||
expectedOutput: `{"A":"1","d":"4"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
val := structWithElevenFields{}
|
||||
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||
should.Nil(err)
|
||||
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(tc.expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
type onlyTaggedFieldStruct struct {
|
||||
A string `json:"a"`
|
||||
B string
|
||||
FSimpl F `json:"f_simpl"`
|
||||
ISimpl I
|
||||
FPtr *F `json:"f_ptr"`
|
||||
IPtr *I
|
||||
F
|
||||
*I
|
||||
}
|
||||
|
||||
type F struct {
|
||||
G string `json:"g"`
|
||||
H string
|
||||
}
|
||||
|
||||
type I struct {
|
||||
J string `json:"j"`
|
||||
K string
|
||||
}
|
||||
|
||||
func Test_OnlyTaggedField(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
obj := onlyTaggedFieldStruct{
|
||||
A: "a",
|
||||
B: "b",
|
||||
FSimpl: F{G: "g", H: "h"},
|
||||
ISimpl: I{J: "j", K: "k"},
|
||||
FPtr: &F{G: "g", H: "h"},
|
||||
IPtr: &I{J: "j", K: "k"},
|
||||
F: F{G: "g", H: "h"},
|
||||
I: &I{J: "j", K: "k"},
|
||||
}
|
||||
|
||||
output, err := jsoniter.Config{OnlyTaggedField: true}.Froze().Marshal(obj)
|
||||
should.Nil(err)
|
||||
|
||||
m := make(map[string]interface{})
|
||||
err = jsoniter.Unmarshal(output, &m)
|
||||
should.Nil(err)
|
||||
|
||||
should.Equal(map[string]interface{}{
|
||||
"a": "a",
|
||||
"f_simpl": map[string]interface{}{
|
||||
"g": "g",
|
||||
},
|
||||
"f_ptr": map[string]interface{}{
|
||||
"g": "g",
|
||||
},
|
||||
"g": "g",
|
||||
"j": "j",
|
||||
}, m)
|
||||
}
|
@ -1,17 +1,27 @@
|
||||
package jsoniter
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_disallowUnknownFields(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct{}
|
||||
var obj TestObject
|
||||
decoder := jsoniter.NewDecoder(bytes.NewBufferString(`{"field1":100}`))
|
||||
decoder.DisallowUnknownFields()
|
||||
should.Error(decoder.Decode(&obj))
|
||||
}
|
||||
|
||||
func Test_new_decoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
|
||||
decoder2 := NewDecoder(bytes.NewBufferString(`[1][2]`))
|
||||
decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`[1][2]`))
|
||||
arr1 := []int{}
|
||||
should.Nil(decoder1.Decode(&arr1))
|
||||
should.Equal([]int{1}, arr1)
|
||||
@ -37,7 +47,7 @@ func Test_use_number(t *testing.T) {
|
||||
should := require.New(t)
|
||||
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
|
||||
decoder1.UseNumber()
|
||||
decoder2 := NewDecoder(bytes.NewBufferString(`123`))
|
||||
decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`123`))
|
||||
decoder2.UseNumber()
|
||||
var obj1 interface{}
|
||||
should.Nil(decoder1.Decode(&obj1))
|
||||
@ -47,38 +57,8 @@ func Test_use_number(t *testing.T) {
|
||||
should.Equal(json.Number("123"), obj2)
|
||||
}
|
||||
|
||||
func Test_use_number_for_unmarshal(t *testing.T) {
|
||||
func Test_decoder_more(t *testing.T) {
|
||||
should := require.New(t)
|
||||
api := Config{UseNumber: true}.Froze()
|
||||
var obj interface{}
|
||||
should.Nil(api.UnmarshalFromString("123", &obj))
|
||||
should.Equal(json.Number("123"), obj)
|
||||
}
|
||||
|
||||
func Test_marshal_indent(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := struct {
|
||||
F1 int
|
||||
F2 []int
|
||||
}{1, []int{2, 3, 4}}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
output, err = MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
}
|
||||
|
||||
func Test_marshal_indent_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := map[int]int{1: 2}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
decoder := jsoniter.NewDecoder(bytes.NewBufferString("abcde"))
|
||||
should.True(decoder.More())
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// +build go1.8
|
||||
//+build go1.8
|
||||
|
||||
package jsoniter
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -19,14 +20,14 @@ func Test_new_encoder(t *testing.T) {
|
||||
encoder1.Encode([]int{1})
|
||||
should.Equal("[1]\n", buf1.String())
|
||||
buf2 := &bytes.Buffer{}
|
||||
encoder2 := NewEncoder(buf2)
|
||||
encoder2 := jsoniter.NewEncoder(buf2)
|
||||
encoder2.SetEscapeHTML(false)
|
||||
encoder2.Encode([]int{1})
|
||||
should.Equal("[1]", buf2.String())
|
||||
should.Equal("[1]\n", buf2.String())
|
||||
}
|
||||
|
||||
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
||||
api := Config{EscapeHTML: false}.Froze()
|
||||
api := jsoniter.Config{EscapeHTML: false}.Froze()
|
||||
should := require.New(t)
|
||||
for i := 0; i < utf8.RuneSelf; i++ {
|
||||
input := string([]byte{byte(i)})
|
20
api_tests/encoder_test.go
Normal file
20
api_tests/encoder_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Standard Encoder has trailing newline.
|
||||
func TestEncoderHasTrailingNewline(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var buf, stdbuf bytes.Buffer
|
||||
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||
enc.Encode(1)
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.Encode(1)
|
||||
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||
}
|
36
api_tests/marshal_indent_test.go
Normal file
36
api_tests/marshal_indent_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_marshal_indent(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := struct {
|
||||
F1 int
|
||||
F2 []int
|
||||
}{1, []int{2, 3, 4}}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
output, err = jsoniter.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
}
|
||||
|
||||
func Test_marshal_indent_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := map[int]int{1: 2}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = jsoniter.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
}
|
47
api_tests/marshal_json_escape_test.go
Normal file
47
api_tests/marshal_json_escape_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var marshalConfig = jsoniter.Config{
|
||||
EscapeHTML: false,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
type Container struct {
|
||||
Bar interface{}
|
||||
}
|
||||
|
||||
func (c *Container) MarshalJSON() ([]byte, error) {
|
||||
return marshalConfig.Marshal(&c.Bar)
|
||||
}
|
||||
|
||||
func TestEncodeEscape(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
container := &Container{
|
||||
Bar: []string{"123<ab>", "ooo"},
|
||||
}
|
||||
out, err := marshalConfig.Marshal(container)
|
||||
should.Nil(err)
|
||||
bufout := string(out)
|
||||
|
||||
var stdbuf bytes.Buffer
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.SetEscapeHTML(false)
|
||||
err = stdenc.Encode(container)
|
||||
should.Nil(err)
|
||||
stdout := string(stdbuf.Bytes())
|
||||
if stdout[len(stdout)-1:] == "\n" {
|
||||
stdout = stdout[:len(stdout)-1]
|
||||
}
|
||||
|
||||
should.Equal(stdout, bufout)
|
||||
}
|
36
api_tests/marshal_json_test.go
Normal file
36
api_tests/marshal_json_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
type Foo struct {
|
||||
Bar interface{}
|
||||
}
|
||||
|
||||
func (f Foo) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(f.Bar)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
|
||||
// Standard Encoder has trailing newline.
|
||||
func TestEncodeMarshalJSON(t *testing.T) {
|
||||
|
||||
foo := Foo {
|
||||
Bar: 123,
|
||||
}
|
||||
should := require.New(t)
|
||||
var buf, stdbuf bytes.Buffer
|
||||
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||
enc.Encode(foo)
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.Encode(foo)
|
||||
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||
}
|
25
benchmarks/encode_string_test.go
Normal file
25
benchmarks/encode_string_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/json-iterator/go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_encode_string_with_SetEscapeHTML(b *testing.B) {
|
||||
type V struct {
|
||||
S string
|
||||
B bool
|
||||
I int
|
||||
}
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(true)
|
||||
if err := enc.Encode(V{S: "s", B: true, I: 233}); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
158
benchmarks/jsoniter_large_file_test.go
Normal file
158
benchmarks/jsoniter_large_file_test.go
Normal file
@ -0,0 +1,158 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func Test_large_file(t *testing.T) {
|
||||
// file, err := os.Open("/tmp/large-file.json")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// iter := Parse(file, 4096)
|
||||
// count := 0
|
||||
// for iter.ReadArray() {
|
||||
// iter.Skip()
|
||||
// count++
|
||||
// }
|
||||
// if count != 11351 {
|
||||
// t.Fatal(count)
|
||||
// }
|
||||
//}
|
||||
|
||||
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) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
iter := jsoniter.Parse(jsoniter.ConfigDefault, file, 4096)
|
||||
count := 0
|
||||
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
|
||||
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
|
||||
iter.Skip()
|
||||
count++
|
||||
return true
|
||||
})
|
||||
file.Close()
|
||||
if iter.Error != nil {
|
||||
b.Error(iter.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
file.Close()
|
||||
result := []struct{}{}
|
||||
err := json.Unmarshal(bytes, &result)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
128
benchmarks/stream_test.go
Normal file
128
benchmarks/stream_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func Benchmark_stream_encode_big_object(b *testing.B) {
|
||||
var buf bytes.Buffer
|
||||
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 100)
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
stream.Reset(&buf)
|
||||
encodeObject(stream)
|
||||
if stream.Error != nil {
|
||||
b.Errorf("error: %+v", stream.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeObject(t *testing.T) {
|
||||
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 100)
|
||||
encodeObject(stream)
|
||||
if stream.Error != nil {
|
||||
t.Errorf("error encoding a test object: %+v", stream.Error)
|
||||
return
|
||||
}
|
||||
var m = make(map[string]interface{})
|
||||
if err := jsoniter.Unmarshal(stream.Buffer(), &m); err != nil {
|
||||
t.Errorf("error unmarshaling a test object: %+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func encodeObject(stream *jsoniter.Stream) {
|
||||
stream.WriteObjectStart()
|
||||
|
||||
stream.WriteObjectField("objectId")
|
||||
stream.WriteUint64(8838243212)
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("name")
|
||||
stream.WriteString("Jane Doe")
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("address")
|
||||
stream.WriteObjectStart()
|
||||
for i, field := range addressFields {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(field.key)
|
||||
stream.WriteString(field.val)
|
||||
}
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("geo")
|
||||
{
|
||||
stream.WriteObjectStart()
|
||||
stream.WriteObjectField("latitude")
|
||||
stream.WriteFloat64(-154.550817)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("longitude")
|
||||
stream.WriteFloat64(-84.176159)
|
||||
stream.WriteObjectEnd()
|
||||
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("specialties")
|
||||
stream.WriteArrayStart()
|
||||
for i, s := range specialties {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteString(s)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
|
||||
stream.WriteMore()
|
||||
for i, text := range longText {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField("longText" + strconv.Itoa(i))
|
||||
stream.WriteString(text)
|
||||
}
|
||||
|
||||
for i := 0; i < 25; i++ {
|
||||
num := i * 18328
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("integerField" + strconv.Itoa(i))
|
||||
stream.WriteInt64(int64(num))
|
||||
}
|
||||
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
type field struct{ key, val string }
|
||||
|
||||
var (
|
||||
addressFields = []field{
|
||||
{"address1", "123 Example St"},
|
||||
{"address2", "Apartment 5D, Suite 3"},
|
||||
{"city", "Miami"},
|
||||
{"state", "FL"},
|
||||
{"postalCode", "33133"},
|
||||
{"country", "US"},
|
||||
}
|
||||
specialties = []string{
|
||||
"Web Design",
|
||||
"Go Programming",
|
||||
"Tennis",
|
||||
"Cycling",
|
||||
"Mixed martial arts",
|
||||
}
|
||||
longText = []string{
|
||||
`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
|
||||
`Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`,
|
||||
`But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?`,
|
||||
`At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.`,
|
||||
`On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.`,
|
||||
}
|
||||
)
|
12
build.sh
Executable file
12
build.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then
|
||||
mkdir -p /tmp/build-golang/src/github.com/json-iterator
|
||||
ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go
|
||||
fi
|
||||
export GOPATH=/tmp/build-golang
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
cd /tmp/build-golang/src/github.com/json-iterator/go
|
||||
exec $GOPATH/bin/dep ensure -update
|
375
config.go
Normal file
375
config.go
Normal file
@ -0,0 +1,375 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/modern-go/concurrent"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
// Config customize how the API should behave.
|
||||
// The API is created from Config by Froze.
|
||||
type Config struct {
|
||||
IndentionStep int
|
||||
MarshalFloatWith6Digits bool
|
||||
EscapeHTML bool
|
||||
SortMapKeys bool
|
||||
UseNumber bool
|
||||
DisallowUnknownFields bool
|
||||
TagKey string
|
||||
OnlyTaggedField bool
|
||||
ValidateJsonRawMessage bool
|
||||
ObjectFieldMustBeSimpleString bool
|
||||
CaseSensitive bool
|
||||
}
|
||||
|
||||
// API the public interface of this package.
|
||||
// Primary Marshal and Unmarshal.
|
||||
type API interface {
|
||||
IteratorPool
|
||||
StreamPool
|
||||
MarshalToString(v interface{}) (string, error)
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||
UnmarshalFromString(str string, v interface{}) error
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
Get(data []byte, path ...interface{}) Any
|
||||
NewEncoder(writer io.Writer) *Encoder
|
||||
NewDecoder(reader io.Reader) *Decoder
|
||||
Valid(data []byte) bool
|
||||
RegisterExtension(extension Extension)
|
||||
DecoderOf(typ reflect2.Type) ValDecoder
|
||||
EncoderOf(typ reflect2.Type) ValEncoder
|
||||
}
|
||||
|
||||
// ConfigDefault the default API
|
||||
var ConfigDefault = Config{
|
||||
EscapeHTML: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
||||
var ConfigCompatibleWithStandardLibrary = Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigFastest marshals float with only 6 digits precision
|
||||
var ConfigFastest = Config{
|
||||
EscapeHTML: false,
|
||||
MarshalFloatWith6Digits: true, // will lose precession
|
||||
ObjectFieldMustBeSimpleString: true, // do not unescape object field
|
||||
}.Froze()
|
||||
|
||||
type frozenConfig struct {
|
||||
configBeforeFrozen Config
|
||||
sortMapKeys bool
|
||||
indentionStep int
|
||||
objectFieldMustBeSimpleString bool
|
||||
onlyTaggedField bool
|
||||
disallowUnknownFields bool
|
||||
decoderCache *concurrent.Map
|
||||
encoderCache *concurrent.Map
|
||||
encoderExtension Extension
|
||||
decoderExtension Extension
|
||||
extraExtensions []Extension
|
||||
streamPool *sync.Pool
|
||||
iteratorPool *sync.Pool
|
||||
caseSensitive bool
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) initCache() {
|
||||
cfg.decoderCache = concurrent.NewMap()
|
||||
cfg.encoderCache = concurrent.NewMap()
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
|
||||
cfg.decoderCache.Store(cacheKey, decoder)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
|
||||
cfg.encoderCache.Store(cacheKey, encoder)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
|
||||
decoder, found := cfg.decoderCache.Load(cacheKey)
|
||||
if found {
|
||||
return decoder.(ValDecoder)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
|
||||
encoder, found := cfg.encoderCache.Load(cacheKey)
|
||||
if found {
|
||||
return encoder.(ValEncoder)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var cfgCache = concurrent.NewMap()
|
||||
|
||||
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
|
||||
obj, found := cfgCache.Load(cfg)
|
||||
if found {
|
||||
return obj.(*frozenConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
|
||||
cfgCache.Store(cfg, frozenConfig)
|
||||
}
|
||||
|
||||
// Froze forge API from config
|
||||
func (cfg Config) Froze() API {
|
||||
api := &frozenConfig{
|
||||
sortMapKeys: cfg.SortMapKeys,
|
||||
indentionStep: cfg.IndentionStep,
|
||||
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
||||
onlyTaggedField: cfg.OnlyTaggedField,
|
||||
disallowUnknownFields: cfg.DisallowUnknownFields,
|
||||
caseSensitive: cfg.CaseSensitive,
|
||||
}
|
||||
api.streamPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return NewStream(api, nil, 512)
|
||||
},
|
||||
}
|
||||
api.iteratorPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return NewIterator(api)
|
||||
},
|
||||
}
|
||||
api.initCache()
|
||||
encoderExtension := EncoderExtension{}
|
||||
decoderExtension := DecoderExtension{}
|
||||
if cfg.MarshalFloatWith6Digits {
|
||||
api.marshalFloatWith6Digits(encoderExtension)
|
||||
}
|
||||
if cfg.EscapeHTML {
|
||||
api.escapeHTML(encoderExtension)
|
||||
}
|
||||
if cfg.UseNumber {
|
||||
api.useNumber(decoderExtension)
|
||||
}
|
||||
if cfg.ValidateJsonRawMessage {
|
||||
api.validateJsonRawMessage(encoderExtension)
|
||||
}
|
||||
api.encoderExtension = encoderExtension
|
||||
api.decoderExtension = decoderExtension
|
||||
api.configBeforeFrozen = cfg
|
||||
return api
|
||||
}
|
||||
|
||||
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
|
||||
api := getFrozenConfigFromCache(cfg)
|
||||
if api != nil {
|
||||
return api
|
||||
}
|
||||
api = cfg.Froze().(*frozenConfig)
|
||||
for _, extension := range extraExtensions {
|
||||
api.RegisterExtension(extension)
|
||||
}
|
||||
addFrozenConfigToCache(cfg, api)
|
||||
return api
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
|
||||
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
|
||||
rawMessage := *(*json.RawMessage)(ptr)
|
||||
iter := cfg.BorrowIterator([]byte(rawMessage))
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.Read()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
stream.WriteRaw("null")
|
||||
} else {
|
||||
stream.WriteRaw(string(rawMessage))
|
||||
}
|
||||
}, func(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.RawMessage)(ptr))) == 0
|
||||
}}
|
||||
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
||||
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
|
||||
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
exitingValue := *((*interface{})(ptr))
|
||||
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
|
||||
iter.ReadVal(exitingValue)
|
||||
return
|
||||
}
|
||||
if iter.WhatIsNext() == NumberValue {
|
||||
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
|
||||
} else {
|
||||
*((*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) {
|
||||
cfg.extraExtensions = append(cfg.extraExtensions, extension)
|
||||
copied := cfg.configBeforeFrozen
|
||||
cfg.configBeforeFrozen = copied
|
||||
}
|
||||
|
||||
type lossyFloat32Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat32Lossy(*((*float32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type lossyFloat64Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat64Lossy(*((*float64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float64)(ptr)) == 0
|
||||
}
|
||||
|
||||
// EnableLossyFloatMarshalling keeps 10**(-6) precision
|
||||
// for float variables for better performance.
|
||||
func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
|
||||
// for better performance
|
||||
extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{}
|
||||
extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{}
|
||||
}
|
||||
|
||||
type htmlEscapedStringEncoder struct {
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
stream.WriteStringWithHTMLEscaped(str)
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*string)(ptr)) == ""
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
|
||||
encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanDecoders() {
|
||||
typeDecoders = map[string]ValDecoder{}
|
||||
fieldDecoders = map[string]ValDecoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanEncoders() {
|
||||
typeEncoders = map[string]ValEncoder{}
|
||||
fieldEncoders = map[string]ValEncoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return "", stream.Error
|
||||
}
|
||||
return string(stream.Buffer()), nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return nil, stream.Error
|
||||
}
|
||||
result := stream.Buffer()
|
||||
copied := make([]byte, len(result))
|
||||
copy(copied, result)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
if prefix != "" {
|
||||
panic("prefix is not supported")
|
||||
}
|
||||
for _, r := range indent {
|
||||
if r != ' ' {
|
||||
panic("indent can only be space")
|
||||
}
|
||||
}
|
||||
newCfg := cfg.configBeforeFrozen
|
||||
newCfg.IndentionStep = len(indent)
|
||||
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
||||
data := []byte(str)
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(v)
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
return locatePath(iter, path)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(v)
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
|
||||
stream := NewStream(cfg, writer, 512)
|
||||
return &Encoder{stream}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
|
||||
iter := Parse(cfg, reader, 512)
|
||||
return &Decoder{iter}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Valid(data []byte) bool {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.Skip()
|
||||
return iter.Error == nil
|
||||
}
|
@ -3,6 +3,7 @@ package jsoniter
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExampleMarshal() {
|
||||
@ -93,3 +94,28 @@ func ExampleGet() {
|
||||
// Output:
|
||||
// Crimson
|
||||
}
|
||||
|
||||
func ExampleMyKey() {
|
||||
hello := MyKey("hello")
|
||||
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
|
||||
fmt.Println(string(output))
|
||||
obj := map[*MyKey]string{}
|
||||
Unmarshal(output, &obj)
|
||||
for k, v := range obj {
|
||||
fmt.Println(*k, v)
|
||||
}
|
||||
// Output:
|
||||
// {"Hello":"world"}
|
||||
// Hel world
|
||||
}
|
||||
|
||||
type MyKey string
|
||||
|
||||
func (m *MyKey) MarshalText() ([]byte, error) {
|
||||
return []byte(strings.Replace(string(*m), "h", "H", -1)), nil
|
||||
}
|
||||
|
||||
func (m *MyKey) UnmarshalText(text []byte) error {
|
||||
*m = MyKey(text[:3])
|
||||
return nil
|
||||
}
|
||||
|
223
extension_tests/decoder_test.go
Normal file
223
extension_tests/decoder_test.go
Normal file
@ -0,0 +1,223 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Test_customize_type_decoder(t *testing.T) {
|
||||
t.Skip()
|
||||
jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
*((*time.Time)(ptr)) = t
|
||||
})
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
val := time.Time{}
|
||||
err := jsoniter.Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
year, month, day := val.Date()
|
||||
if year != 2016 || month != 12 || day != 5 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_customize_byte_array_encoder(t *testing.T) {
|
||||
t.Skip()
|
||||
//jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
should := require.New(t)
|
||||
jsoniter.RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
t := *((*[]byte)(ptr))
|
||||
stream.WriteString(string(t))
|
||||
}, nil)
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
val := []byte("abc")
|
||||
str, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`"abc"`, str)
|
||||
}
|
||||
|
||||
type CustomEncoderAttachmentTestStruct struct {
|
||||
Value int32 `json:"value"`
|
||||
}
|
||||
|
||||
type CustomEncoderAttachmentTestStructEncoder struct {}
|
||||
|
||||
func (c *CustomEncoderAttachmentTestStructEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
attachVal, ok := stream.Attachment.(int)
|
||||
stream.WriteRaw(`"`)
|
||||
stream.WriteRaw(fmt.Sprintf("%t %d", ok, attachVal))
|
||||
stream.WriteRaw(`"`)
|
||||
}
|
||||
|
||||
func (c *CustomEncoderAttachmentTestStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func Test_custom_encoder_attachment(t *testing.T) {
|
||||
|
||||
jsoniter.RegisterTypeEncoder("test.CustomEncoderAttachmentTestStruct", &CustomEncoderAttachmentTestStructEncoder{})
|
||||
expectedValue := 17
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.Config{SortMapKeys: true}.Froze(), buf, 4096)
|
||||
stream.Attachment = expectedValue
|
||||
val := map[string]CustomEncoderAttachmentTestStruct{"a": {}}
|
||||
stream.WriteVal(val)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("{\"a\":\"true 17\"}", buf.String())
|
||||
}
|
||||
|
||||
func Test_customize_field_decoder(t *testing.T) {
|
||||
type Tom struct {
|
||||
field1 string
|
||||
}
|
||||
jsoniter.RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
})
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
tom := Tom{}
|
||||
err := jsoniter.Unmarshal([]byte(`{"field1": 100}`), &tom)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_recursive_empty_interface_customization(t *testing.T) {
|
||||
t.Skip()
|
||||
var obj interface{}
|
||||
jsoniter.RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
switch iter.WhatIsNext() {
|
||||
case jsoniter.NumberValue:
|
||||
*(*interface{})(ptr) = iter.ReadInt64()
|
||||
default:
|
||||
*(*interface{})(ptr) = iter.Read()
|
||||
}
|
||||
})
|
||||
should := require.New(t)
|
||||
jsoniter.Unmarshal([]byte("[100]"), &obj)
|
||||
should.Equal([]interface{}{int64(100)}, obj)
|
||||
}
|
||||
|
||||
type MyInterface interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
type MyString string
|
||||
|
||||
func (ms MyString) Hello() string {
|
||||
return string(ms)
|
||||
}
|
||||
|
||||
func Test_read_custom_interface(t *testing.T) {
|
||||
t.Skip()
|
||||
should := require.New(t)
|
||||
var val MyInterface
|
||||
jsoniter.RegisterTypeDecoderFunc("jsoniter.MyInterface", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*MyInterface)(ptr)) = MyString(iter.ReadString())
|
||||
})
|
||||
err := jsoniter.UnmarshalFromString(`"hello"`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", val.Hello())
|
||||
}
|
||||
|
||||
const flow1 = `
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}`
|
||||
|
||||
const flow2 = `
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
`
|
||||
|
||||
type (
|
||||
Type1 struct {
|
||||
A string
|
||||
}
|
||||
|
||||
Type2 struct {
|
||||
A string
|
||||
}
|
||||
)
|
||||
|
||||
func (t *Type2) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Type2) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestType1NoFinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow1))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type1{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType1FinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow2))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type1{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType2NoFinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow1))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type2{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType2FinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow2))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type2{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
120
extension_tests/extension_test.go
Normal file
120
extension_tests/extension_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/stretchr/testify/require"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type TestObject1 struct {
|
||||
Field1 string
|
||||
}
|
||||
|
||||
type testExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
if structDescriptor.Type.String() != "test.TestObject1" {
|
||||
return
|
||||
}
|
||||
binding := structDescriptor.GetField("Field1")
|
||||
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
str := *((*string)(ptr))
|
||||
val, _ := strconv.Atoi(str)
|
||||
stream.WriteInt(val)
|
||||
}}
|
||||
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
}}
|
||||
binding.ToNames = []string{"field-1"}
|
||||
binding.FromNames = []string{"field-1"}
|
||||
}
|
||||
|
||||
func Test_customize_field_by_extension(t *testing.T) {
|
||||
should := require.New(t)
|
||||
cfg := jsoniter.Config{}.Froze()
|
||||
cfg.RegisterExtension(&testExtension{})
|
||||
obj := TestObject1{}
|
||||
err := cfg.UnmarshalFromString(`{"field-1": 100}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Equal("100", obj.Field1)
|
||||
str, err := cfg.MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-1":100}`, str)
|
||||
}
|
||||
|
||||
func Test_customize_map_key_encoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
cfg := jsoniter.Config{}.Froze()
|
||||
cfg.RegisterExtension(&testMapKeyExtension{})
|
||||
m := map[int]int{1: 2}
|
||||
output, err := cfg.MarshalToString(m)
|
||||
should.NoError(err)
|
||||
should.Equal(`{"2":2}`, output)
|
||||
m = map[int]int{}
|
||||
should.NoError(cfg.UnmarshalFromString(output, &m))
|
||||
should.Equal(map[int]int{1: 2}, m)
|
||||
}
|
||||
|
||||
type testMapKeyExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *testMapKeyExtension) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ.Kind() == reflect.Int {
|
||||
return &funcEncoder{
|
||||
fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
stream.WriteRaw(`"`)
|
||||
stream.WriteInt(*(*int)(ptr) + 1)
|
||||
stream.WriteRaw(`"`)
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (extension *testMapKeyExtension) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ.Kind() == reflect.Int {
|
||||
return &funcDecoder{
|
||||
fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
i, err := strconv.Atoi(iter.ReadString())
|
||||
if err != nil {
|
||||
iter.ReportError("read map key", err.Error())
|
||||
return
|
||||
}
|
||||
i--
|
||||
*(*int)(ptr) = i
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type funcDecoder struct {
|
||||
fun jsoniter.DecoderFunc
|
||||
}
|
||||
|
||||
func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
decoder.fun(ptr, iter)
|
||||
}
|
||||
|
||||
type funcEncoder struct {
|
||||
fun jsoniter.EncoderFunc
|
||||
isEmptyFunc func(ptr unsafe.Pointer) bool
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
encoder.fun(ptr, stream)
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
if encoder.isEmptyFunc == nil {
|
||||
return false
|
||||
}
|
||||
return encoder.isEmptyFunc(ptr)
|
||||
}
|
238
extra/binary_as_string_codec.go
Normal file
238
extra/binary_as_string_codec.go
Normal file
@ -0,0 +1,238 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// safeSet holds the value true if the ASCII character with the given array
|
||||
// position can be represented inside a JSON string without any further
|
||||
// escaping.
|
||||
//
|
||||
// All values are true except for the ASCII control characters (0-31), the
|
||||
// double quote ("), and the backslash character ("\").
|
||||
var safeSet = [utf8.RuneSelf]bool{
|
||||
' ': true,
|
||||
'!': true,
|
||||
'"': false,
|
||||
'#': true,
|
||||
'$': true,
|
||||
'%': true,
|
||||
'&': true,
|
||||
'\'': true,
|
||||
'(': true,
|
||||
')': true,
|
||||
'*': true,
|
||||
'+': true,
|
||||
',': true,
|
||||
'-': true,
|
||||
'.': true,
|
||||
'/': true,
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
':': true,
|
||||
';': true,
|
||||
'<': true,
|
||||
'=': true,
|
||||
'>': true,
|
||||
'?': true,
|
||||
'@': true,
|
||||
'A': true,
|
||||
'B': true,
|
||||
'C': true,
|
||||
'D': true,
|
||||
'E': true,
|
||||
'F': true,
|
||||
'G': true,
|
||||
'H': true,
|
||||
'I': true,
|
||||
'J': true,
|
||||
'K': true,
|
||||
'L': true,
|
||||
'M': true,
|
||||
'N': true,
|
||||
'O': true,
|
||||
'P': true,
|
||||
'Q': true,
|
||||
'R': true,
|
||||
'S': true,
|
||||
'T': true,
|
||||
'U': true,
|
||||
'V': true,
|
||||
'W': true,
|
||||
'X': true,
|
||||
'Y': true,
|
||||
'Z': true,
|
||||
'[': true,
|
||||
'\\': false,
|
||||
']': true,
|
||||
'^': true,
|
||||
'_': true,
|
||||
'`': true,
|
||||
'a': true,
|
||||
'b': true,
|
||||
'c': true,
|
||||
'd': true,
|
||||
'e': true,
|
||||
'f': true,
|
||||
'g': true,
|
||||
'h': true,
|
||||
'i': true,
|
||||
'j': true,
|
||||
'k': true,
|
||||
'l': true,
|
||||
'm': true,
|
||||
'n': true,
|
||||
'o': true,
|
||||
'p': true,
|
||||
'q': true,
|
||||
'r': true,
|
||||
's': true,
|
||||
't': true,
|
||||
'u': true,
|
||||
'v': true,
|
||||
'w': true,
|
||||
'x': true,
|
||||
'y': true,
|
||||
'z': true,
|
||||
'{': true,
|
||||
'|': true,
|
||||
'}': true,
|
||||
'~': true,
|
||||
'\u007f': true,
|
||||
}
|
||||
|
||||
var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
|
||||
|
||||
type BinaryAsStringExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ == binaryType {
|
||||
return &binaryAsStringCodec{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ == binaryType {
|
||||
return &binaryAsStringCodec{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type binaryAsStringCodec struct {
|
||||
}
|
||||
|
||||
func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
rawBytes := iter.ReadStringAsSlice()
|
||||
bytes := make([]byte, 0, len(rawBytes))
|
||||
for i := 0; i < len(rawBytes); i++ {
|
||||
b := rawBytes[i]
|
||||
if b == '\\' {
|
||||
b2 := rawBytes[i+1]
|
||||
if b2 != '\\' {
|
||||
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
||||
return
|
||||
}
|
||||
b3 := rawBytes[i+2]
|
||||
if b3 != 'x' {
|
||||
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
||||
return
|
||||
}
|
||||
b4 := rawBytes[i+3]
|
||||
b5 := rawBytes[i+4]
|
||||
i += 4
|
||||
b = readHex(iter, b4, b5)
|
||||
}
|
||||
bytes = append(bytes, b)
|
||||
}
|
||||
*(*[]byte)(ptr) = bytes
|
||||
}
|
||||
func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*[]byte)(ptr))) == 0
|
||||
}
|
||||
func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
|
||||
stream.SetBuffer(newBuffer)
|
||||
}
|
||||
|
||||
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
|
||||
var ret byte
|
||||
if b1 >= '0' && b1 <= '9' {
|
||||
ret = b1 - '0'
|
||||
} else if b1 >= 'a' && b1 <= 'f' {
|
||||
ret = b1 - 'a' + 10
|
||||
} else {
|
||||
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
|
||||
return 0
|
||||
}
|
||||
ret *= 16
|
||||
if b2 >= '0' && b2 <= '9' {
|
||||
ret += b2 - '0'
|
||||
} else if b2 >= 'a' && b2 <= 'f' {
|
||||
ret += b2 - 'a' + 10
|
||||
} else {
|
||||
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
|
||||
return 0
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var hex = "0123456789abcdef"
|
||||
|
||||
func writeBytes(space []byte, s []byte) []byte {
|
||||
space = append(space, '"')
|
||||
// write string, the fast path, without utf8 and escape support
|
||||
var i int
|
||||
var c byte
|
||||
for i, c = range s {
|
||||
if c < utf8.RuneSelf && safeSet[c] {
|
||||
space = append(space, c)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(s)-1 {
|
||||
space = append(space, '"')
|
||||
return space
|
||||
}
|
||||
return writeBytesSlowPath(space, s[i:])
|
||||
}
|
||||
|
||||
func writeBytesSlowPath(space []byte, s []byte) []byte {
|
||||
start := 0
|
||||
// for the remaining parts, we process them char by char
|
||||
var i int
|
||||
var b byte
|
||||
for i, b = range s {
|
||||
if b >= utf8.RuneSelf {
|
||||
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
||||
start = i + 1
|
||||
continue
|
||||
}
|
||||
if safeSet[b] {
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
space = append(space, s[start:i]...)
|
||||
}
|
||||
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
||||
start = i + 1
|
||||
}
|
||||
if start < len(s) {
|
||||
space = append(space, s[start:]...)
|
||||
}
|
||||
return append(space, '"')
|
||||
}
|
32
extra/binary_as_string_codec_test.go
Normal file
32
extra/binary_as_string_codec_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jsoniter.RegisterExtension(&BinaryAsStringExtension{})
|
||||
}
|
||||
|
||||
func TestBinaryAsStringCodec(t *testing.T) {
|
||||
t.Run("safe set", func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := jsoniter.Marshal([]byte("hello"))
|
||||
should.NoError(err)
|
||||
should.Equal(`"hello"`, string(output))
|
||||
var val []byte
|
||||
should.NoError(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal(`hello`, string(val))
|
||||
})
|
||||
t.Run("non safe set", func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := jsoniter.Marshal([]byte{1, 2, 3, 23})
|
||||
should.NoError(err)
|
||||
should.Equal(`"\\x01\\x02\\x03\\x17"`, string(output))
|
||||
var val []byte
|
||||
should.NoError(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal([]byte{1, 2, 3, 23}, val)
|
||||
})
|
||||
}
|
@ -2,11 +2,14 @@ package extra
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
const maxUint = ^uint(0)
|
||||
@ -146,7 +149,7 @@ type tolerateEmptyArrayExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
|
||||
func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
|
||||
if typ.Kind() == reflect.Struct || typ.Kind() == reflect.Map {
|
||||
return &tolerateEmptyArrayDecoder{decoder}
|
||||
}
|
||||
@ -158,7 +161,7 @@ type tolerateEmptyArrayDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if iter.WhatIsNext() == jsoniter.Array {
|
||||
if iter.WhatIsNext() == jsoniter.ArrayValue {
|
||||
iter.Skip()
|
||||
newIter := iter.Pool().BorrowIterator([]byte("{}"))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
@ -174,12 +177,15 @@ type fuzzyStringDecoder struct {
|
||||
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
switch valueType {
|
||||
case jsoniter.Number:
|
||||
case jsoniter.NumberValue:
|
||||
var number json.Number
|
||||
iter.ReadVal(&number)
|
||||
*((*string)(ptr)) = string(number)
|
||||
case jsoniter.String:
|
||||
case jsoniter.StringValue:
|
||||
*((*string)(ptr)) = iter.ReadString()
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*string)(ptr)) = ""
|
||||
default:
|
||||
iter.ReportError("fuzzyStringDecoder", "not number or string")
|
||||
}
|
||||
@ -193,20 +199,32 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.Number:
|
||||
case jsoniter.NumberValue:
|
||||
var number json.Number
|
||||
iter.ReadVal(&number)
|
||||
str = string(number)
|
||||
case jsoniter.String:
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
case jsoniter.BoolValue:
|
||||
if iter.ReadBool() {
|
||||
str = "1"
|
||||
} else {
|
||||
str = "0"
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
str = "0"
|
||||
default:
|
||||
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
||||
}
|
||||
if len(str) == 0 {
|
||||
str = "0"
|
||||
}
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
isFloat := strings.IndexByte(str, '.') != -1
|
||||
decoder.fun(isFloat, ptr, newIter)
|
||||
if newIter.Error != nil {
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
}
|
||||
@ -218,16 +236,26 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.Number:
|
||||
case jsoniter.NumberValue:
|
||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
||||
case jsoniter.String:
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
*((*float32)(ptr)) = newIter.ReadFloat32()
|
||||
if newIter.Error != nil {
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
case jsoniter.BoolValue:
|
||||
// support bool to float32
|
||||
if iter.ReadBool() {
|
||||
*((*float32)(ptr)) = 1
|
||||
} else {
|
||||
*((*float32)(ptr)) = 0
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*float32)(ptr)) = 0
|
||||
default:
|
||||
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
|
||||
}
|
||||
@ -240,17 +268,27 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.Number:
|
||||
case jsoniter.NumberValue:
|
||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||
case jsoniter.String:
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
*((*float64)(ptr)) = newIter.ReadFloat64()
|
||||
if newIter.Error != nil {
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
case jsoniter.BoolValue:
|
||||
// support bool to float64
|
||||
if iter.ReadBool() {
|
||||
*((*float64)(ptr)) = 1
|
||||
} else {
|
||||
*((*float64)(ptr)) = 0
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*float64)(ptr)) = 0
|
||||
default:
|
||||
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
|
||||
iter.ReportError("fuzzyFloat64Decoder", "not number or string")
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,14 @@ func Test_any_to_int64(t *testing.T) {
|
||||
should.Equal(int64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`""`, &val))
|
||||
should.Equal(int64(0), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int64(1), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(int64(-10), val)
|
||||
@ -57,6 +65,13 @@ func Test_any_to_int(t *testing.T) {
|
||||
should.Equal(10, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(10, val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(0, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(1, val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -74,6 +89,13 @@ func Test_any_to_int16(t *testing.T) {
|
||||
should.Equal(int16(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int16(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int16(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int16(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -91,6 +113,13 @@ func Test_any_to_int32(t *testing.T) {
|
||||
should.Equal(int32(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int32(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -108,6 +137,13 @@ func Test_any_to_int8(t *testing.T) {
|
||||
should.Equal(int8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int8(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int8(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int8(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -125,6 +161,13 @@ func Test_any_to_uint8(t *testing.T) {
|
||||
should.Equal(uint8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint8(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint8(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint8(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -144,6 +187,12 @@ func Test_any_to_uint64(t *testing.T) {
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint64(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint64(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint64(0), val)
|
||||
@ -165,6 +214,12 @@ func Test_any_to_uint32(t *testing.T) {
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint32(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint32(0), val)
|
||||
@ -186,6 +241,12 @@ func Test_any_to_uint16(t *testing.T) {
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint16(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint16(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint16(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint16(0), val)
|
||||
@ -205,6 +266,12 @@ func Test_any_to_uint(t *testing.T) {
|
||||
should.Equal(uint(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint(10), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
@ -223,6 +290,13 @@ func Test_any_to_float32(t *testing.T) {
|
||||
should.Equal(float32(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(float32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(float32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(float32(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
}
|
||||
@ -240,6 +314,13 @@ func Test_any_to_float64(t *testing.T) {
|
||||
should.Equal(float64(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(float64(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(float64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(float64(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
}
|
||||
@ -257,3 +338,56 @@ func Test_empty_array_as_object(t *testing.T) {
|
||||
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
|
||||
should.Equal(struct{}{}, val)
|
||||
}
|
||||
|
||||
func Test_bad_case(t *testing.T) {
|
||||
var jsonstr = `
|
||||
{
|
||||
"extra_type": 181760,
|
||||
"combo_type": 0,
|
||||
"trigger_time_ms": 1498800398000,
|
||||
"_create_time": "2017-06-16 11:21:39",
|
||||
"_msg_type": 41000
|
||||
}
|
||||
`
|
||||
|
||||
type OrderEventRequestParams struct {
|
||||
ExtraType uint64 `json:"extra_type"`
|
||||
}
|
||||
|
||||
var a OrderEventRequestParams
|
||||
err := jsoniter.UnmarshalFromString(jsonstr, &a)
|
||||
should := require.New(t)
|
||||
should.Nil(err)
|
||||
}
|
||||
|
||||
func Test_null_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message string
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message int
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_float32(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message float32
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_float64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message float64
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@ -17,8 +18,21 @@ type namingStrategyExtension struct {
|
||||
|
||||
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
binding.ToNames = []string{extension.translate(binding.Field.Name)}
|
||||
binding.FromNames = []string{extension.translate(binding.Field.Name)}
|
||||
if unicode.IsLower(rune(binding.Field.Name()[0])) || binding.Field.Name()[0] == '_'{
|
||||
continue
|
||||
}
|
||||
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||
if hastag {
|
||||
tagParts := strings.Split(tag, ",")
|
||||
if tagParts[0] == "-" {
|
||||
continue // hidden field
|
||||
}
|
||||
if tagParts[0] != "" {
|
||||
continue // field explicitly named
|
||||
}
|
||||
}
|
||||
binding.ToNames = []string{extension.translate(binding.Field.Name())}
|
||||
binding.FromNames = []string{extension.translate(binding.Field.Name())}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,3 +21,46 @@ func Test_lower_case_with_underscores(t *testing.T) {
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_overrides(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string `json:"UserName"`
|
||||
FirstLanguage string
|
||||
}{
|
||||
UserName: "taowen",
|
||||
FirstLanguage: "Chinese",
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"UserName":"taowen","first_language":"Chinese"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_omitempty(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string
|
||||
FirstLanguage string `json:",omitempty"`
|
||||
}{
|
||||
UserName: "taowen",
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"taowen"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_private_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string
|
||||
userId int
|
||||
_UserAge int
|
||||
}{
|
||||
UserName: "allen",
|
||||
userId: 100,
|
||||
_UserAge: 30,
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"allen"}`, string(output))
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@ -16,10 +17,38 @@ type privateFieldsExtension struct {
|
||||
|
||||
func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
isPrivate := unicode.IsLower(rune(binding.Field.Name[0]))
|
||||
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
|
||||
if isPrivate {
|
||||
binding.FromNames = []string{binding.Field.Name}
|
||||
binding.ToNames = []string{binding.Field.Name}
|
||||
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||
if !hastag {
|
||||
binding.FromNames = []string{binding.Field.Name()}
|
||||
binding.ToNames = []string{binding.Field.Name()}
|
||||
continue
|
||||
}
|
||||
tagParts := strings.Split(tag, ",")
|
||||
names := calcFieldNames(binding.Field.Name(), tagParts[0], tag)
|
||||
binding.FromNames = names
|
||||
binding.ToNames = names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
|
||||
// ignore?
|
||||
if wholeTag == "-" {
|
||||
return []string{}
|
||||
}
|
||||
// rename?
|
||||
var fieldNames []string
|
||||
if tagProvidedFieldName == "" {
|
||||
fieldNames = []string{originalFieldName}
|
||||
} else {
|
||||
fieldNames = []string{tagProvidedFieldName}
|
||||
}
|
||||
// private?
|
||||
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
|
||||
if isNotExported {
|
||||
fieldNames = []string{}
|
||||
}
|
||||
return fieldNames
|
||||
}
|
||||
|
@ -29,6 +29,3 @@ func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Strea
|
||||
ts := *((*time.Time)(ptr))
|
||||
stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds())
|
||||
}
|
||||
func (codec *timeAsInt64Codec) EncodeInterface(val interface{}, stream *jsoniter.Stream) {
|
||||
jsoniter.WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
@ -1,304 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Config customize how the API should behave.
|
||||
// The API is created from Config by Froze.
|
||||
type Config struct {
|
||||
IndentionStep int
|
||||
MarshalFloatWith6Digits bool
|
||||
EscapeHTML bool
|
||||
SortMapKeys bool
|
||||
UseNumber bool
|
||||
}
|
||||
|
||||
type frozenConfig struct {
|
||||
configBeforeFrozen Config
|
||||
sortMapKeys bool
|
||||
indentionStep int
|
||||
decoderCache unsafe.Pointer
|
||||
encoderCache unsafe.Pointer
|
||||
extensions []Extension
|
||||
streamPool chan *Stream
|
||||
iteratorPool chan *Iterator
|
||||
}
|
||||
|
||||
// API the public interface of this package.
|
||||
// Primary Marshal and Unmarshal.
|
||||
type API interface {
|
||||
IteratorPool
|
||||
StreamPool
|
||||
MarshalToString(v interface{}) (string, error)
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||
UnmarshalFromString(str string, v interface{}) error
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
Get(data []byte, path ...interface{}) Any
|
||||
NewEncoder(writer io.Writer) *Encoder
|
||||
NewDecoder(reader io.Reader) *Decoder
|
||||
}
|
||||
|
||||
// ConfigDefault the default API
|
||||
var ConfigDefault = Config{
|
||||
EscapeHTML: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
||||
var ConfigCompatibleWithStandardLibrary = Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigFastest marshals float with only 6 digits precision
|
||||
var ConfigFastest = Config{
|
||||
EscapeHTML: false,
|
||||
MarshalFloatWith6Digits: true,
|
||||
}.Froze()
|
||||
|
||||
// Froze forge API from config
|
||||
func (cfg Config) Froze() API {
|
||||
// TODO: cache frozen config
|
||||
frozenConfig := &frozenConfig{
|
||||
sortMapKeys: cfg.SortMapKeys,
|
||||
indentionStep: cfg.IndentionStep,
|
||||
streamPool: make(chan *Stream, 16),
|
||||
iteratorPool: make(chan *Iterator, 16),
|
||||
}
|
||||
atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
|
||||
atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
|
||||
if cfg.MarshalFloatWith6Digits {
|
||||
frozenConfig.marshalFloatWith6Digits()
|
||||
}
|
||||
if cfg.EscapeHTML {
|
||||
frozenConfig.escapeHTML()
|
||||
}
|
||||
if cfg.UseNumber {
|
||||
frozenConfig.useNumber()
|
||||
}
|
||||
frozenConfig.configBeforeFrozen = cfg
|
||||
return frozenConfig
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) useNumber() {
|
||||
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if iter.WhatIsNext() == Number {
|
||||
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
|
||||
} else {
|
||||
*((*interface{})(ptr)) = iter.Read()
|
||||
}
|
||||
}})
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) registerExtension(extension Extension) {
|
||||
cfg.extensions = append(cfg.extensions, extension)
|
||||
}
|
||||
|
||||
type lossyFloat32Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat32Lossy(*((*float32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type lossyFloat64Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat64Lossy(*((*float64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float64)(ptr)) == 0
|
||||
}
|
||||
|
||||
// EnableLossyFloatMarshalling keeps 10**(-6) precision
|
||||
// for float variables for better performance.
|
||||
func (cfg *frozenConfig) marshalFloatWith6Digits() {
|
||||
// for better performance
|
||||
cfg.addEncoderToCache(reflect.TypeOf((*float32)(nil)).Elem(), &lossyFloat32Encoder{})
|
||||
cfg.addEncoderToCache(reflect.TypeOf((*float64)(nil)).Elem(), &lossyFloat64Encoder{})
|
||||
}
|
||||
|
||||
type htmlEscapedStringEncoder struct {
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
stream.WriteStringWithHTMLEscaped(str)
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*string)(ptr)) == ""
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) escapeHTML() {
|
||||
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
|
||||
done := false
|
||||
for !done {
|
||||
ptr := atomic.LoadPointer(&cfg.decoderCache)
|
||||
cache := *(*map[reflect.Type]ValDecoder)(ptr)
|
||||
copied := map[reflect.Type]ValDecoder{}
|
||||
for k, v := range cache {
|
||||
copied[k] = v
|
||||
}
|
||||
copied[cacheKey] = decoder
|
||||
done = atomic.CompareAndSwapPointer(&cfg.decoderCache, ptr, unsafe.Pointer(&copied))
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
|
||||
done := false
|
||||
for !done {
|
||||
ptr := atomic.LoadPointer(&cfg.encoderCache)
|
||||
cache := *(*map[reflect.Type]ValEncoder)(ptr)
|
||||
copied := map[reflect.Type]ValEncoder{}
|
||||
for k, v := range cache {
|
||||
copied[k] = v
|
||||
}
|
||||
copied[cacheKey] = encoder
|
||||
done = atomic.CompareAndSwapPointer(&cfg.encoderCache, ptr, unsafe.Pointer(&copied))
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
|
||||
ptr := atomic.LoadPointer(&cfg.decoderCache)
|
||||
cache := *(*map[reflect.Type]ValDecoder)(ptr)
|
||||
return cache[cacheKey]
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
|
||||
ptr := atomic.LoadPointer(&cfg.encoderCache)
|
||||
cache := *(*map[reflect.Type]ValEncoder)(ptr)
|
||||
return cache[cacheKey]
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanDecoders() {
|
||||
typeDecoders = map[string]ValDecoder{}
|
||||
fieldDecoders = map[string]ValDecoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanEncoders() {
|
||||
typeEncoders = map[string]ValEncoder{}
|
||||
fieldEncoders = map[string]ValEncoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return "", stream.Error
|
||||
}
|
||||
return string(stream.Buffer()), nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return nil, stream.Error
|
||||
}
|
||||
result := stream.Buffer()
|
||||
copied := make([]byte, len(result))
|
||||
copy(copied, result)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
if prefix != "" {
|
||||
panic("prefix is not supported")
|
||||
}
|
||||
for _, r := range indent {
|
||||
if r != ' ' {
|
||||
panic("indent can only be space")
|
||||
}
|
||||
}
|
||||
newCfg := cfg.configBeforeFrozen
|
||||
newCfg.IndentionStep = len(indent)
|
||||
return newCfg.Froze().Marshal(v)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
||||
data := []byte(str)
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(v)
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.ReportError("UnmarshalFromString", "there are bytes left after unmarshal")
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
return locatePath(iter, path)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
typ := reflect.TypeOf(v)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
// return non-pointer error
|
||||
return errors.New("the second param must be ptr type")
|
||||
}
|
||||
iter.ReadVal(v)
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
|
||||
stream := NewStream(cfg, writer, 512)
|
||||
return &Encoder{stream}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
|
||||
iter := Parse(cfg, reader, 512)
|
||||
return &Decoder{iter}
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ReadObject read one field from object.
|
||||
// If object ended, returns empty string.
|
||||
// Otherwise, returns the field name.
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return "" // null
|
||||
case '{':
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
return string(iter.readObjectFieldAsBytes())
|
||||
}
|
||||
if c == '}' {
|
||||
return "" // end of object
|
||||
}
|
||||
iter.ReportError("ReadObject", `expect " after {`)
|
||||
return
|
||||
case ',':
|
||||
return string(iter.readObjectFieldAsBytes())
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readFieldHash() int32 {
|
||||
hash := int64(0x811c9dc5)
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
b := iter.buf[i]
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
if b == '"' {
|
||||
iter.head = i + 1
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||
}
|
||||
return int32(hash)
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.ReportError("readFieldHash", `incomplete field name`)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
|
||||
func calcHash(str string) int32 {
|
||||
hash := int64(0x811c9dc5)
|
||||
for _, b := range str {
|
||||
hash ^= int64(unicode.ToLower(b))
|
||||
hash *= 0x1000193
|
||||
}
|
||||
return int32(hash)
|
||||
}
|
||||
|
||||
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.readObjectFieldAsBytes()
|
||||
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.readObjectFieldAsBytes()
|
||||
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if c == '}' {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect " after }`)
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect { or n`)
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadMapCB read map with callback, the key can be any string
|
||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field")
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field")
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadMapCB", `object not ended with }`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if c == '}' {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect " after }`)
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect { or n`)
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectStart() bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '}' {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return true
|
||||
} else if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return false
|
||||
}
|
||||
iter.ReportError("readObjectStart", "expect { or n")
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
|
||||
str := iter.ReadStringAsSlice()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.ReportError("readObjectFieldAsBytes", "expect : after object field")
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == nil {
|
||||
return str
|
||||
}
|
||||
return ret
|
||||
}
|
@ -1,679 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ValDecoder is an internal type registered to cache as needed.
|
||||
// Don't confuse jsoniter.ValDecoder with json.Decoder.
|
||||
// For json.Decoder's adapter, refer to jsoniter.AdapterDecoder(todo link).
|
||||
//
|
||||
// Reflection on type to create decoders, which is then cached
|
||||
// Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions
|
||||
// 1. create instance of new value, for example *int will need a int to be allocated
|
||||
// 2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New
|
||||
// 3. assignment to map, both key and value will be reflect.Value
|
||||
// For a simple struct binding, it will be reflect.Value free and allocation free
|
||||
type ValDecoder interface {
|
||||
Decode(ptr unsafe.Pointer, iter *Iterator)
|
||||
}
|
||||
|
||||
// ValEncoder is an internal type registered to cache as needed.
|
||||
// Don't confuse jsoniter.ValEncoder with json.Encoder.
|
||||
// For json.Encoder's adapter, refer to jsoniter.AdapterEncoder(todo godoc link).
|
||||
type ValEncoder interface {
|
||||
IsEmpty(ptr unsafe.Pointer) bool
|
||||
Encode(ptr unsafe.Pointer, stream *Stream)
|
||||
EncodeInterface(val interface{}, stream *Stream)
|
||||
}
|
||||
|
||||
type checkIsEmpty interface {
|
||||
IsEmpty(ptr unsafe.Pointer) bool
|
||||
}
|
||||
|
||||
// WriteToStream the default implementation for TypeEncoder method EncodeInterface
|
||||
func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
|
||||
e := (*emptyInterface)(unsafe.Pointer(&val))
|
||||
if e.word == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
if reflect.TypeOf(val).Kind() == reflect.Ptr {
|
||||
encoder.Encode(unsafe.Pointer(&e.word), stream)
|
||||
} else {
|
||||
encoder.Encode(e.word, stream)
|
||||
}
|
||||
}
|
||||
|
||||
var jsonNumberType reflect.Type
|
||||
var jsonRawMessageType reflect.Type
|
||||
var jsoniterRawMessageType reflect.Type
|
||||
var anyType reflect.Type
|
||||
var marshalerType reflect.Type
|
||||
var unmarshalerType reflect.Type
|
||||
var textMarshalerType reflect.Type
|
||||
var textUnmarshalerType reflect.Type
|
||||
|
||||
func init() {
|
||||
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
|
||||
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
|
||||
jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem()
|
||||
anyType = reflect.TypeOf((*Any)(nil)).Elem()
|
||||
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
}
|
||||
|
||||
type optionalDecoder struct {
|
||||
valueType reflect.Type
|
||||
valueDecoder ValDecoder
|
||||
}
|
||||
|
||||
func (decoder *optionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if iter.ReadNil() {
|
||||
*((*unsafe.Pointer)(ptr)) = nil
|
||||
} else {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
//pointer to null, we have to allocate memory to hold the value
|
||||
value := reflect.New(decoder.valueType)
|
||||
newPtr := extractInterface(value.Interface()).word
|
||||
decoder.valueDecoder.Decode(newPtr, iter)
|
||||
*((*uintptr)(ptr)) = uintptr(newPtr)
|
||||
} else {
|
||||
//reuse existing instance
|
||||
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type deferenceDecoder struct {
|
||||
// only to deference a pointer
|
||||
valueType reflect.Type
|
||||
valueDecoder ValDecoder
|
||||
}
|
||||
|
||||
func (decoder *deferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
//pointer to null, we have to allocate memory to hold the value
|
||||
value := reflect.New(decoder.valueType)
|
||||
newPtr := extractInterface(value.Interface()).word
|
||||
decoder.valueDecoder.Decode(newPtr, iter)
|
||||
*((*uintptr)(ptr)) = uintptr(newPtr)
|
||||
} else {
|
||||
//reuse existing instance
|
||||
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||
}
|
||||
}
|
||||
|
||||
type optionalEncoder struct {
|
||||
valueEncoder ValEncoder
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
stream.WriteNil()
|
||||
} else {
|
||||
encoder.valueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type placeholderEncoder struct {
|
||||
cfg *frozenConfig
|
||||
cacheKey reflect.Type
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
encoder.getRealEncoder().Encode(ptr, stream)
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.getRealEncoder().IsEmpty(ptr)
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) getRealEncoder() ValEncoder {
|
||||
for i := 0; i < 30; i++ {
|
||||
realDecoder := encoder.cfg.getEncoderFromCache(encoder.cacheKey)
|
||||
_, isPlaceholder := realDecoder.(*placeholderEncoder)
|
||||
if isPlaceholder {
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
return realDecoder
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("real encoder not found for cache key: %v", encoder.cacheKey))
|
||||
}
|
||||
|
||||
type placeholderDecoder struct {
|
||||
cfg *frozenConfig
|
||||
cacheKey reflect.Type
|
||||
}
|
||||
|
||||
func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
for i := 0; i < 30; i++ {
|
||||
realDecoder := decoder.cfg.getDecoderFromCache(decoder.cacheKey)
|
||||
_, isPlaceholder := realDecoder.(*placeholderDecoder)
|
||||
if isPlaceholder {
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
realDecoder.Decode(ptr, iter)
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("real decoder not found for cache key: %v", decoder.cacheKey))
|
||||
}
|
||||
|
||||
// emptyInterface is the header for an interface{} value.
|
||||
type emptyInterface struct {
|
||||
typ unsafe.Pointer
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
// emptyInterface is the header for an interface with method (not interface{})
|
||||
type nonEmptyInterface struct {
|
||||
// see ../runtime/iface.go:/Itab
|
||||
itab *struct {
|
||||
ityp unsafe.Pointer // static interface type
|
||||
typ unsafe.Pointer // dynamic concrete type
|
||||
link unsafe.Pointer
|
||||
bad int32
|
||||
unused int32
|
||||
fun [100000]unsafe.Pointer // method table
|
||||
}
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
||||
typ := reflect.TypeOf(obj)
|
||||
cacheKey := typ.Elem()
|
||||
decoder, err := decoderOfType(iter.cfg, cacheKey)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&obj))
|
||||
decoder.Decode(e.word, iter)
|
||||
}
|
||||
|
||||
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
||||
func (stream *Stream) WriteVal(val interface{}) {
|
||||
if nil == val {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
typ := reflect.TypeOf(val)
|
||||
cacheKey := typ
|
||||
encoder, err := encoderOfType(stream.cfg, cacheKey)
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
return
|
||||
}
|
||||
encoder.EncodeInterface(val, stream)
|
||||
}
|
||||
|
||||
type prefix string
|
||||
|
||||
func (p prefix) addToDecoder(decoder ValDecoder, err error) (ValDecoder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", p, err.Error())
|
||||
}
|
||||
return decoder, err
|
||||
}
|
||||
|
||||
func (p prefix) addToEncoder(encoder ValEncoder, err error) (ValEncoder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", p, err.Error())
|
||||
}
|
||||
return encoder, err
|
||||
}
|
||||
|
||||
func decoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
cacheKey := typ
|
||||
decoder := cfg.getDecoderFromCache(cacheKey)
|
||||
if decoder != nil {
|
||||
return decoder, nil
|
||||
}
|
||||
decoder = getTypeDecoderFromExtension(typ)
|
||||
if decoder != nil {
|
||||
cfg.addDecoderToCache(cacheKey, decoder)
|
||||
return decoder, nil
|
||||
}
|
||||
decoder = &placeholderDecoder{cfg: cfg, cacheKey: cacheKey}
|
||||
cfg.addDecoderToCache(cacheKey, decoder)
|
||||
decoder, err := createDecoderOfType(cfg, typ)
|
||||
for _, extension := range extensions {
|
||||
decoder = extension.DecorateDecoder(typ, decoder)
|
||||
}
|
||||
cfg.addDecoderToCache(cacheKey, decoder)
|
||||
return decoder, err
|
||||
}
|
||||
|
||||
func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
typeName := typ.String()
|
||||
if typ == jsonRawMessageType {
|
||||
return &jsonRawMessageCodec{}, nil
|
||||
}
|
||||
if typ == jsoniterRawMessageType {
|
||||
return &jsoniterRawMessageCodec{}, nil
|
||||
}
|
||||
if typ.AssignableTo(jsonNumberType) {
|
||||
return &jsonNumberCodec{}, nil
|
||||
}
|
||||
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
|
||||
return &base64Codec{}, nil
|
||||
}
|
||||
if typ.Implements(unmarshalerType) {
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
decoder = &optionalDecoder{typ.Elem(), decoder}
|
||||
}
|
||||
return decoder, nil
|
||||
}
|
||||
if reflect.PtrTo(typ).Implements(unmarshalerType) {
|
||||
templateInterface := reflect.New(typ).Interface()
|
||||
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
|
||||
return decoder, nil
|
||||
}
|
||||
if typ.Implements(textUnmarshalerType) {
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
decoder = &optionalDecoder{typ.Elem(), decoder}
|
||||
}
|
||||
return decoder, nil
|
||||
}
|
||||
if reflect.PtrTo(typ).Implements(textUnmarshalerType) {
|
||||
templateInterface := reflect.New(typ).Interface()
|
||||
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
|
||||
return decoder, nil
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{}, nil
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
if typeName != "string" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*string)(nil)).Elem())
|
||||
}
|
||||
return &stringCodec{}, nil
|
||||
case reflect.Int:
|
||||
if typeName != "int" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*int)(nil)).Elem())
|
||||
}
|
||||
return &intCodec{}, nil
|
||||
case reflect.Int8:
|
||||
if typeName != "int8" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*int8)(nil)).Elem())
|
||||
}
|
||||
return &int8Codec{}, nil
|
||||
case reflect.Int16:
|
||||
if typeName != "int16" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*int16)(nil)).Elem())
|
||||
}
|
||||
return &int16Codec{}, nil
|
||||
case reflect.Int32:
|
||||
if typeName != "int32" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*int32)(nil)).Elem())
|
||||
}
|
||||
return &int32Codec{}, nil
|
||||
case reflect.Int64:
|
||||
if typeName != "int64" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*int64)(nil)).Elem())
|
||||
}
|
||||
return &int64Codec{}, nil
|
||||
case reflect.Uint:
|
||||
if typeName != "uint" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uint)(nil)).Elem())
|
||||
}
|
||||
return &uintCodec{}, nil
|
||||
case reflect.Uint8:
|
||||
if typeName != "uint8" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uint8)(nil)).Elem())
|
||||
}
|
||||
return &uint8Codec{}, nil
|
||||
case reflect.Uint16:
|
||||
if typeName != "uint16" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uint16)(nil)).Elem())
|
||||
}
|
||||
return &uint16Codec{}, nil
|
||||
case reflect.Uint32:
|
||||
if typeName != "uint32" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uint32)(nil)).Elem())
|
||||
}
|
||||
return &uint32Codec{}, nil
|
||||
case reflect.Uintptr:
|
||||
if typeName != "uintptr" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uintptr)(nil)).Elem())
|
||||
}
|
||||
return &uintptrCodec{}, nil
|
||||
case reflect.Uint64:
|
||||
if typeName != "uint64" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*uint64)(nil)).Elem())
|
||||
}
|
||||
return &uint64Codec{}, nil
|
||||
case reflect.Float32:
|
||||
if typeName != "float32" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*float32)(nil)).Elem())
|
||||
}
|
||||
return &float32Codec{}, nil
|
||||
case reflect.Float64:
|
||||
if typeName != "float64" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*float64)(nil)).Elem())
|
||||
}
|
||||
return &float64Codec{}, nil
|
||||
case reflect.Bool:
|
||||
if typeName != "bool" {
|
||||
return decoderOfType(cfg, reflect.TypeOf((*bool)(nil)).Elem())
|
||||
}
|
||||
return &boolCodec{}, nil
|
||||
case reflect.Interface:
|
||||
if typ.NumMethod() == 0 {
|
||||
return &emptyInterfaceCodec{}, nil
|
||||
}
|
||||
return &nonEmptyInterfaceCodec{}, nil
|
||||
case reflect.Struct:
|
||||
return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(cfg, typ))
|
||||
case reflect.Array:
|
||||
return prefix("[array]").addToDecoder(decoderOfArray(cfg, typ))
|
||||
case reflect.Slice:
|
||||
return prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ))
|
||||
case reflect.Map:
|
||||
return prefix("[map]").addToDecoder(decoderOfMap(cfg, typ))
|
||||
case reflect.Ptr:
|
||||
return prefix("[optional]").addToDecoder(decoderOfOptional(cfg, typ))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func encoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
cacheKey := typ
|
||||
encoder := cfg.getEncoderFromCache(cacheKey)
|
||||
if encoder != nil {
|
||||
return encoder, nil
|
||||
}
|
||||
encoder = getTypeEncoderFromExtension(typ)
|
||||
if encoder != nil {
|
||||
cfg.addEncoderToCache(cacheKey, encoder)
|
||||
return encoder, nil
|
||||
}
|
||||
encoder = &placeholderEncoder{cfg: cfg, cacheKey: cacheKey}
|
||||
cfg.addEncoderToCache(cacheKey, encoder)
|
||||
encoder, err := createEncoderOfType(cfg, typ)
|
||||
for _, extension := range extensions {
|
||||
encoder = extension.DecorateEncoder(typ, encoder)
|
||||
}
|
||||
cfg.addEncoderToCache(cacheKey, encoder)
|
||||
return encoder, err
|
||||
}
|
||||
|
||||
func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
if typ == jsonRawMessageType {
|
||||
return &jsonRawMessageCodec{}, nil
|
||||
}
|
||||
if typ == jsoniterRawMessageType {
|
||||
return &jsoniterRawMessageCodec{}, nil
|
||||
}
|
||||
if typ.AssignableTo(jsonNumberType) {
|
||||
return &jsonNumberCodec{}, nil
|
||||
}
|
||||
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
|
||||
return &base64Codec{typ}, nil
|
||||
}
|
||||
if typ.Implements(marshalerType) {
|
||||
checkIsEmpty, err := createCheckIsEmpty(typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
var encoder ValEncoder = &marshalerEncoder{
|
||||
templateInterface: extractInterface(templateInterface),
|
||||
checkIsEmpty: checkIsEmpty,
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
encoder = &optionalEncoder{encoder}
|
||||
}
|
||||
return encoder, nil
|
||||
}
|
||||
if typ.Implements(textMarshalerType) {
|
||||
checkIsEmpty, err := createCheckIsEmpty(typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
var encoder ValEncoder = &textMarshalerEncoder{
|
||||
templateInterface: extractInterface(templateInterface),
|
||||
checkIsEmpty: checkIsEmpty,
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
encoder = &optionalEncoder{encoder}
|
||||
}
|
||||
return encoder, nil
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{}, nil
|
||||
}
|
||||
return createEncoderOfSimpleType(cfg, typ)
|
||||
}
|
||||
|
||||
func createCheckIsEmpty(typ reflect.Type) (checkIsEmpty, error) {
|
||||
kind := typ.Kind()
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return &stringCodec{}, nil
|
||||
case reflect.Int:
|
||||
return &intCodec{}, nil
|
||||
case reflect.Int8:
|
||||
return &int8Codec{}, nil
|
||||
case reflect.Int16:
|
||||
return &int16Codec{}, nil
|
||||
case reflect.Int32:
|
||||
return &int32Codec{}, nil
|
||||
case reflect.Int64:
|
||||
return &int64Codec{}, nil
|
||||
case reflect.Uint:
|
||||
return &uintCodec{}, nil
|
||||
case reflect.Uint8:
|
||||
return &uint8Codec{}, nil
|
||||
case reflect.Uint16:
|
||||
return &uint16Codec{}, nil
|
||||
case reflect.Uint32:
|
||||
return &uint32Codec{}, nil
|
||||
case reflect.Uintptr:
|
||||
return &uintptrCodec{}, nil
|
||||
case reflect.Uint64:
|
||||
return &uint64Codec{}, nil
|
||||
case reflect.Float32:
|
||||
return &float32Codec{}, nil
|
||||
case reflect.Float64:
|
||||
return &float64Codec{}, nil
|
||||
case reflect.Bool:
|
||||
return &boolCodec{}, nil
|
||||
case reflect.Interface:
|
||||
if typ.NumMethod() == 0 {
|
||||
return &emptyInterfaceCodec{}, nil
|
||||
}
|
||||
return &nonEmptyInterfaceCodec{}, nil
|
||||
case reflect.Struct:
|
||||
return &structEncoder{}, nil
|
||||
case reflect.Array:
|
||||
return &arrayEncoder{}, nil
|
||||
case reflect.Slice:
|
||||
return &sliceEncoder{}, nil
|
||||
case reflect.Map:
|
||||
return &mapEncoder{}, nil
|
||||
case reflect.Ptr:
|
||||
return &optionalEncoder{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func createEncoderOfSimpleType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
typeName := typ.String()
|
||||
kind := typ.Kind()
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
if typeName != "string" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*string)(nil)).Elem())
|
||||
}
|
||||
return &stringCodec{}, nil
|
||||
case reflect.Int:
|
||||
if typeName != "int" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*int)(nil)).Elem())
|
||||
}
|
||||
return &intCodec{}, nil
|
||||
case reflect.Int8:
|
||||
if typeName != "int8" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*int8)(nil)).Elem())
|
||||
}
|
||||
return &int8Codec{}, nil
|
||||
case reflect.Int16:
|
||||
if typeName != "int16" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*int16)(nil)).Elem())
|
||||
}
|
||||
return &int16Codec{}, nil
|
||||
case reflect.Int32:
|
||||
if typeName != "int32" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*int32)(nil)).Elem())
|
||||
}
|
||||
return &int32Codec{}, nil
|
||||
case reflect.Int64:
|
||||
if typeName != "int64" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*int64)(nil)).Elem())
|
||||
}
|
||||
return &int64Codec{}, nil
|
||||
case reflect.Uint:
|
||||
if typeName != "uint" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uint)(nil)).Elem())
|
||||
}
|
||||
return &uintCodec{}, nil
|
||||
case reflect.Uint8:
|
||||
if typeName != "uint8" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uint8)(nil)).Elem())
|
||||
}
|
||||
return &uint8Codec{}, nil
|
||||
case reflect.Uint16:
|
||||
if typeName != "uint16" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uint16)(nil)).Elem())
|
||||
}
|
||||
return &uint16Codec{}, nil
|
||||
case reflect.Uint32:
|
||||
if typeName != "uint32" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uint32)(nil)).Elem())
|
||||
}
|
||||
return &uint32Codec{}, nil
|
||||
case reflect.Uintptr:
|
||||
if typeName != "uintptr" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uintptr)(nil)).Elem())
|
||||
}
|
||||
return &uintptrCodec{}, nil
|
||||
case reflect.Uint64:
|
||||
if typeName != "uint64" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*uint64)(nil)).Elem())
|
||||
}
|
||||
return &uint64Codec{}, nil
|
||||
case reflect.Float32:
|
||||
if typeName != "float32" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*float32)(nil)).Elem())
|
||||
}
|
||||
return &float32Codec{}, nil
|
||||
case reflect.Float64:
|
||||
if typeName != "float64" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*float64)(nil)).Elem())
|
||||
}
|
||||
return &float64Codec{}, nil
|
||||
case reflect.Bool:
|
||||
if typeName != "bool" {
|
||||
return encoderOfType(cfg, reflect.TypeOf((*bool)(nil)).Elem())
|
||||
}
|
||||
return &boolCodec{}, nil
|
||||
case reflect.Interface:
|
||||
if typ.NumMethod() == 0 {
|
||||
return &emptyInterfaceCodec{}, nil
|
||||
}
|
||||
return &nonEmptyInterfaceCodec{}, nil
|
||||
case reflect.Struct:
|
||||
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(cfg, typ))
|
||||
case reflect.Array:
|
||||
return prefix("[array]").addToEncoder(encoderOfArray(cfg, typ))
|
||||
case reflect.Slice:
|
||||
return prefix("[slice]").addToEncoder(encoderOfSlice(cfg, typ))
|
||||
case reflect.Map:
|
||||
return prefix("[map]").addToEncoder(encoderOfMap(cfg, typ))
|
||||
case reflect.Ptr:
|
||||
return prefix("[optional]").addToEncoder(encoderOfOptional(cfg, typ))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func decoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
elemType := typ.Elem()
|
||||
decoder, err := decoderOfType(cfg, elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &optionalDecoder{elemType, decoder}, nil
|
||||
}
|
||||
|
||||
func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
elemType := typ.Elem()
|
||||
elemEncoder, err := encoderOfType(cfg, elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder := &optionalEncoder{elemEncoder}
|
||||
if elemType.Kind() == reflect.Map {
|
||||
encoder = &optionalEncoder{encoder}
|
||||
}
|
||||
return encoder, nil
|
||||
}
|
||||
|
||||
func decoderOfMap(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
decoder, err := decoderOfType(cfg, typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapInterface := reflect.New(typ).Interface()
|
||||
return &mapDecoder{typ, typ.Key(), typ.Elem(), decoder, extractInterface(mapInterface)}, nil
|
||||
}
|
||||
|
||||
func extractInterface(val interface{}) emptyInterface {
|
||||
return *((*emptyInterface)(unsafe.Pointer(&val)))
|
||||
}
|
||||
|
||||
func encoderOfMap(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
elemType := typ.Elem()
|
||||
encoder, err := encoderOfType(cfg, elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapInterface := reflect.New(typ).Elem().Interface()
|
||||
if cfg.sortMapKeys {
|
||||
return &sortKeysMapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||
}
|
||||
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func decoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
decoder, err := decoderOfType(cfg, typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &arrayDecoder{typ, typ.Elem(), decoder}, nil
|
||||
}
|
||||
|
||||
func encoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
encoder, err := encoderOfType(cfg, typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ.Elem().Kind() == reflect.Map {
|
||||
encoder = &optionalEncoder{encoder}
|
||||
}
|
||||
return &arrayEncoder{typ, typ.Elem(), encoder}, nil
|
||||
}
|
||||
|
||||
type arrayEncoder struct {
|
||||
arrayType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder ValEncoder
|
||||
}
|
||||
|
||||
func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteArrayStart()
|
||||
elemPtr := unsafe.Pointer(ptr)
|
||||
encoder.elemEncoder.Encode(elemPtr, stream)
|
||||
for i := 1; i < encoder.arrayType.Len(); i++ {
|
||||
stream.WriteMore()
|
||||
elemPtr = unsafe.Pointer(uintptr(elemPtr) + encoder.elemType.Size())
|
||||
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
if stream.Error != nil && stream.Error != io.EOF {
|
||||
stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *arrayEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
// special optimization for interface{}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&val))
|
||||
if e.word == nil {
|
||||
stream.WriteArrayStart()
|
||||
stream.WriteNil()
|
||||
stream.WriteArrayEnd()
|
||||
return
|
||||
}
|
||||
elemType := encoder.arrayType.Elem()
|
||||
if encoder.arrayType.Len() == 1 && (elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map) {
|
||||
ptr := uintptr(e.word)
|
||||
e.word = unsafe.Pointer(&ptr)
|
||||
}
|
||||
if reflect.TypeOf(val).Kind() == reflect.Ptr {
|
||||
encoder.Encode(unsafe.Pointer(&e.word), stream)
|
||||
} else {
|
||||
encoder.Encode(e.word, stream)
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type arrayDecoder struct {
|
||||
arrayType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemDecoder ValDecoder
|
||||
}
|
||||
|
||||
func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.doDecode(ptr, iter)
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
offset := uintptr(0)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
if offset < decoder.arrayType.Size() {
|
||||
decoder.elemDecoder.Decode(unsafe.Pointer(uintptr(ptr)+offset), iter)
|
||||
offset += decoder.elemType.Size()
|
||||
} else {
|
||||
iter.Skip()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type mapDecoder struct {
|
||||
mapType reflect.Type
|
||||
keyType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemDecoder ValDecoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
|
||||
mapInterface := decoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface).Elem()
|
||||
if iter.ReadNil() {
|
||||
realVal.Set(reflect.Zero(decoder.mapType))
|
||||
return
|
||||
}
|
||||
if realVal.IsNil() {
|
||||
realVal.Set(reflect.MakeMap(realVal.Type()))
|
||||
}
|
||||
iter.ReadMapCB(func(iter *Iterator, keyStr string) bool {
|
||||
elem := reflect.New(decoder.elemType)
|
||||
decoder.elemDecoder.Decode(unsafe.Pointer(elem.Pointer()), iter)
|
||||
// to put into map, we have to use reflection
|
||||
keyType := decoder.keyType
|
||||
// TODO: remove this from loop
|
||||
switch {
|
||||
case keyType.Kind() == reflect.String:
|
||||
realVal.SetMapIndex(reflect.ValueOf(keyStr).Convert(keyType), elem.Elem())
|
||||
return true
|
||||
case keyType.Implements(textUnmarshalerType):
|
||||
textUnmarshaler := reflect.New(keyType.Elem()).Interface().(encoding.TextUnmarshaler)
|
||||
err := textUnmarshaler.UnmarshalText([]byte(keyStr))
|
||||
if err != nil {
|
||||
iter.ReportError("read map key as TextUnmarshaler", err.Error())
|
||||
return false
|
||||
}
|
||||
realVal.SetMapIndex(reflect.ValueOf(textUnmarshaler), elem.Elem())
|
||||
return true
|
||||
case reflect.PtrTo(keyType).Implements(textUnmarshalerType):
|
||||
textUnmarshaler := reflect.New(keyType).Interface().(encoding.TextUnmarshaler)
|
||||
err := textUnmarshaler.UnmarshalText([]byte(keyStr))
|
||||
if err != nil {
|
||||
iter.ReportError("read map key as TextUnmarshaler", err.Error())
|
||||
return false
|
||||
}
|
||||
realVal.SetMapIndex(reflect.ValueOf(textUnmarshaler).Elem(), elem.Elem())
|
||||
return true
|
||||
default:
|
||||
switch keyType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, err := strconv.ParseInt(keyStr, 10, 64)
|
||||
if err != nil || reflect.Zero(keyType).OverflowInt(n) {
|
||||
iter.ReportError("read map key as int64", "read int64 failed")
|
||||
return false
|
||||
}
|
||||
realVal.SetMapIndex(reflect.ValueOf(n).Convert(keyType), elem.Elem())
|
||||
return true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n, err := strconv.ParseUint(keyStr, 10, 64)
|
||||
if err != nil || reflect.Zero(keyType).OverflowUint(n) {
|
||||
iter.ReportError("read map key as uint64", "read uint64 failed")
|
||||
return false
|
||||
}
|
||||
realVal.SetMapIndex(reflect.ValueOf(n).Convert(keyType), elem.Elem())
|
||||
return true
|
||||
}
|
||||
}
|
||||
iter.ReportError("read map key", "unexpected map key type "+keyType.String())
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
type mapEncoder struct {
|
||||
mapType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder ValEncoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
stream.WriteObjectStart()
|
||||
for i, key := range realVal.MapKeys() {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
encodeMapKey(key, stream)
|
||||
if stream.indention > 0 {
|
||||
stream.writeTwoBytes(byte(':'), byte(' '))
|
||||
} else {
|
||||
stream.writeByte(':')
|
||||
}
|
||||
val := realVal.MapIndex(key).Interface()
|
||||
encoder.elemEncoder.EncodeInterface(val, stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
func encodeMapKey(key reflect.Value, stream *Stream) {
|
||||
if key.Kind() == reflect.String {
|
||||
stream.WriteString(key.String())
|
||||
return
|
||||
}
|
||||
if tm, ok := key.Interface().(encoding.TextMarshaler); ok {
|
||||
buf, err := tm.MarshalText()
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
return
|
||||
}
|
||||
stream.writeByte('"')
|
||||
stream.Write(buf)
|
||||
stream.writeByte('"')
|
||||
return
|
||||
}
|
||||
switch key.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
stream.writeByte('"')
|
||||
stream.WriteInt64(key.Int())
|
||||
stream.writeByte('"')
|
||||
return
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
stream.writeByte('"')
|
||||
stream.WriteUint64(key.Uint())
|
||||
stream.writeByte('"')
|
||||
return
|
||||
}
|
||||
stream.Error = &json.UnsupportedTypeError{Type: key.Type()}
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
return realVal.Len() == 0
|
||||
}
|
||||
|
||||
type sortKeysMapEncoder struct {
|
||||
mapType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder ValEncoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
|
||||
// Extract and sort the keys.
|
||||
keys := realVal.MapKeys()
|
||||
sv := stringValues(make([]reflectWithString, len(keys)))
|
||||
for i, v := range keys {
|
||||
sv[i].v = v
|
||||
if err := sv[i].resolve(); err != nil {
|
||||
stream.Error = err
|
||||
return
|
||||
}
|
||||
}
|
||||
sort.Sort(sv)
|
||||
|
||||
stream.WriteObjectStart()
|
||||
for i, key := range sv {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteVal(key.s) // might need html escape, so can not WriteString directly
|
||||
if stream.indention > 0 {
|
||||
stream.writeTwoBytes(byte(':'), byte(' '))
|
||||
} else {
|
||||
stream.writeByte(':')
|
||||
}
|
||||
val := realVal.MapIndex(key.v).Interface()
|
||||
encoder.elemEncoder.EncodeInterface(val, stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||
// It implements the methods to sort by string.
|
||||
type stringValues []reflectWithString
|
||||
|
||||
type reflectWithString struct {
|
||||
v reflect.Value
|
||||
s string
|
||||
}
|
||||
|
||||
func (w *reflectWithString) resolve() error {
|
||||
if w.v.Kind() == reflect.String {
|
||||
w.s = w.v.String()
|
||||
return nil
|
||||
}
|
||||
if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
|
||||
buf, err := tm.MarshalText()
|
||||
w.s = string(buf)
|
||||
return err
|
||||
}
|
||||
switch w.v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
w.s = strconv.FormatInt(w.v.Int(), 10)
|
||||
return nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
w.s = strconv.FormatUint(w.v.Uint(), 10)
|
||||
return nil
|
||||
}
|
||||
return &json.UnsupportedTypeError{Type: w.v.Type()}
|
||||
}
|
||||
|
||||
func (sv stringValues) Len() int { return len(sv) }
|
||||
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||
func (sv stringValues) Less(i, j int) bool { return sv[i].s < sv[j].s }
|
||||
|
||||
func (encoder *sortKeysMapEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *sortKeysMapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
return realVal.Len() == 0
|
||||
}
|
@ -1,647 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type stringCodec struct {
|
||||
}
|
||||
|
||||
func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = iter.ReadString()
|
||||
}
|
||||
|
||||
func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
stream.WriteString(str)
|
||||
}
|
||||
|
||||
func (codec *stringCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *stringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*string)(ptr)) == ""
|
||||
}
|
||||
|
||||
type intCodec struct {
|
||||
}
|
||||
|
||||
func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int)(ptr)) = iter.ReadInt()
|
||||
}
|
||||
|
||||
func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt(*((*int)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *intCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *intCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uintptrCodec struct {
|
||||
}
|
||||
|
||||
func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
|
||||
}
|
||||
|
||||
func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint64(uint64(*((*uintptr)(ptr))))
|
||||
}
|
||||
|
||||
func (codec *uintptrCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uintptrCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uintptr)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int8Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int8)(ptr)) = iter.ReadInt8()
|
||||
}
|
||||
|
||||
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt8(*((*int8)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *int8Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *int8Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int8)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int16Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int16)(ptr)) = iter.ReadInt16()
|
||||
}
|
||||
|
||||
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt16(*((*int16)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *int16Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *int16Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int16)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int32)(ptr)) = iter.ReadInt32()
|
||||
}
|
||||
|
||||
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt32(*((*int32)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *int32Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *int32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int64)(ptr)) = iter.ReadInt64()
|
||||
}
|
||||
|
||||
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt64(*((*int64)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *int64Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *int64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uintCodec struct {
|
||||
}
|
||||
|
||||
func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint)(ptr)) = iter.ReadUint()
|
||||
}
|
||||
|
||||
func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint(*((*uint)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *uintCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uintCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint8Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint8)(ptr)) = iter.ReadUint8()
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint8(*((*uint8)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint8)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint16Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint16)(ptr)) = iter.ReadUint16()
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint16(*((*uint16)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint16)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint32)(ptr)) = iter.ReadUint32()
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint32(*((*uint32)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint64)(ptr)) = iter.ReadUint64()
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint64(*((*uint64)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type float32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
||||
}
|
||||
|
||||
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat32(*((*float32)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *float32Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *float32Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type float64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||
}
|
||||
|
||||
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat64(*((*float64)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *float64Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *float64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type boolCodec struct {
|
||||
}
|
||||
|
||||
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*bool)(ptr)) = iter.ReadBool()
|
||||
}
|
||||
|
||||
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteBool(*((*bool)(ptr)))
|
||||
}
|
||||
|
||||
func (codec *boolCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, codec)
|
||||
}
|
||||
|
||||
func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return !(*((*bool)(ptr)))
|
||||
}
|
||||
|
||||
type emptyInterfaceCodec struct {
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*interface{})(ptr)) = iter.Read()
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteVal(*((*interface{})(ptr)))
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return ptr == nil
|
||||
}
|
||||
|
||||
type nonEmptyInterfaceCodec struct {
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
if nonEmptyInterface.itab == nil {
|
||||
iter.ReportError("read non-empty interface", "do not know which concrete type to decode to")
|
||||
return
|
||||
}
|
||||
var i interface{}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||
e.typ = nonEmptyInterface.itab.typ
|
||||
e.word = nonEmptyInterface.word
|
||||
iter.ReadVal(&i)
|
||||
nonEmptyInterface.word = e.word
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
var i interface{}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||
e.typ = nonEmptyInterface.itab.typ
|
||||
e.word = nonEmptyInterface.word
|
||||
stream.WriteVal(i)
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
return nonEmptyInterface.word == nil
|
||||
}
|
||||
|
||||
type anyCodec struct {
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*Any)(ptr)) = iter.ReadAny()
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
(*((*Any)(ptr))).WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *anyCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
(val.(Any)).WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return (*((*Any)(ptr))).Size() == 0
|
||||
}
|
||||
|
||||
type jsonNumberCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteRaw(string(*((*json.Number)(ptr))))
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteRaw(string(val.(json.Number)))
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.Number)(ptr))) == 0
|
||||
}
|
||||
|
||||
type jsonRawMessageCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes())
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteRaw(string(val.(json.RawMessage)))
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.RawMessage)(ptr))) == 0
|
||||
}
|
||||
|
||||
type jsoniterRawMessageCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*RawMessage)(ptr)) = RawMessage(iter.SkipAndReturnBytes())
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteRaw(string(*((*RawMessage)(ptr))))
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteRaw(string(val.(RawMessage)))
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*RawMessage)(ptr))) == 0
|
||||
}
|
||||
|
||||
type base64Codec struct {
|
||||
actualType reflect.Type
|
||||
}
|
||||
|
||||
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if iter.ReadNil() {
|
||||
ptrSlice := (*sliceHeader)(ptr)
|
||||
ptrSlice.Len = 0
|
||||
ptrSlice.Cap = 0
|
||||
ptrSlice.Data = nil
|
||||
return
|
||||
}
|
||||
encoding := base64.StdEncoding
|
||||
src := iter.SkipAndReturnBytes()
|
||||
src = src[1 : len(src)-1]
|
||||
decodedLen := encoding.DecodedLen(len(src))
|
||||
dst := make([]byte, decodedLen)
|
||||
len, err := encoding.Decode(dst, src)
|
||||
if err != nil {
|
||||
iter.ReportError("decode base64", err.Error())
|
||||
} else {
|
||||
dst = dst[:len]
|
||||
dstSlice := (*sliceHeader)(unsafe.Pointer(&dst))
|
||||
ptrSlice := (*sliceHeader)(ptr)
|
||||
ptrSlice.Data = dstSlice.Data
|
||||
ptrSlice.Cap = dstSlice.Cap
|
||||
ptrSlice.Len = dstSlice.Len
|
||||
}
|
||||
}
|
||||
|
||||
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
src := *((*[]byte)(ptr))
|
||||
if len(src) == 0 {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
encoding := base64.StdEncoding
|
||||
stream.writeByte('"')
|
||||
toGrow := encoding.EncodedLen(len(src))
|
||||
stream.ensure(toGrow)
|
||||
encoding.Encode(stream.buf[stream.n:], src)
|
||||
stream.n += toGrow
|
||||
stream.writeByte('"')
|
||||
}
|
||||
|
||||
func (codec *base64Codec) EncodeInterface(val interface{}, stream *Stream) {
|
||||
ptr := extractInterface(val).word
|
||||
src := *((*[]byte)(ptr))
|
||||
if len(src) == 0 {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
encoding := base64.StdEncoding
|
||||
stream.writeByte('"')
|
||||
toGrow := encoding.EncodedLen(len(src))
|
||||
stream.ensure(toGrow)
|
||||
encoding.Encode(stream.buf[stream.n:], src)
|
||||
stream.n += toGrow
|
||||
stream.writeByte('"')
|
||||
}
|
||||
|
||||
func (codec *base64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*[]byte)(ptr))) == 0
|
||||
}
|
||||
|
||||
type stringModeNumberDecoder struct {
|
||||
elemDecoder ValDecoder
|
||||
}
|
||||
|
||||
func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.ReportError("stringModeNumberDecoder", `expect "`)
|
||||
return
|
||||
}
|
||||
decoder.elemDecoder.Decode(ptr, iter)
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
c = iter.readByte()
|
||||
if c != '"' {
|
||||
iter.ReportError("stringModeNumberDecoder", `expect "`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type stringModeStringDecoder struct {
|
||||
elemDecoder ValDecoder
|
||||
cfg *frozenConfig
|
||||
}
|
||||
|
||||
func (decoder *stringModeStringDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.elemDecoder.Decode(ptr, iter)
|
||||
str := *((*string)(ptr))
|
||||
tempIter := decoder.cfg.BorrowIterator([]byte(str))
|
||||
defer decoder.cfg.ReturnIterator(tempIter)
|
||||
*((*string)(ptr)) = tempIter.ReadString()
|
||||
}
|
||||
|
||||
type stringModeNumberEncoder struct {
|
||||
elemEncoder ValEncoder
|
||||
}
|
||||
|
||||
func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.writeByte('"')
|
||||
encoder.elemEncoder.Encode(ptr, stream)
|
||||
stream.writeByte('"')
|
||||
}
|
||||
|
||||
func (encoder *stringModeNumberEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.elemEncoder.IsEmpty(ptr)
|
||||
}
|
||||
|
||||
type stringModeStringEncoder struct {
|
||||
elemEncoder ValEncoder
|
||||
cfg *frozenConfig
|
||||
}
|
||||
|
||||
func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
tempStream := encoder.cfg.BorrowStream(nil)
|
||||
defer encoder.cfg.ReturnStream(tempStream)
|
||||
encoder.elemEncoder.Encode(ptr, tempStream)
|
||||
stream.WriteString(string(tempStream.Buffer()))
|
||||
}
|
||||
|
||||
func (encoder *stringModeStringEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.elemEncoder.IsEmpty(ptr)
|
||||
}
|
||||
|
||||
type marshalerEncoder struct {
|
||||
templateInterface emptyInterface
|
||||
checkIsEmpty checkIsEmpty
|
||||
}
|
||||
|
||||
func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
templateInterface := encoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
marshaler := (*realInterface).(json.Marshaler)
|
||||
bytes, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
} else {
|
||||
stream.Write(bytes)
|
||||
}
|
||||
}
|
||||
func (encoder *marshalerEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
||||
}
|
||||
|
||||
type textMarshalerEncoder struct {
|
||||
templateInterface emptyInterface
|
||||
checkIsEmpty checkIsEmpty
|
||||
}
|
||||
|
||||
func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
templateInterface := encoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
marshaler := (*realInterface).(encoding.TextMarshaler)
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
} else {
|
||||
stream.WriteString(string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *textMarshalerEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.checkIsEmpty.IsEmpty(ptr)
|
||||
}
|
||||
|
||||
type unmarshalerDecoder struct {
|
||||
templateInterface emptyInterface
|
||||
}
|
||||
|
||||
func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
templateInterface := decoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
unmarshaler := (*realInterface).(json.Unmarshaler)
|
||||
iter.nextToken()
|
||||
iter.unreadByte() // skip spaces
|
||||
bytes := iter.SkipAndReturnBytes()
|
||||
err := unmarshaler.UnmarshalJSON(bytes)
|
||||
if err != nil {
|
||||
iter.ReportError("unmarshalerDecoder", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type textUnmarshalerDecoder struct {
|
||||
templateInterface emptyInterface
|
||||
}
|
||||
|
||||
func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
templateInterface := decoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
unmarshaler := (*realInterface).(encoding.TextUnmarshaler)
|
||||
str := iter.ReadString()
|
||||
err := unmarshaler.UnmarshalText([]byte(str))
|
||||
if err != nil {
|
||||
iter.ReportError("textUnmarshalerDecoder", err.Error())
|
||||
}
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
type bindingTo struct {
|
||||
binding *Binding
|
||||
toName string
|
||||
ignored bool
|
||||
}
|
||||
orderedBindings := []*bindingTo{}
|
||||
structDescriptor, err := describeStruct(cfg, typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
for _, toName := range binding.ToNames {
|
||||
new := &bindingTo{
|
||||
binding: binding,
|
||||
toName: toName,
|
||||
}
|
||||
for _, old := range orderedBindings {
|
||||
if old.toName != toName {
|
||||
continue
|
||||
}
|
||||
old.ignored, new.ignored = resolveConflictBinding(old.binding, new.binding)
|
||||
}
|
||||
orderedBindings = append(orderedBindings, new)
|
||||
}
|
||||
}
|
||||
if len(orderedBindings) == 0 {
|
||||
return &emptyStructEncoder{}, nil
|
||||
}
|
||||
finalOrderedFields := []structFieldTo{}
|
||||
for _, bindingTo := range orderedBindings {
|
||||
if !bindingTo.ignored {
|
||||
finalOrderedFields = append(finalOrderedFields, structFieldTo{
|
||||
encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
|
||||
toName: bindingTo.toName,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil
|
||||
}
|
||||
|
||||
func resolveConflictBinding(old, new *Binding) (ignoreOld, ignoreNew bool) {
|
||||
newTagged := new.Field.Tag.Get("json") != ""
|
||||
oldTagged := old.Field.Tag.Get("json") != ""
|
||||
if newTagged {
|
||||
if oldTagged {
|
||||
if len(old.levels) > len(new.levels) {
|
||||
return true, false
|
||||
} else if len(new.levels) > len(old.levels) {
|
||||
return false, true
|
||||
} else {
|
||||
return true, true
|
||||
}
|
||||
} else {
|
||||
return true, false
|
||||
}
|
||||
} else {
|
||||
if oldTagged {
|
||||
return true, false
|
||||
}
|
||||
if len(old.levels) > len(new.levels) {
|
||||
return true, false
|
||||
} else if len(new.levels) > len(old.levels) {
|
||||
return false, true
|
||||
} else {
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
bindings := map[string]*Binding{}
|
||||
structDescriptor, err := describeStruct(cfg, typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
for _, fromName := range binding.FromNames {
|
||||
old := bindings[fromName]
|
||||
if old == nil {
|
||||
bindings[fromName] = binding
|
||||
continue
|
||||
}
|
||||
ignoreOld, ignoreNew := resolveConflictBinding(old, binding)
|
||||
if ignoreOld {
|
||||
delete(bindings, fromName)
|
||||
}
|
||||
if !ignoreNew {
|
||||
bindings[fromName] = binding
|
||||
}
|
||||
}
|
||||
}
|
||||
fields := map[string]*structFieldDecoder{}
|
||||
for k, binding := range bindings {
|
||||
fields[k] = binding.Decoder.(*structFieldDecoder)
|
||||
}
|
||||
return createStructDecoder(typ, fields)
|
||||
}
|
||||
|
||||
type structFieldEncoder struct {
|
||||
field *reflect.StructField
|
||||
fieldEncoder ValEncoder
|
||||
omitempty bool
|
||||
}
|
||||
|
||||
func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
|
||||
encoder.fieldEncoder.Encode(fieldPtr, stream)
|
||||
if stream.Error != nil && stream.Error != io.EOF {
|
||||
stream.Error = fmt.Errorf("%s: %s", encoder.field.Name, stream.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *structFieldEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
|
||||
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
||||
}
|
||||
|
||||
type structEncoder struct {
|
||||
onePtrEmbedded bool
|
||||
onePtrOptimization bool
|
||||
fields []structFieldTo
|
||||
}
|
||||
|
||||
type structFieldTo struct {
|
||||
encoder *structFieldEncoder
|
||||
toName string
|
||||
}
|
||||
|
||||
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteObjectStart()
|
||||
isNotFirst := false
|
||||
for _, field := range encoder.fields {
|
||||
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
||||
continue
|
||||
}
|
||||
if isNotFirst {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(field.toName)
|
||||
field.encoder.Encode(ptr, stream)
|
||||
isNotFirst = true
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
e := (*emptyInterface)(unsafe.Pointer(&val))
|
||||
if encoder.onePtrOptimization {
|
||||
if e.word == nil && encoder.onePtrEmbedded {
|
||||
stream.WriteObjectStart()
|
||||
stream.WriteObjectEnd()
|
||||
return
|
||||
}
|
||||
ptr := uintptr(e.word)
|
||||
e.word = unsafe.Pointer(&ptr)
|
||||
}
|
||||
if reflect.TypeOf(val).Kind() == reflect.Ptr {
|
||||
encoder.Encode(unsafe.Pointer(&e.word), stream)
|
||||
} else {
|
||||
encoder.Encode(e.word, stream)
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type emptyStructEncoder struct {
|
||||
}
|
||||
|
||||
func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteEmptyObject()
|
||||
}
|
||||
|
||||
func (encoder *emptyStructEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
|
||||
decoder, err := decoderOfType(cfg, typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sliceDecoder{typ, typ.Elem(), decoder}, nil
|
||||
}
|
||||
|
||||
func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
||||
encoder, err := encoderOfType(cfg, typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ.Elem().Kind() == reflect.Map {
|
||||
encoder = &optionalEncoder{encoder}
|
||||
}
|
||||
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
|
||||
}
|
||||
|
||||
type sliceEncoder struct {
|
||||
sliceType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder ValEncoder
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
if slice.Data == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
if slice.Len == 0 {
|
||||
stream.WriteEmptyArray()
|
||||
return
|
||||
}
|
||||
stream.WriteArrayStart()
|
||||
elemPtr := unsafe.Pointer(slice.Data)
|
||||
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
|
||||
for i := 1; i < slice.Len; i++ {
|
||||
stream.WriteMore()
|
||||
elemPtr = unsafe.Pointer(uintptr(elemPtr) + encoder.elemType.Size())
|
||||
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
if stream.Error != nil && stream.Error != io.EOF {
|
||||
stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) EncodeInterface(val interface{}, stream *Stream) {
|
||||
WriteToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
return slice.Len == 0
|
||||
}
|
||||
|
||||
type sliceDecoder struct {
|
||||
sliceType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemDecoder ValDecoder
|
||||
}
|
||||
|
||||
// sliceHeader is a safe version of SliceHeader used within this package.
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.doDecode(ptr, iter)
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
if iter.ReadNil() {
|
||||
slice.Len = 0
|
||||
slice.Cap = 0
|
||||
slice.Data = nil
|
||||
return
|
||||
}
|
||||
reuseSlice(slice, decoder.sliceType, 4)
|
||||
slice.Len = 0
|
||||
offset := uintptr(0)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
growOne(slice, decoder.sliceType, decoder.elemType)
|
||||
decoder.elemDecoder.Decode(unsafe.Pointer(uintptr(slice.Data)+offset), iter)
|
||||
offset += decoder.elemType.Size()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// grow grows the slice s so that it can hold extra more values, allocating
|
||||
// more capacity if needed. It also returns the old and new slice lengths.
|
||||
func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Type) {
|
||||
newLen := slice.Len + 1
|
||||
if newLen <= slice.Cap {
|
||||
slice.Len = newLen
|
||||
return
|
||||
}
|
||||
newCap := slice.Cap
|
||||
if newCap == 0 {
|
||||
newCap = 1
|
||||
} else {
|
||||
for newCap < newLen {
|
||||
if slice.Len < 1024 {
|
||||
newCap += newCap
|
||||
} else {
|
||||
newCap += newCap / 4
|
||||
}
|
||||
}
|
||||
}
|
||||
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer())
|
||||
// copy old array into new array
|
||||
originalBytesCount := uintptr(slice.Len) * elementType.Size()
|
||||
srcPtr := (*[1 << 30]byte)(slice.Data)
|
||||
dstPtr := (*[1 << 30]byte)(dst)
|
||||
for i := uintptr(0); i < originalBytesCount; i++ {
|
||||
dstPtr[i] = srcPtr[i]
|
||||
}
|
||||
slice.Len = newLen
|
||||
slice.Cap = newCap
|
||||
slice.Data = dst
|
||||
}
|
||||
|
||||
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
|
||||
if expectedCap <= slice.Cap {
|
||||
return
|
||||
}
|
||||
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer())
|
||||
slice.Cap = expectedCap
|
||||
slice.Data = dst
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
var digits []uint32
|
||||
|
||||
func init() {
|
||||
digits = make([]uint32, 1000)
|
||||
for i := uint32(0); i < 1000; i++ {
|
||||
digits[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i%10 + '0'
|
||||
if i < 10 {
|
||||
digits[i] += 2 << 24
|
||||
} else if i < 100 {
|
||||
digits[i] += 1 << 24
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFirstBuf(buf []byte, v uint32, n int) int {
|
||||
start := v >> 24
|
||||
if start == 0 {
|
||||
buf[n] = byte(v >> 16)
|
||||
n++
|
||||
buf[n] = byte(v >> 8)
|
||||
n++
|
||||
} else if start == 1 {
|
||||
buf[n] = byte(v >> 8)
|
||||
n++
|
||||
}
|
||||
buf[n] = byte(v)
|
||||
n++
|
||||
return n
|
||||
}
|
||||
|
||||
func writeBuf(buf []byte, v uint32, n int) {
|
||||
buf[n] = byte(v >> 16)
|
||||
buf[n+1] = byte(v >> 8)
|
||||
buf[n+2] = byte(v)
|
||||
}
|
||||
|
||||
// WriteUint8 write uint8 to stream
|
||||
func (stream *Stream) WriteUint8(val uint8) {
|
||||
stream.ensure(3)
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], stream.n)
|
||||
}
|
||||
|
||||
// WriteInt8 write int8 to stream
|
||||
func (stream *Stream) WriteInt8(nval int8) {
|
||||
stream.ensure(4)
|
||||
n := stream.n
|
||||
var val uint8
|
||||
if nval < 0 {
|
||||
val = uint8(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint8(nval)
|
||||
}
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
}
|
||||
|
||||
// WriteUint16 write uint16 to stream
|
||||
func (stream *Stream) WriteUint16(val uint16) {
|
||||
stream.ensure(5)
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], stream.n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
n := writeFirstBuf(stream.buf, digits[q1], stream.n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
|
||||
// WriteInt16 write int16 to stream
|
||||
func (stream *Stream) WriteInt16(nval int16) {
|
||||
stream.ensure(6)
|
||||
n := stream.n
|
||||
var val uint16
|
||||
if nval < 0 {
|
||||
val = uint16(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint16(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
n = writeFirstBuf(stream.buf, digits[q1], n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
|
||||
// WriteUint32 write uint32 to stream
|
||||
func (stream *Stream) WriteUint32(val uint32) {
|
||||
stream.ensure(10)
|
||||
n := stream.n
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, digits[q1], n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2*1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q2], n)
|
||||
} else {
|
||||
r3 := q2 - q3*1000
|
||||
stream.buf[n] = byte(q3 + '0')
|
||||
n++
|
||||
writeBuf(stream.buf, digits[r3], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, digits[r2], n)
|
||||
writeBuf(stream.buf, digits[r1], n+3)
|
||||
stream.n = n + 6
|
||||
}
|
||||
|
||||
// WriteInt32 write int32 to stream
|
||||
func (stream *Stream) WriteInt32(nval int32) {
|
||||
stream.ensure(11)
|
||||
n := stream.n
|
||||
var val uint32
|
||||
if nval < 0 {
|
||||
val = uint32(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint32(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, digits[q1], n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2*1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q2], n)
|
||||
} else {
|
||||
r3 := q2 - q3*1000
|
||||
stream.buf[n] = byte(q3 + '0')
|
||||
n++
|
||||
writeBuf(stream.buf, digits[r3], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, digits[r2], n)
|
||||
writeBuf(stream.buf, digits[r1], n+3)
|
||||
stream.n = n + 6
|
||||
}
|
||||
|
||||
// WriteUint64 write uint64 to stream
|
||||
func (stream *Stream) WriteUint64(val uint64) {
|
||||
stream.ensure(20)
|
||||
n := stream.n
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, digits[q1], n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2*1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q2], n)
|
||||
writeBuf(stream.buf, digits[r2], n)
|
||||
writeBuf(stream.buf, digits[r1], n+3)
|
||||
stream.n = n + 6
|
||||
return
|
||||
}
|
||||
r3 := q2 - q3*1000
|
||||
q4 := q3 / 1000
|
||||
if q4 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q3], n)
|
||||
writeBuf(stream.buf, digits[r3], n)
|
||||
writeBuf(stream.buf, digits[r2], n+3)
|
||||
writeBuf(stream.buf, digits[r1], n+6)
|
||||
stream.n = n + 9
|
||||
return
|
||||
}
|
||||
r4 := q3 - q4*1000
|
||||
q5 := q4 / 1000
|
||||
if q5 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q4], n)
|
||||
writeBuf(stream.buf, digits[r4], n)
|
||||
writeBuf(stream.buf, digits[r3], n+3)
|
||||
writeBuf(stream.buf, digits[r2], n+6)
|
||||
writeBuf(stream.buf, digits[r1], n+9)
|
||||
stream.n = n + 12
|
||||
return
|
||||
}
|
||||
r5 := q4 - q5*1000
|
||||
q6 := q5 / 1000
|
||||
if q6 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q5], n)
|
||||
} else {
|
||||
n = writeFirstBuf(stream.buf, digits[q6], n)
|
||||
r6 := q5 - q6*1000
|
||||
writeBuf(stream.buf, digits[r6], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, digits[r5], n)
|
||||
writeBuf(stream.buf, digits[r4], n+3)
|
||||
writeBuf(stream.buf, digits[r3], n+6)
|
||||
writeBuf(stream.buf, digits[r2], n+9)
|
||||
writeBuf(stream.buf, digits[r1], n+12)
|
||||
stream.n = n + 15
|
||||
}
|
||||
|
||||
// WriteInt64 write int64 to stream
|
||||
func (stream *Stream) WriteInt64(nval int64) {
|
||||
stream.ensure(20)
|
||||
n := stream.n
|
||||
var val uint64
|
||||
if nval < 0 {
|
||||
val = uint64(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint64(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, digits[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1*1000
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, digits[q1], n)
|
||||
writeBuf(stream.buf, digits[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2*1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q2], n)
|
||||
writeBuf(stream.buf, digits[r2], n)
|
||||
writeBuf(stream.buf, digits[r1], n+3)
|
||||
stream.n = n + 6
|
||||
return
|
||||
}
|
||||
r3 := q2 - q3*1000
|
||||
q4 := q3 / 1000
|
||||
if q4 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q3], n)
|
||||
writeBuf(stream.buf, digits[r3], n)
|
||||
writeBuf(stream.buf, digits[r2], n+3)
|
||||
writeBuf(stream.buf, digits[r1], n+6)
|
||||
stream.n = n + 9
|
||||
return
|
||||
}
|
||||
r4 := q3 - q4*1000
|
||||
q5 := q4 / 1000
|
||||
if q5 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q4], n)
|
||||
writeBuf(stream.buf, digits[r4], n)
|
||||
writeBuf(stream.buf, digits[r3], n+3)
|
||||
writeBuf(stream.buf, digits[r2], n+6)
|
||||
writeBuf(stream.buf, digits[r1], n+9)
|
||||
stream.n = n + 12
|
||||
return
|
||||
}
|
||||
r5 := q4 - q5*1000
|
||||
q6 := q5 / 1000
|
||||
if q6 == 0 {
|
||||
n = writeFirstBuf(stream.buf, digits[q5], n)
|
||||
} else {
|
||||
stream.buf[n] = byte(q6 + '0')
|
||||
n++
|
||||
r6 := q5 - q6*1000
|
||||
writeBuf(stream.buf, digits[r6], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, digits[r5], n)
|
||||
writeBuf(stream.buf, digits[r4], n+3)
|
||||
writeBuf(stream.buf, digits[r3], n+6)
|
||||
writeBuf(stream.buf, digits[r2], n+9)
|
||||
writeBuf(stream.buf, digits[r1], n+12)
|
||||
stream.n = n + 15
|
||||
}
|
||||
|
||||
// WriteInt write int to stream
|
||||
func (stream *Stream) WriteInt(val int) {
|
||||
stream.WriteInt64(int64(val))
|
||||
}
|
||||
|
||||
// WriteUint write uint to stream
|
||||
func (stream *Stream) WriteUint(val uint) {
|
||||
stream.WriteUint64(uint64(val))
|
||||
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module github.com/json-iterator/go
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/google/gofuzz v1.0.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
|
||||
github.com/modern-go/reflect2 v1.0.2
|
||||
github.com/stretchr/testify v1.8.0
|
||||
)
|
21
go.sum
Normal file
21
go.sum
Normal file
@ -0,0 +1,21 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -10,20 +10,20 @@ import (
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
// Invalid invalid JSON element
|
||||
Invalid ValueType = iota
|
||||
// String JSON element "string"
|
||||
String
|
||||
// Number JSON element 100 or 0.10
|
||||
Number
|
||||
// Nil JSON element null
|
||||
Nil
|
||||
// Bool JSON element true or false
|
||||
Bool
|
||||
// Array JSON element []
|
||||
Array
|
||||
// Object JSON element {}
|
||||
Object
|
||||
// InvalidValue invalid JSON element
|
||||
InvalidValue ValueType = iota
|
||||
// StringValue JSON element "string"
|
||||
StringValue
|
||||
// NumberValue JSON element 100 or 0.10
|
||||
NumberValue
|
||||
// NilValue JSON element null
|
||||
NilValue
|
||||
// BoolValue JSON element true or false
|
||||
BoolValue
|
||||
// ArrayValue JSON element []
|
||||
ArrayValue
|
||||
// ObjectValue JSON element {}
|
||||
ObjectValue
|
||||
)
|
||||
|
||||
var hexDigits []byte
|
||||
@ -45,25 +45,25 @@ func init() {
|
||||
}
|
||||
valueTypes = make([]ValueType, 256)
|
||||
for i := 0; i < len(valueTypes); i++ {
|
||||
valueTypes[i] = Invalid
|
||||
valueTypes[i] = InvalidValue
|
||||
}
|
||||
valueTypes['"'] = String
|
||||
valueTypes['-'] = Number
|
||||
valueTypes['0'] = Number
|
||||
valueTypes['1'] = Number
|
||||
valueTypes['2'] = Number
|
||||
valueTypes['3'] = Number
|
||||
valueTypes['4'] = Number
|
||||
valueTypes['5'] = Number
|
||||
valueTypes['6'] = Number
|
||||
valueTypes['7'] = Number
|
||||
valueTypes['8'] = Number
|
||||
valueTypes['9'] = Number
|
||||
valueTypes['t'] = Bool
|
||||
valueTypes['f'] = Bool
|
||||
valueTypes['n'] = Nil
|
||||
valueTypes['['] = Array
|
||||
valueTypes['{'] = Object
|
||||
valueTypes['"'] = StringValue
|
||||
valueTypes['-'] = NumberValue
|
||||
valueTypes['0'] = NumberValue
|
||||
valueTypes['1'] = NumberValue
|
||||
valueTypes['2'] = NumberValue
|
||||
valueTypes['3'] = NumberValue
|
||||
valueTypes['4'] = NumberValue
|
||||
valueTypes['5'] = NumberValue
|
||||
valueTypes['6'] = NumberValue
|
||||
valueTypes['7'] = NumberValue
|
||||
valueTypes['8'] = NumberValue
|
||||
valueTypes['9'] = NumberValue
|
||||
valueTypes['t'] = BoolValue
|
||||
valueTypes['f'] = BoolValue
|
||||
valueTypes['n'] = NilValue
|
||||
valueTypes['['] = ArrayValue
|
||||
valueTypes['{'] = ObjectValue
|
||||
}
|
||||
|
||||
// Iterator is a io.Reader like object, with JSON specific read functions.
|
||||
@ -74,9 +74,11 @@ type Iterator struct {
|
||||
buf []byte
|
||||
head int
|
||||
tail int
|
||||
depth int
|
||||
captureStartedAt int
|
||||
captured []byte
|
||||
Error error
|
||||
Attachment interface{} // open for customized decoder
|
||||
}
|
||||
|
||||
// NewIterator creates an empty Iterator instance
|
||||
@ -87,6 +89,7 @@ func NewIterator(cfg API) *Iterator {
|
||||
buf: nil,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +101,7 @@ func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
|
||||
buf: make([]byte, bufSize),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +113,7 @@ func ParseBytes(cfg API, input []byte) *Iterator {
|
||||
buf: input,
|
||||
head: 0,
|
||||
tail: len(input),
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +132,7 @@ func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
||||
iter.reader = reader
|
||||
iter.head = 0
|
||||
iter.tail = 0
|
||||
iter.depth = 0
|
||||
return iter
|
||||
}
|
||||
|
||||
@ -136,6 +142,7 @@ func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||
iter.buf = input
|
||||
iter.head = 0
|
||||
iter.tail = len(input)
|
||||
iter.depth = 0
|
||||
return iter
|
||||
}
|
||||
|
||||
@ -167,7 +174,7 @@ func (iter *Iterator) isObjectEnd() bool {
|
||||
if c == '}' {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("isObjectEnd", "object ended prematurely")
|
||||
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
|
||||
return true
|
||||
}
|
||||
|
||||
@ -200,8 +207,22 @@ func (iter *Iterator) ReportError(operation string, msg string) {
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
iter.Error = fmt.Errorf("%s: %s, parsing %v ...%s... at %s", operation, msg, iter.head,
|
||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
||||
peekEnd := iter.head + 10
|
||||
if peekEnd > iter.tail {
|
||||
peekEnd = iter.tail
|
||||
}
|
||||
parsing := string(iter.buf[peekStart:peekEnd])
|
||||
contextStart := iter.head - 50
|
||||
if contextStart < 0 {
|
||||
contextStart = 0
|
||||
}
|
||||
contextEnd := iter.head + 50
|
||||
if contextEnd > iter.tail {
|
||||
contextEnd = iter.tail
|
||||
}
|
||||
context := string(iter.buf[contextStart:contextEnd])
|
||||
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...",
|
||||
operation, msg, iter.head-peekStart, parsing, context)
|
||||
}
|
||||
|
||||
// CurrentBuffer gets current buffer as string for debugging purpose
|
||||
@ -210,7 +231,7 @@ func (iter *Iterator) CurrentBuffer() string {
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
return fmt.Sprintf("parsing %v ...|%s|... at %s", iter.head,
|
||||
return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head,
|
||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
||||
}
|
||||
|
||||
@ -270,29 +291,33 @@ func (iter *Iterator) unreadByte() {
|
||||
func (iter *Iterator) Read() interface{} {
|
||||
valueType := iter.WhatIsNext()
|
||||
switch valueType {
|
||||
case String:
|
||||
case StringValue:
|
||||
return iter.ReadString()
|
||||
case Number:
|
||||
case NumberValue:
|
||||
if iter.cfg.configBeforeFrozen.UseNumber {
|
||||
return json.Number(iter.readNumberAsString())
|
||||
}
|
||||
return iter.ReadFloat64()
|
||||
case Nil:
|
||||
case NilValue:
|
||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
||||
return nil
|
||||
case Bool:
|
||||
case BoolValue:
|
||||
return iter.ReadBool()
|
||||
case Array:
|
||||
case ArrayValue:
|
||||
arr := []interface{}{}
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
arr = append(arr, iter.Read())
|
||||
var elem interface{}
|
||||
iter.ReadVal(&elem)
|
||||
arr = append(arr, elem)
|
||||
return true
|
||||
})
|
||||
return arr
|
||||
case Object:
|
||||
case ObjectValue:
|
||||
obj := map[string]interface{}{}
|
||||
iter.ReadMapCB(func(Iter *Iterator, field string) bool {
|
||||
obj[field] = iter.Read()
|
||||
var elem interface{}
|
||||
iter.ReadVal(&elem)
|
||||
obj[field] = elem
|
||||
return true
|
||||
})
|
||||
return obj
|
||||
@ -301,3 +326,24 @@ func (iter *Iterator) Read() interface{} {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
|
||||
const maxDepth = 10000
|
||||
|
||||
func (iter *Iterator) incrementDepth() (success bool) {
|
||||
iter.depth++
|
||||
if iter.depth <= maxDepth {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("incrementDepth", "exceeded max depth")
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) decrementDepth() (success bool) {
|
||||
iter.depth--
|
||||
if iter.depth >= 0 {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("decrementDepth", "unexpected negative nesting")
|
||||
return false
|
||||
}
|
@ -19,7 +19,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
||||
case ',':
|
||||
return true
|
||||
default:
|
||||
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found: "+string([]byte{c}))
|
||||
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -28,31 +28,37 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == '[' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
if !callback(iter) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
if !callback(iter) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != ']' {
|
||||
iter.ReportError("ReadArrayCB", "expect ] in the end")
|
||||
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
return true
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadArrayCB", "expect [ or n, but found: "+string([]byte{c}))
|
||||
iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c}))
|
||||
return false
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@ -75,14 +77,12 @@ func (iter *Iterator) ReadFloat32() (ret float32) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||
value := uint64(0)
|
||||
c := byte(' ')
|
||||
i := iter.head
|
||||
// first char
|
||||
if i == iter.tail {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
c = iter.buf[i]
|
||||
c := iter.buf[i]
|
||||
i++
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
@ -105,7 +105,7 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||
return
|
||||
}
|
||||
}
|
||||
value = uint64(ind)
|
||||
value := uint64(ind)
|
||||
// chars before dot
|
||||
non_decimal_loop:
|
||||
for ; i < iter.tail; i++ {
|
||||
@ -129,6 +129,9 @@ non_decimal_loop:
|
||||
if c == '.' {
|
||||
i++
|
||||
decimalPlaces := 0
|
||||
if i == iter.tail {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c]
|
||||
@ -140,9 +143,7 @@ non_decimal_loop:
|
||||
}
|
||||
// too many decimal places
|
||||
return iter.readFloat32SlowPath()
|
||||
case invalidCharForNumber:
|
||||
fallthrough
|
||||
case dotInNumber:
|
||||
case invalidCharForNumber, dotInNumber:
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
decimalPlaces++
|
||||
@ -189,12 +190,9 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
if len(str) == 0 {
|
||||
iter.ReportError("readFloat32SlowPath", "empty number")
|
||||
return
|
||||
}
|
||||
if str[0] == '-' {
|
||||
iter.ReportError("readFloat32SlowPath", "-- is not valid")
|
||||
errMsg := validateFloat(str)
|
||||
if errMsg != "" {
|
||||
iter.ReportError("readFloat32SlowPath", errMsg)
|
||||
return
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 32)
|
||||
@ -216,14 +214,12 @@ func (iter *Iterator) ReadFloat64() (ret float64) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||
value := uint64(0)
|
||||
c := byte(' ')
|
||||
i := iter.head
|
||||
// first char
|
||||
if i == iter.tail {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
c = iter.buf[i]
|
||||
c := iter.buf[i]
|
||||
i++
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
@ -246,7 +242,7 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||
return
|
||||
}
|
||||
}
|
||||
value = uint64(ind)
|
||||
value := uint64(ind)
|
||||
// chars before dot
|
||||
non_decimal_loop:
|
||||
for ; i < iter.tail; i++ {
|
||||
@ -270,6 +266,9 @@ non_decimal_loop:
|
||||
if c == '.' {
|
||||
i++
|
||||
decimalPlaces := 0
|
||||
if i == iter.tail {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c]
|
||||
@ -281,9 +280,7 @@ non_decimal_loop:
|
||||
}
|
||||
// too many decimal places
|
||||
return iter.readFloat64SlowPath()
|
||||
case invalidCharForNumber:
|
||||
fallthrough
|
||||
case dotInNumber:
|
||||
case invalidCharForNumber, dotInNumber:
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
decimalPlaces++
|
||||
@ -291,6 +288,9 @@ non_decimal_loop:
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
if value > maxFloat64 {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
return iter.readFloat64SlowPath()
|
||||
@ -301,12 +301,9 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
if len(str) == 0 {
|
||||
iter.ReportError("readFloat64SlowPath", "empty number")
|
||||
return
|
||||
}
|
||||
if str[0] == '-' {
|
||||
iter.ReportError("readFloat64SlowPath", "-- is not valid")
|
||||
errMsg := validateFloat(str)
|
||||
if errMsg != "" {
|
||||
iter.ReportError("readFloat64SlowPath", errMsg)
|
||||
return
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 64)
|
||||
@ -316,3 +313,30 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
|
||||
// ReadNumber read json.Number
|
||||
func (iter *Iterator) ReadNumber() (ret json.Number) {
|
||||
return json.Number(iter.readNumberAsString())
|
||||
}
|
@ -9,6 +9,7 @@ var intDigits []int8
|
||||
|
||||
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
|
||||
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1
|
||||
const maxFloat64 = 1<<53 - 1
|
||||
|
||||
func init() {
|
||||
intDigits = make([]int8, 256)
|
||||
@ -22,11 +23,17 @@ func init() {
|
||||
|
||||
// ReadUint read uint
|
||||
func (iter *Iterator) ReadUint() uint {
|
||||
if strconv.IntSize == 32 {
|
||||
return uint(iter.ReadUint32())
|
||||
}
|
||||
return uint(iter.ReadUint64())
|
||||
}
|
||||
|
||||
// ReadInt read int
|
||||
func (iter *Iterator) ReadInt() int {
|
||||
if strconv.IntSize == 32 {
|
||||
return int(iter.ReadInt32())
|
||||
}
|
||||
return int(iter.ReadInt64())
|
||||
}
|
||||
|
||||
@ -115,6 +122,7 @@ func (iter *Iterator) ReadUint32() (ret uint32) {
|
||||
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
iter.assertInteger()
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
@ -127,12 +135,14 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind2 := intDigits[iter.buf[i]]
|
||||
if ind2 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
i++
|
||||
ind3 := intDigits[iter.buf[i]]
|
||||
if ind3 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10 + uint32(ind2)
|
||||
}
|
||||
//iter.head = i + 1
|
||||
@ -141,30 +151,35 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind4 := intDigits[iter.buf[i]]
|
||||
if ind4 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100 + uint32(ind2)*10 + uint32(ind3)
|
||||
}
|
||||
i++
|
||||
ind5 := intDigits[iter.buf[i]]
|
||||
if ind5 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4)
|
||||
}
|
||||
i++
|
||||
ind6 := intDigits[iter.buf[i]]
|
||||
if ind6 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5)
|
||||
}
|
||||
i++
|
||||
ind7 := intDigits[iter.buf[i]]
|
||||
if ind7 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6)
|
||||
}
|
||||
i++
|
||||
ind8 := intDigits[iter.buf[i]]
|
||||
if ind8 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7)
|
||||
}
|
||||
i++
|
||||
@ -172,6 +187,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8)
|
||||
iter.head = i
|
||||
if ind9 == invalidCharForNumber {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
@ -180,6 +196,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
if value > uint32SafeToMultiply10 {
|
||||
@ -194,6 +211,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
value = (value << 3) + (value << 1) + uint32(ind)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
@ -226,6 +244,7 @@ func (iter *Iterator) ReadUint64() uint64 {
|
||||
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
iter.assertInteger()
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
@ -233,11 +252,73 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
return
|
||||
}
|
||||
value := uint64(ind)
|
||||
if iter.tail-iter.head > 10 {
|
||||
i := iter.head
|
||||
ind2 := intDigits[iter.buf[i]]
|
||||
if ind2 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
i++
|
||||
ind3 := intDigits[iter.buf[i]]
|
||||
if ind3 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10 + uint64(ind2)
|
||||
}
|
||||
//iter.head = i + 1
|
||||
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
|
||||
i++
|
||||
ind4 := intDigits[iter.buf[i]]
|
||||
if ind4 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100 + uint64(ind2)*10 + uint64(ind3)
|
||||
}
|
||||
i++
|
||||
ind5 := intDigits[iter.buf[i]]
|
||||
if ind5 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4)
|
||||
}
|
||||
i++
|
||||
ind6 := intDigits[iter.buf[i]]
|
||||
if ind6 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5)
|
||||
}
|
||||
i++
|
||||
ind7 := intDigits[iter.buf[i]]
|
||||
if ind7 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6)
|
||||
}
|
||||
i++
|
||||
ind8 := intDigits[iter.buf[i]]
|
||||
if ind8 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7)
|
||||
}
|
||||
i++
|
||||
ind9 := intDigits[iter.buf[i]]
|
||||
value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8)
|
||||
iter.head = i
|
||||
if ind9 == invalidCharForNumber {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
if value > uint64SafeToMultiple10 {
|
||||
@ -252,7 +333,14 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) assertInteger() {
|
||||
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
|
||||
iter.ReportError("assertInteger", "can not decode float as int")
|
||||
}
|
||||
}
|
267
iter_object.go
Normal file
267
iter_object.go
Normal file
@ -0,0 +1,267 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadObject read one field from object.
|
||||
// If object ended, returns empty string.
|
||||
// Otherwise, returns the field name.
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return "" // null
|
||||
case '{':
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
return field
|
||||
}
|
||||
if c == '}' {
|
||||
return "" // end of object
|
||||
}
|
||||
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
|
||||
return
|
||||
case ',':
|
||||
field := iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
return field
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CaseInsensitive
|
||||
func (iter *Iterator) readFieldHash() int64 {
|
||||
hash := int64(0x811c9dc5)
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
b := iter.buf[i]
|
||||
if b == '\\' {
|
||||
iter.head = i
|
||||
for _, b := range iter.readStringSlowPath() {
|
||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
if b == '"' {
|
||||
iter.head = i + 1
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.ReportError("readFieldHash", `incomplete field name`)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func calcHash(str string, caseSensitive bool) int64 {
|
||||
if !caseSensitive {
|
||||
str = strings.ToLower(str)
|
||||
}
|
||||
hash := int64(0x811c9dc5)
|
||||
for _, b := range []byte(str) {
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
return int64(hash)
|
||||
}
|
||||
|
||||
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
var field string
|
||||
if c == '{' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field = iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == '}' {
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadMapCB read map with callback, the key can be any string
|
||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadMapCB", `object not ended with }`)
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == '}' {
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectStart() bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '}' {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return true
|
||||
} else if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return false
|
||||
}
|
||||
iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
|
||||
str := iter.ReadStringAsSlice()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == nil {
|
||||
return str
|
||||
}
|
||||
return ret
|
||||
}
|
@ -14,7 +14,7 @@ func (iter *Iterator) ReadNil() (ret bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadBool reads a json object as Bool
|
||||
// ReadBool reads a json object as BoolValue
|
||||
func (iter *Iterator) ReadBool() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == 't' {
|
||||
@ -25,7 +25,7 @@ func (iter *Iterator) ReadBool() (ret bool) {
|
||||
iter.skipFourBytes('a', 'l', 's', 'e')
|
||||
return false
|
||||
}
|
||||
iter.ReportError("ReadBool", "expect t or f")
|
||||
iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
@ -37,17 +37,24 @@ func (iter *Iterator) SkipAndReturnBytes() []byte {
|
||||
return iter.stopCapture()
|
||||
}
|
||||
|
||||
type captureBuffer struct {
|
||||
startedAt int
|
||||
captured []byte
|
||||
// SkipAndAppendBytes skips next JSON element and appends its content to
|
||||
// buffer, returning the result.
|
||||
func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte {
|
||||
iter.startCaptureTo(buf, iter.head)
|
||||
iter.Skip()
|
||||
return iter.stopCapture()
|
||||
}
|
||||
|
||||
func (iter *Iterator) startCapture(captureStartedAt int) {
|
||||
func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) {
|
||||
if iter.captured != nil {
|
||||
panic("already in capture mode")
|
||||
}
|
||||
iter.captureStartedAt = captureStartedAt
|
||||
iter.captured = make([]byte, 0, 32)
|
||||
iter.captured = buf
|
||||
}
|
||||
|
||||
func (iter *Iterator) startCapture(captureStartedAt int) {
|
||||
iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt)
|
||||
}
|
||||
|
||||
func (iter *Iterator) stopCapture() []byte {
|
||||
@ -58,11 +65,7 @@ func (iter *Iterator) stopCapture() []byte {
|
||||
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
||||
iter.captureStartedAt = -1
|
||||
iter.captured = nil
|
||||
if len(captured) == 0 {
|
||||
return remaining
|
||||
}
|
||||
captured = append(captured, remaining...)
|
||||
return captured
|
||||
return append(captured, remaining...)
|
||||
}
|
||||
|
||||
// Skip skips a json object and positions to relatively the next json object
|
@ -1,4 +1,4 @@
|
||||
//+build jsoniter-sloppy
|
||||
//+build jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
||||
@ -22,6 +22,9 @@ func (iter *Iterator) skipNumber() {
|
||||
|
||||
func (iter *Iterator) skipArray() {
|
||||
level := 1
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
@ -31,8 +34,14 @@ func (iter *Iterator) skipArray() {
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '[': // If open symbol, increase level
|
||||
level++
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
case ']': // If close symbol, increase level
|
||||
level--
|
||||
if !iter.decrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
@ -50,6 +59,10 @@ func (iter *Iterator) skipArray() {
|
||||
|
||||
func (iter *Iterator) skipObject() {
|
||||
level := 1
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
@ -59,8 +72,14 @@ func (iter *Iterator) skipObject() {
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '{': // If open symbol, increase level
|
||||
level++
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
case '}': // If close symbol, increase level
|
||||
level--
|
||||
if !iter.decrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
@ -1,4 +1,4 @@
|
||||
//+build jsoniter-sloppy
|
||||
//+build jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
@ -1,13 +1,23 @@
|
||||
//+build !jsoniter-sloppy
|
||||
//+build !jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func (iter *Iterator) skipNumber() {
|
||||
if !iter.trySkipNumber() {
|
||||
iter.unreadByte()
|
||||
iter.ReadFloat32()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
iter.ReadFloat64()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
iter.Error = nil
|
||||
iter.ReadBigFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,12 +31,24 @@ func (iter *Iterator) trySkipNumber() bool {
|
||||
if dotFound {
|
||||
iter.ReportError("validateNumber", `more than one dot found in number`)
|
||||
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:
|
||||
switch c {
|
||||
case ',', ']', '}', ' ', '\t', '\n', '\r':
|
||||
if iter.head == i {
|
||||
return false // if - without following digits
|
||||
}
|
||||
iter.head = i
|
||||
return true // must be valid
|
||||
}
|
||||
@ -52,7 +74,7 @@ func (iter *Iterator) trySkipString() bool {
|
||||
} else if c == '\\' {
|
||||
return false
|
||||
} else if c < ' ' {
|
||||
iter.ReportError("ReadString",
|
||||
iter.ReportError("trySkipString",
|
||||
fmt.Sprintf(`invalid control character found: %d`, c))
|
||||
return true // already failed
|
||||
}
|
@ -28,7 +28,7 @@ func (iter *Iterator) ReadString() (ret string) {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return ""
|
||||
}
|
||||
iter.ReportError("ReadString", `expects " or n`)
|
||||
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
@ -42,66 +42,75 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
|
||||
}
|
||||
if c == '\\' {
|
||||
c = iter.readByte()
|
||||
switch c {
|
||||
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
|
||||
}
|
||||
str = iter.readEscapedChar(c, str)
|
||||
} else {
|
||||
str = append(str, c)
|
||||
}
|
||||
}
|
||||
iter.ReportError("ReadString", "unexpected end of input")
|
||||
iter.ReportError("readStringSlowPath", "unexpected end of input")
|
||||
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("readEscapedChar",
|
||||
`invalid escape char after \`)
|
||||
return nil
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// ReadStringAsSlice read string from iterator without copying into string form.
|
||||
// The []byte can not be kept, as it will change after next iterator call.
|
||||
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
||||
@ -130,7 +139,7 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
||||
}
|
||||
return copied
|
||||
}
|
||||
iter.ReportError("ReadString", `expects " or n`)
|
||||
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
@ -147,7 +156,7 @@ func (iter *Iterator) readU4() (ret rune) {
|
||||
} else if c >= 'A' && c <= 'F' {
|
||||
ret = ret*16 + rune(c-'A'+10)
|
||||
} else {
|
||||
iter.ReportError("readU4", "expects 0~9 or a~f")
|
||||
iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_alias(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type myint int
|
||||
type myint8 int8
|
||||
type myint16 int16
|
||||
type myint32 int32
|
||||
type myint64 int64
|
||||
type myuint uint
|
||||
type myuint8 uint8
|
||||
type myuint16 uint16
|
||||
type myuint32 uint32
|
||||
type myuint64 uint64
|
||||
type myfloat32 float32
|
||||
type myfloat64 float64
|
||||
type mystring string
|
||||
type mybool bool
|
||||
type myuintptr uintptr
|
||||
var a struct {
|
||||
A myint8 `json:"a"`
|
||||
B myint16 `json:"stream"`
|
||||
C myint32 `json:"c"`
|
||||
D myint64 `json:"d"`
|
||||
E myuint8 `json:"e"`
|
||||
F myuint16 `json:"f"`
|
||||
G myuint32 `json:"g"`
|
||||
H myuint64 `json:"h"`
|
||||
I myfloat32 `json:"i"`
|
||||
J myfloat64 `json:"j"`
|
||||
K mystring `json:"k"`
|
||||
L myint `json:"l"`
|
||||
M myuint `json:"m"`
|
||||
N mybool `json:"n"`
|
||||
O myuintptr `json:"o"`
|
||||
}
|
||||
|
||||
should.Nil(UnmarshalFromString(`{"a" : 1, "stream" : 1, "c": 1, "d" : 1, "e" : 1, "f" : 1, "g" : 1, "h": 1, "i" : 1, "j" : 1, "k" :"xxxx", "l" : 1, "m":1, "n": true, "o" : 1}`, &a))
|
||||
should.Equal(myfloat32(1), a.I)
|
||||
should.Equal(myfloat64(1), a.J)
|
||||
should.Equal(myint8(1), a.A)
|
||||
should.Equal(myint16(1), a.B)
|
||||
should.Equal(myint32(1), a.C)
|
||||
should.Equal(myint64(1), a.D)
|
||||
should.Equal(myuint8(1), a.E)
|
||||
should.Equal(myuint16(1), a.F)
|
||||
should.Equal(myuint32(1), a.G)
|
||||
should.Equal(myuint64(1), a.H)
|
||||
should.Equal(mystring("xxxx"), a.K)
|
||||
should.Equal(mybool(true), a.N)
|
||||
should.Equal(myuintptr(1), a.O)
|
||||
b, err := Marshal(a)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"a":1,"stream":1,"c":1,"d":1,"e":1,"f":1,"g":1,"h":1,"i":1,"j":1,"k":"xxxx","l":1,"m":1,"n":true,"o":1}`, string(b))
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_wrap_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal("hello", any.Get("Field1").ToString())
|
||||
any = Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal(1, any.Size())
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var stringConvertMap = map[string]string{
|
||||
"null": "",
|
||||
"321.1": "321.1",
|
||||
`"1.1"`: "1.1",
|
||||
`"-123.1"`: "-123.1",
|
||||
"0.0": "0.0",
|
||||
"0": "0",
|
||||
`"0"`: "0",
|
||||
`"0.0"`: "0.0",
|
||||
`"00.0"`: "00.0",
|
||||
"true": "true",
|
||||
"false": "false",
|
||||
`"true"`: "true",
|
||||
`"false"`: "false",
|
||||
`"true123"`: "true123",
|
||||
`"+1"`: "+1",
|
||||
"[]": "[]",
|
||||
"[1,2]": "[1,2]",
|
||||
"{}": "{}",
|
||||
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
||||
}
|
||||
|
||||
func Test_read_any_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for k, v := range stringConvertMap {
|
||||
any := Get([]byte(k))
|
||||
should.Equal(v, any.ToString(), "original val "+k)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_string_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte(`"hello"`))
|
||||
should.Equal("hello", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
any = Get([]byte(`" "`))
|
||||
should.False(any.ToBool())
|
||||
any = Get([]byte(`"false"`))
|
||||
should.True(any.ToBool())
|
||||
any = Get([]byte(`"123"`))
|
||||
should.Equal(123, any.ToInt())
|
||||
}
|
||||
|
||||
func Test_wrap_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := WrapString("123")
|
||||
should.Equal(123, any.ToInt())
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_true(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `true`)
|
||||
should.True(iter.ReadBool())
|
||||
iter = ParseString(ConfigDefault, `true`)
|
||||
should.Equal(true, iter.Read())
|
||||
}
|
||||
|
||||
func Test_false(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `false`)
|
||||
should.False(iter.ReadBool())
|
||||
}
|
||||
|
||||
func Test_write_true_false(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(ConfigDefault, buf, 4096)
|
||||
stream.WriteTrue()
|
||||
stream.WriteFalse()
|
||||
stream.WriteBool(false)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("truefalsefalse", buf.String())
|
||||
}
|
||||
|
||||
func Test_write_val_bool(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(ConfigDefault, buf, 4096)
|
||||
stream.WriteVal(true)
|
||||
should.Equal(stream.Buffered(), 4)
|
||||
stream.Flush()
|
||||
should.Equal(stream.Buffered(), 0)
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("true", buf.String())
|
||||
}
|
||||
|
||||
func Test_encode_string_bool(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field bool `json:",omitempty,string"`
|
||||
}
|
||||
should := require.New(t)
|
||||
output, err := json.Marshal(TestObject{true})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"true"}`, string(output))
|
||||
output, err = Marshal(TestObject{true})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"true"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_decode_string_bool(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field bool `json:",omitempty,string"`
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
err := json.Unmarshal([]byte(`{"Field":"true"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.True(obj.Field)
|
||||
|
||||
obj = TestObject{}
|
||||
err = json.Unmarshal([]byte(`{"Field":true}`), &obj)
|
||||
should.NotNil(err)
|
||||
|
||||
obj = TestObject{}
|
||||
err = Unmarshal([]byte(`{"Field":"true"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.True(obj.Field)
|
||||
|
||||
obj = TestObject{}
|
||||
err = Unmarshal([]byte(`{"Field":true}`), &obj)
|
||||
should.NotNil(err)
|
||||
}
|
@ -1,287 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Test_customize_type_decoder(t *testing.T) {
|
||||
RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
*((*time.Time)(ptr)) = t
|
||||
})
|
||||
defer ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
val := time.Time{}
|
||||
err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
year, month, day := val.Date()
|
||||
if year != 2016 || month != 12 || day != 5 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_customize_type_encoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
RegisterTypeEncoderFunc("time.Time", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
t := *((*time.Time)(ptr))
|
||||
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
|
||||
}, nil)
|
||||
defer ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
val := time.Unix(0, 0)
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`"1970-01-01 00:00:00"`, str)
|
||||
}
|
||||
|
||||
func Test_customize_byte_array_encoder(t *testing.T) {
|
||||
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
should := require.New(t)
|
||||
RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
t := *((*[]byte)(ptr))
|
||||
stream.WriteString(string(t))
|
||||
}, nil)
|
||||
defer ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
val := []byte("abc")
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`"abc"`, str)
|
||||
}
|
||||
|
||||
func Test_customize_float_marshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
json := Config{MarshalFloatWith6Digits: true}.Froze()
|
||||
str, err := json.MarshalToString(float32(1.23456789))
|
||||
should.Nil(err)
|
||||
should.Equal("1.234568", str)
|
||||
}
|
||||
|
||||
type Tom struct {
|
||||
field1 string
|
||||
}
|
||||
|
||||
func Test_customize_field_decoder(t *testing.T) {
|
||||
RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
})
|
||||
defer ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
tom := Tom{}
|
||||
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TestObject1 struct {
|
||||
field1 string
|
||||
}
|
||||
|
||||
type testExtension struct {
|
||||
DummyExtension
|
||||
}
|
||||
|
||||
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
|
||||
if structDescriptor.Type.String() != "jsoniter.TestObject1" {
|
||||
return
|
||||
}
|
||||
binding := structDescriptor.GetField("field1")
|
||||
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
val, _ := strconv.Atoi(str)
|
||||
stream.WriteInt(val)
|
||||
}}
|
||||
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
}}
|
||||
binding.ToNames = []string{"field-1"}
|
||||
binding.FromNames = []string{"field-1"}
|
||||
}
|
||||
|
||||
func Test_customize_field_by_extension(t *testing.T) {
|
||||
should := require.New(t)
|
||||
RegisterExtension(&testExtension{})
|
||||
obj := TestObject1{}
|
||||
err := UnmarshalFromString(`{"field-1": 100}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Equal("100", obj.field1)
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-1":100}`, str)
|
||||
}
|
||||
|
||||
type timeImplementedMarshaler time.Time
|
||||
|
||||
func (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {
|
||||
seconds := time.Time(obj).Unix()
|
||||
return []byte(strconv.FormatInt(seconds, 10)), nil
|
||||
}
|
||||
|
||||
func Test_marshaler(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field timeImplementedMarshaler
|
||||
}
|
||||
should := require.New(t)
|
||||
val := timeImplementedMarshaler(time.Unix(123, 0))
|
||||
obj := TestObject{val}
|
||||
bytes, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":123}`, string(bytes))
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":123}`, str)
|
||||
}
|
||||
|
||||
func Test_marshaler_and_encoder(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *timeImplementedMarshaler
|
||||
}
|
||||
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
should := require.New(t)
|
||||
RegisterTypeEncoderFunc("jsoniter.timeImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteString("hello from encoder")
|
||||
}, nil)
|
||||
val := timeImplementedMarshaler(time.Unix(123, 0))
|
||||
obj := TestObject{&val}
|
||||
bytes, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":123}`, string(bytes))
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello from encoder"}`, str)
|
||||
}
|
||||
|
||||
type ObjectImplementedUnmarshaler int
|
||||
|
||||
func (obj *ObjectImplementedUnmarshaler) UnmarshalJSON(s []byte) error {
|
||||
val, _ := strconv.ParseInt(string(s[1:len(s)-1]), 10, 64)
|
||||
*obj = ObjectImplementedUnmarshaler(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_unmarshaler(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var obj ObjectImplementedUnmarshaler
|
||||
err := json.Unmarshal([]byte(` "100" `), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(obj))
|
||||
iter := ParseString(ConfigDefault, ` "100" `)
|
||||
iter.ReadVal(&obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(obj))
|
||||
}
|
||||
|
||||
func Test_unmarshaler_and_decoder(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *ObjectImplementedUnmarshaler
|
||||
Field2 string
|
||||
}
|
||||
ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
should := require.New(t)
|
||||
RegisterTypeDecoderFunc("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*(*ObjectImplementedUnmarshaler)(ptr) = 10
|
||||
iter.Skip()
|
||||
})
|
||||
obj := TestObject{}
|
||||
val := ObjectImplementedUnmarshaler(0)
|
||||
obj.Field = &val
|
||||
err := json.Unmarshal([]byte(`{"Field":"100"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(*obj.Field))
|
||||
err = Unmarshal([]byte(`{"Field":"100"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(10, int(*obj.Field))
|
||||
}
|
||||
|
||||
type tmString string
|
||||
type tmStruct struct {
|
||||
String tmString
|
||||
}
|
||||
|
||||
func (s tmStruct) MarshalJSON() ([]byte, error) {
|
||||
var b []byte
|
||||
b = append(b, '"')
|
||||
b = append(b, s.String...)
|
||||
b = append(b, '"')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func Test_marshaler_on_struct(t *testing.T) {
|
||||
fixed := tmStruct{"hello"}
|
||||
//json.Marshal(fixed)
|
||||
Marshal(fixed)
|
||||
}
|
||||
|
||||
type withChan struct {
|
||||
F2 chan []byte
|
||||
}
|
||||
|
||||
func (q withChan) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`""`), nil
|
||||
}
|
||||
|
||||
func (q *withChan) UnmarshalJSON(value []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_marshal_json_with_chan(t *testing.T) {
|
||||
type TestObject struct {
|
||||
F1 withChan
|
||||
}
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(TestObject{})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"F1":""}`, output)
|
||||
}
|
||||
|
||||
type withTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (t *withTime) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
func (t withTime) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"fake"`), nil
|
||||
}
|
||||
|
||||
func Test_marshal_json_with_time(t *testing.T) {
|
||||
type S1 struct {
|
||||
F1 withTime
|
||||
F2 *withTime
|
||||
}
|
||||
type TestObject struct {
|
||||
TF1 S1
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{
|
||||
S1{
|
||||
F1: withTime{
|
||||
time.Unix(0, 0),
|
||||
},
|
||||
F2: &withTime{
|
||||
time.Unix(0, 0),
|
||||
},
|
||||
},
|
||||
}
|
||||
output, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"TF1":{"F1":"fake","F2":"fake"}}`, string(output))
|
||||
output, err = Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"TF1":{"F1":"fake","F2":"fake"}}`, string(output))
|
||||
obj = TestObject{}
|
||||
should.Nil(json.Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
|
||||
should.NotNil(obj.TF1.F2)
|
||||
obj = TestObject{}
|
||||
should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
|
||||
should.NotNil(obj.TF1.F2)
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_bind_api_demo(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []int{}
|
||||
err := UnmarshalFromString(`[0,1,2,3] `, &val)
|
||||
should.Nil(err)
|
||||
should.Equal([]int{0, 1, 2, 3}, val)
|
||||
}
|
||||
|
||||
func Test_iterator_api_demo(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[0,1,2,3]`)
|
||||
total := 0
|
||||
for iter.ReadArray() {
|
||||
total += iter.ReadInt()
|
||||
}
|
||||
should.Equal(6, total)
|
||||
}
|
||||
|
||||
type People struct {
|
||||
Name string
|
||||
Gender string
|
||||
Age int
|
||||
Address string
|
||||
Mobile string
|
||||
Country string
|
||||
Height int
|
||||
}
|
||||
|
||||
func jsoniterMarshal(p *People) error {
|
||||
_, err := Marshal(p)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func stdMarshal(p *People) error {
|
||||
_, err := json.Marshal(p)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func BenchmarkJosniterMarshal(b *testing.B) {
|
||||
var p People
|
||||
p.Address = "上海市徐汇区漕宝路"
|
||||
p.Age = 30
|
||||
p.Country = "中国"
|
||||
p.Gender = "male"
|
||||
p.Height = 170
|
||||
p.Mobile = "18502120533"
|
||||
p.Name = "Elvin"
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := jsoniterMarshal(&p)
|
||||
if nil != err {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStdMarshal(b *testing.B) {
|
||||
var p People
|
||||
p.Address = "上海市徐汇区漕宝路"
|
||||
p.Age = 30
|
||||
p.Country = "中国"
|
||||
p.Gender = "male"
|
||||
p.Height = 170
|
||||
p.Mobile = "18502120533"
|
||||
p.Name = "Elvin"
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := stdMarshal(&p)
|
||||
if nil != err {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_encode_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var a interface{}
|
||||
a = int8(10)
|
||||
str, err := MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "10")
|
||||
a = float32(3)
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "3")
|
||||
a = map[string]interface{}{"abc": 1}
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, `{"abc":1}`)
|
||||
a = uintptr(1)
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "1")
|
||||
a = uint(1)
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "1")
|
||||
a = uint8(1)
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "1")
|
||||
a = json.RawMessage("abc")
|
||||
MarshalToString(a)
|
||||
str, err = MarshalToString(a)
|
||||
should.Nil(err)
|
||||
should.Equal(str, "abc")
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_encode_fixed_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type FixedArray [2]float64
|
||||
fixed := FixedArray{0.1, 1.0}
|
||||
output, err := MarshalToString(fixed)
|
||||
should.Nil(err)
|
||||
should.Equal("[0.1,1]", output)
|
||||
}
|
||||
|
||||
func Test_encode_fixed_array_of_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type FixedArray [2]map[string]string
|
||||
fixed := FixedArray{map[string]string{"1": "2"}, map[string]string{"3": "4"}}
|
||||
output, err := MarshalToString(fixed)
|
||||
should.Nil(err)
|
||||
should.Equal(`[{"1":"2"},{"3":"4"}]`, output)
|
||||
}
|
||||
|
||||
func Test_decode_fixed_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type FixedArray [2]float64
|
||||
var fixed FixedArray
|
||||
should.Nil(json.Unmarshal([]byte("[1,2,3]"), &fixed))
|
||||
should.Equal(float64(1), fixed[0])
|
||||
should.Equal(float64(2), fixed[1])
|
||||
should.Nil(Unmarshal([]byte("[1,2,3]"), &fixed))
|
||||
should.Equal(float64(1), fixed[0])
|
||||
should.Equal(float64(2), fixed[1])
|
||||
}
|
@ -1,299 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Test_write_array_of_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
array := []interface{}{"hello"}
|
||||
str, err := MarshalToString(array)
|
||||
should.Nil(err)
|
||||
should.Equal(`["hello"]`, str)
|
||||
}
|
||||
|
||||
func Test_write_map_of_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := map[string]interface{}{"hello": "world"}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"hello":"world"}`, str)
|
||||
}
|
||||
|
||||
func Test_write_map_of_interface_in_struct(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field map[string]interface{}
|
||||
}
|
||||
should := require.New(t)
|
||||
val := TestObject{map[string]interface{}{"hello": "world"}}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":{"hello":"world"}}`, str)
|
||||
}
|
||||
|
||||
func Test_write_map_of_interface_in_struct_with_two_fields(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field map[string]interface{}
|
||||
Field2 string
|
||||
}
|
||||
should := require.New(t)
|
||||
val := TestObject{map[string]interface{}{"hello": "world"}, ""}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"Field":{"hello":"world"}`)
|
||||
}
|
||||
|
||||
type MyInterface interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
type MyString string
|
||||
|
||||
func (ms MyString) Hello() string {
|
||||
return string(ms)
|
||||
}
|
||||
|
||||
func Test_write_map_of_custom_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
myStr := MyString("world")
|
||||
should.Equal("world", myStr.Hello())
|
||||
val := map[string]MyInterface{"hello": myStr}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"hello":"world"}`, str)
|
||||
}
|
||||
|
||||
func Test_write_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val interface{}
|
||||
val = "hello"
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`"hello"`, str)
|
||||
}
|
||||
|
||||
func Test_read_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val interface{}
|
||||
err := UnmarshalFromString(`"hello"`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", val)
|
||||
err = UnmarshalFromString(`1e1`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal(float64(10), val)
|
||||
err = UnmarshalFromString(`1.0e1`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal(float64(10), val)
|
||||
err = json.Unmarshal([]byte(`1.0e1`), &val)
|
||||
should.Nil(err)
|
||||
should.Equal(float64(10), val)
|
||||
}
|
||||
|
||||
func Test_read_custom_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val MyInterface
|
||||
RegisterTypeDecoderFunc("jsoniter.MyInterface", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*MyInterface)(ptr)) = MyString(iter.ReadString())
|
||||
})
|
||||
err := UnmarshalFromString(`"hello"`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", val.Hello())
|
||||
}
|
||||
|
||||
func Test_decode_object_contain_empty_interface(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field interface{}
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
obj.Field = 1024
|
||||
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
|
||||
should.Equal("hello", obj.Field)
|
||||
}
|
||||
|
||||
func Test_decode_object_contain_non_empty_interface(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field MyInterface
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
obj.Field = MyString("abc")
|
||||
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
|
||||
should.Equal(MyString("hello"), obj.Field)
|
||||
}
|
||||
|
||||
func Test_encode_object_contain_empty_interface(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field interface{}
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
obj.Field = 1024
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":1024}`, str)
|
||||
}
|
||||
|
||||
func Test_encode_object_contain_non_empty_interface(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field MyInterface
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
obj.Field = MyString("hello")
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_nil_non_empty_interface(t *testing.T) {
|
||||
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
type TestObject struct {
|
||||
Field []MyInterface
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
b := []byte(`{"Field":["AAA"]}`)
|
||||
should.NotNil(json.Unmarshal(b, &obj))
|
||||
should.NotNil(Unmarshal(b, &obj))
|
||||
}
|
||||
|
||||
func Test_read_large_number_as_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val interface{}
|
||||
err := Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
|
||||
should.Nil(err)
|
||||
output, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`123456789123456789123456789`, output)
|
||||
}
|
||||
|
||||
func Test_nested_one_field_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type YetYetAnotherObject struct {
|
||||
Field string
|
||||
}
|
||||
type YetAnotherObject struct {
|
||||
Field *YetYetAnotherObject
|
||||
}
|
||||
type AnotherObject struct {
|
||||
Field *YetAnotherObject
|
||||
}
|
||||
type TestObject struct {
|
||||
Me *AnotherObject
|
||||
}
|
||||
obj := TestObject{&AnotherObject{&YetAnotherObject{&YetYetAnotherObject{"abc"}}}}
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
|
||||
str, err = MarshalToString(&obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
|
||||
}
|
||||
|
||||
func Test_struct_with_embedded_ptr_with_tag(t *testing.T) {
|
||||
type O1 struct {
|
||||
O1F string
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
O1 *O1
|
||||
}
|
||||
|
||||
type T struct {
|
||||
Option `json:","`
|
||||
}
|
||||
var obj T
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"O1":null}`, output)
|
||||
}
|
||||
|
||||
func Test_struct_with_one_nil(t *testing.T) {
|
||||
type TestObject struct {
|
||||
F *float64
|
||||
}
|
||||
var obj TestObject
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"F":null}`, output)
|
||||
}
|
||||
|
||||
func Test_struct_with_one_nil_embedded(t *testing.T) {
|
||||
type Parent struct {
|
||||
Field1 string
|
||||
Field2 string
|
||||
}
|
||||
type TestObject struct {
|
||||
*Parent
|
||||
}
|
||||
obj := TestObject{}
|
||||
should := require.New(t)
|
||||
bytes, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal("{}", string(bytes))
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{}`, output)
|
||||
}
|
||||
|
||||
func Test_struct_with_not_nil_embedded(t *testing.T) {
|
||||
type Parent struct {
|
||||
Field0 string
|
||||
Field1 []string
|
||||
Field2 map[string]interface{}
|
||||
}
|
||||
type TestObject struct {
|
||||
*Parent
|
||||
}
|
||||
should := require.New(t)
|
||||
var obj TestObject
|
||||
err := UnmarshalFromString(`{"Field0":"1","Field1":null,"Field2":{"K":"V"}}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Nil(obj.Field1)
|
||||
should.Equal(map[string]interface{}{"K": "V"}, obj.Field2)
|
||||
should.Equal("1", obj.Field0)
|
||||
}
|
||||
|
||||
func Test_array_with_one_nil_ptr(t *testing.T) {
|
||||
obj := [1]*float64{nil}
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`[null]`, output)
|
||||
}
|
||||
|
||||
func Test_array_with_one_not_nil_ptr(t *testing.T) {
|
||||
two := float64(2)
|
||||
obj := [1]*float64{&two}
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`[2]`, output)
|
||||
}
|
||||
|
||||
func Test_embedded_array_with_one_nil(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field1 int
|
||||
Field2 [1]*float64
|
||||
}
|
||||
var obj TestObject
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Contains(output, `"Field2":[null]`)
|
||||
}
|
||||
|
||||
func Test_array_with_nothing(t *testing.T) {
|
||||
var obj [2]*float64
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`[null,null]`, output)
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_missing_object_end(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Metric string `json:"metric"`
|
||||
Tags map[string]interface{} `json:"tags"`
|
||||
}
|
||||
obj := TestObject{}
|
||||
should.NotNil(UnmarshalFromString(`{"metric": "sys.777","tags": {"a":"123"}`, &obj))
|
||||
}
|
||||
|
||||
func Test_missing_array_end(t *testing.T) {
|
||||
should := require.New(t)
|
||||
should.NotNil(UnmarshalFromString(`[1,2,3`, &[]int{}))
|
||||
}
|
||||
|
||||
func Test_invalid_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Get([]byte("[]"))
|
||||
should.Equal(Invalid, any.Get(0.3).ValueType())
|
||||
// is nil correct ?
|
||||
should.Equal(nil, any.Get(0.3).GetInterface())
|
||||
|
||||
any = any.Get(0.3)
|
||||
should.Equal(false, any.ToBool())
|
||||
should.Equal(int(0), any.ToInt())
|
||||
should.Equal(int32(0), any.ToInt32())
|
||||
should.Equal(int64(0), any.ToInt64())
|
||||
should.Equal(uint(0), any.ToUint())
|
||||
should.Equal(uint32(0), any.ToUint32())
|
||||
should.Equal(uint64(0), any.ToUint64())
|
||||
should.Equal(float32(0), any.ToFloat32())
|
||||
should.Equal(float64(0), any.ToFloat64())
|
||||
should.Equal("", any.ToString())
|
||||
|
||||
should.Equal(Invalid, any.Get(0.1).Get(1).ValueType())
|
||||
}
|
||||
|
||||
func Test_invalid_struct_input(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct{}
|
||||
input := []byte{54, 141, 30}
|
||||
obj := TestObject{}
|
||||
should.NotNil(Unmarshal(input, &obj))
|
||||
}
|
||||
|
||||
func Test_invalid_slice_input(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct{}
|
||||
input := []byte{93}
|
||||
obj := []string{}
|
||||
should.NotNil(Unmarshal(input, &obj))
|
||||
}
|
||||
|
||||
func Test_invalid_array_input(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct{}
|
||||
input := []byte{93}
|
||||
obj := [0]string{}
|
||||
should.NotNil(Unmarshal(input, &obj))
|
||||
}
|
||||
|
||||
func Test_double_negative(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var v interface{}
|
||||
should.NotNil(json.Unmarshal([]byte(`--2`), &v))
|
||||
var vFloat64 float64
|
||||
should.NotNil(UnmarshalFromString(`--2`, &vFloat64))
|
||||
var vFloat32 float32
|
||||
should.NotNil(UnmarshalFromString(`--2`, &vFloat32))
|
||||
var vInt int
|
||||
should.NotNil(UnmarshalFromString(`--2`, &vInt))
|
||||
iter := ParseString(ConfigDefault, `--2`)
|
||||
iter.Skip()
|
||||
should.NotEqual(io.EOF, iter.Error)
|
||||
should.NotNil(iter.Error)
|
||||
}
|
||||
|
||||
func Test_leading_zero(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var v interface{}
|
||||
should.NotNil(json.Unmarshal([]byte(`01`), &v))
|
||||
var vFloat64 float64
|
||||
should.NotNil(UnmarshalFromString(`01`, &vFloat64))
|
||||
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) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `,`)
|
||||
iter.ReadFloat64()
|
||||
should.NotEqual(io.EOF, iter.Error)
|
||||
should.NotNil(iter.Error)
|
||||
iter = ParseString(ConfigDefault, `,`)
|
||||
iter.ReadFloat32()
|
||||
should.NotEqual(io.EOF, iter.Error)
|
||||
should.NotNil(iter.Error)
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_read_by_one(t *testing.T) {
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 1)
|
||||
b := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'a' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
iter.unreadByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
b = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'a' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_by_two(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 2)
|
||||
b := iter.readByte()
|
||||
should.Nil(iter.Error)
|
||||
should.Equal(byte('a'), b)
|
||||
b = iter.readByte()
|
||||
should.Nil(iter.Error)
|
||||
should.Equal(byte('b'), b)
|
||||
iter.unreadByte()
|
||||
should.Nil(iter.Error)
|
||||
iter.unreadByte()
|
||||
should.Nil(iter.Error)
|
||||
b = iter.readByte()
|
||||
should.Nil(iter.Error)
|
||||
should.Equal(byte('a'), b)
|
||||
}
|
||||
|
||||
func Test_read_until_eof(t *testing.T) {
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 2)
|
||||
iter.readByte()
|
||||
iter.readByte()
|
||||
b := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'c' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
iter.readByte()
|
||||
if iter.Error != io.EOF {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func Test_large_file(t *testing.T) {
|
||||
// file, err := os.Open("/tmp/large-file.json")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// iter := Parse(file, 4096)
|
||||
// count := 0
|
||||
// for iter.ReadArray() {
|
||||
// iter.Skip()
|
||||
// count++
|
||||
// }
|
||||
// if count != 11351 {
|
||||
// t.Fatal(count)
|
||||
// }
|
||||
//}
|
||||
|
||||
func Benchmark_jsoniter_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
iter := Parse(ConfigDefault, file, 4096)
|
||||
count := 0
|
||||
for iter.ReadArray() {
|
||||
iter.Skip()
|
||||
count++
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
file.Close()
|
||||
result := []struct{}{}
|
||||
json.Unmarshal(bytes, &result)
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Test_read_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `{"hello": "world"}`)
|
||||
m := map[string]string{"1": "2"}
|
||||
iter.ReadVal(&m)
|
||||
copy(iter.buf, []byte{0, 0, 0, 0, 0, 0})
|
||||
should.Equal(map[string]string{"1": "2", "hello": "world"}, m)
|
||||
}
|
||||
|
||||
func Test_read_map_of_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `{"hello": "world"}`)
|
||||
m := map[string]interface{}{"1": "2"}
|
||||
iter.ReadVal(&m)
|
||||
should.Equal(map[string]interface{}{"1": "2", "hello": "world"}, m)
|
||||
iter = ParseString(ConfigDefault, `{"hello": "world"}`)
|
||||
should.Equal(map[string]interface{}{"hello": "world"}, iter.Read())
|
||||
}
|
||||
|
||||
func Test_map_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap(map[string][]int{"Field1": {1, 2}})
|
||||
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
|
||||
// map write to
|
||||
stream := NewStream(ConfigDefault, nil, 0)
|
||||
any.WriteTo(stream)
|
||||
// TODO cannot pass
|
||||
//should.Equal(string(stream.buf), "")
|
||||
}
|
||||
|
||||
func Test_write_val_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := map[string]string{"1": "2"}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"1":"2"}`, str)
|
||||
}
|
||||
|
||||
func Test_slice_of_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []map[string]string{{"1": "2"}}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`[{"1":"2"}]`, str)
|
||||
val = []map[string]string{}
|
||||
should.Nil(UnmarshalFromString(str, &val))
|
||||
should.Equal("2", val[0]["1"])
|
||||
}
|
||||
|
||||
func Test_encode_int_key_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := map[int]string{1: "2"}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"1":"2"}`, str)
|
||||
}
|
||||
|
||||
func Test_decode_int_key_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val map[int]string
|
||||
should.Nil(UnmarshalFromString(`{"1":"2"}`, &val))
|
||||
should.Equal(map[int]string{1: "2"}, val)
|
||||
}
|
||||
|
||||
func Test_encode_TextMarshaler_key_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
f, _, _ := big.ParseFloat("1", 10, 64, big.ToZero)
|
||||
val := map[*big.Float]string{f: "2"}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"1":"2"}`, str)
|
||||
}
|
||||
|
||||
func Test_decode_TextMarshaler_key_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val map[*big.Float]string
|
||||
should.Nil(UnmarshalFromString(`{"1":"2"}`, &val))
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"1":"2"}`, str)
|
||||
}
|
||||
|
||||
func Test_map_key_with_escaped_char(t *testing.T) {
|
||||
type Ttest struct {
|
||||
Map map[string]string
|
||||
}
|
||||
var jsonBytes = []byte(`
|
||||
{
|
||||
"Map":{
|
||||
"k\"ey": "val"
|
||||
}
|
||||
}`)
|
||||
should := require.New(t)
|
||||
{
|
||||
var obj Ttest
|
||||
should.Nil(json.Unmarshal(jsonBytes, &obj))
|
||||
should.Equal(map[string]string{"k\"ey": "val"}, obj.Map)
|
||||
}
|
||||
{
|
||||
var obj Ttest
|
||||
should.Nil(Unmarshal(jsonBytes, &obj))
|
||||
should.Equal(map[string]string{"k\"ey": "val"}, obj.Map)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encode_map_with_sorted_keys(t *testing.T) {
|
||||
should := require.New(t)
|
||||
m := map[string]interface{}{
|
||||
"3": 3,
|
||||
"1": 1,
|
||||
"2": 2,
|
||||
}
|
||||
bytes, err := json.Marshal(m)
|
||||
should.Nil(err)
|
||||
output, err := ConfigCompatibleWithStandardLibrary.MarshalToString(m)
|
||||
should.Nil(err)
|
||||
should.Equal(string(bytes), output)
|
||||
}
|
||||
|
||||
func Test_encode_map_uint_keys(t *testing.T) {
|
||||
should := require.New(t)
|
||||
m := map[uint64]interface{}{
|
||||
uint64(1): "a",
|
||||
uint64(2): "a",
|
||||
uint64(4): "a",
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(m)
|
||||
should.Nil(err)
|
||||
|
||||
output, err := ConfigCompatibleWithStandardLibrary.MarshalToString(m)
|
||||
should.Nil(err)
|
||||
should.Equal(string(bytes), output)
|
||||
}
|
||||
|
||||
func Test_read_map_with_reader(t *testing.T) {
|
||||
should := require.New(t)
|
||||
input := `{"branch":"beta","change_log":"add the rows{10}","channel":"fros","create_time":"2017-06-13 16:39:08","firmware_list":"","md5":"80dee2bf7305bcf179582088e29fd7b9","note":{"CoreServices":{"md5":"d26975c0a8c7369f70ed699f2855cc2e","package_name":"CoreServices","version_code":"76","version_name":"1.0.76"},"FrDaemon":{"md5":"6b1f0626673200bc2157422cd2103f5d","package_name":"FrDaemon","version_code":"390","version_name":"1.0.390"},"FrGallery":{"md5":"90d767f0f31bcd3c1d27281ec979ba65","package_name":"FrGallery","version_code":"349","version_name":"1.0.349"},"FrLocal":{"md5":"f15a215b2c070a80a01f07bde4f219eb","package_name":"FrLocal","version_code":"791","version_name":"1.0.791"}},"pack_region_urls":{"CN":"https://s3.cn-north-1.amazonaws.com.cn/xxx-os/ttt_xxx_android_1.5.3.344.393.zip","default":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip","local":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip"},"pack_version":"1.5.3.344.393","pack_version_code":393,"region":"all","release_flag":0,"revision":62,"size":38966875,"status":3}`
|
||||
reader := strings.NewReader(input)
|
||||
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
|
||||
m1 := map[string]interface{}{}
|
||||
should.Nil(decoder.Decode(&m1))
|
||||
m2 := map[string]interface{}{}
|
||||
should.Nil(json.Unmarshal([]byte(input), &m2))
|
||||
should.Equal(m2, m1)
|
||||
should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"])
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Level1 struct {
|
||||
Hello []Level2
|
||||
}
|
||||
|
||||
type Level2 struct {
|
||||
World string
|
||||
}
|
||||
|
||||
func Test_nested(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
||||
l1 := Level1{}
|
||||
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
|
||||
switch l1Field {
|
||||
case "hello":
|
||||
l2Array := []Level2{}
|
||||
for iter.ReadArray() {
|
||||
l2 := Level2{}
|
||||
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
|
||||
switch l2Field {
|
||||
case "world":
|
||||
l2.World = iter.ReadString()
|
||||
default:
|
||||
iter.ReportError("bind l2", "unexpected field: "+l2Field)
|
||||
}
|
||||
}
|
||||
l2Array = append(l2Array, l2)
|
||||
}
|
||||
l1.Hello = l2Array
|
||||
default:
|
||||
iter.ReportError("bind l1", "unexpected field: "+l1Field)
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(l1, Level1{
|
||||
Hello: []Level2{
|
||||
{World: "value1"},
|
||||
{World: "value2"},
|
||||
},
|
||||
}) {
|
||||
t.Fatal(l1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_nested(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
||||
l1 := Level1{}
|
||||
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
|
||||
switch l1Field {
|
||||
case "hello":
|
||||
l1.Hello = readLevel1Hello(iter)
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readLevel1Hello(iter *Iterator) []Level2 {
|
||||
l2Array := make([]Level2, 0, 2)
|
||||
for iter.ReadArray() {
|
||||
l2 := Level2{}
|
||||
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
|
||||
switch l2Field {
|
||||
case "world":
|
||||
l2.World = iter.ReadString()
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
}
|
||||
l2Array = append(l2Array, l2)
|
||||
}
|
||||
return l2Array
|
||||
}
|
||||
|
||||
func Benchmark_json_nested(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
l1 := Level1{}
|
||||
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_read_null(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `null`)
|
||||
should.True(iter.ReadNil())
|
||||
iter = ParseString(ConfigDefault, `null`)
|
||||
should.Nil(iter.Read())
|
||||
iter = ParseString(ConfigDefault, `navy`)
|
||||
iter.Read()
|
||||
should.True(iter.Error != nil && iter.Error != io.EOF)
|
||||
iter = ParseString(ConfigDefault, `navy`)
|
||||
iter.ReadNil()
|
||||
should.True(iter.Error != nil && iter.Error != io.EOF)
|
||||
}
|
||||
|
||||
func Test_write_null(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(ConfigDefault, buf, 4096)
|
||||
stream.WriteNil()
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("null", buf.String())
|
||||
}
|
||||
|
||||
func Test_encode_null(t *testing.T) {
|
||||
should := require.New(t)
|
||||
str, err := MarshalToString(nil)
|
||||
should.Nil(err)
|
||||
should.Equal("null", str)
|
||||
}
|
||||
|
||||
func Test_decode_null_object_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[null,"a"]`)
|
||||
iter.ReadArray()
|
||||
if iter.ReadObject() != "" {
|
||||
t.FailNow()
|
||||
}
|
||||
iter.ReadArray()
|
||||
if iter.ReadString() != "a" {
|
||||
t.FailNow()
|
||||
}
|
||||
type TestObject struct {
|
||||
Field string
|
||||
}
|
||||
objs := []TestObject{}
|
||||
should.Nil(UnmarshalFromString("[null]", &objs))
|
||||
should.Len(objs, 1)
|
||||
}
|
||||
|
||||
func Test_decode_null_array_element(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[null,"a"]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.True(iter.ReadNil())
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal("a", iter.ReadString())
|
||||
}
|
||||
|
||||
func Test_decode_null_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
arr := []string{}
|
||||
should.Nil(UnmarshalFromString("null", &arr))
|
||||
should.Nil(arr)
|
||||
}
|
||||
|
||||
func Test_decode_null_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
arr := map[string]string{}
|
||||
should.Nil(UnmarshalFromString("null", &arr))
|
||||
should.Nil(arr)
|
||||
}
|
||||
|
||||
func Test_decode_null_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[null,"a"]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal("", iter.ReadString())
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal("a", iter.ReadString())
|
||||
}
|
||||
|
||||
func Test_decode_null_skip(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `[null,"a"]`)
|
||||
iter.ReadArray()
|
||||
iter.Skip()
|
||||
iter.ReadArray()
|
||||
if iter.ReadString() != "a" {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encode_nil_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type Ttest map[string]string
|
||||
var obj1 Ttest
|
||||
output, err := json.Marshal(obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = json.Marshal(&obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = Marshal(obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = Marshal(&obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
}
|
||||
|
||||
func Test_encode_nil_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type Ttest []string
|
||||
var obj1 Ttest
|
||||
output, err := json.Marshal(obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = json.Marshal(&obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = Marshal(obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
output, err = Marshal(&obj1)
|
||||
should.Nil(err)
|
||||
should.Equal("null", string(output))
|
||||
}
|
@ -1,591 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_empty_object(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `{}`)
|
||||
field := iter.ReadObject()
|
||||
should.Equal("", field)
|
||||
iter = ParseString(ConfigDefault, `{}`)
|
||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||
should.FailNow("should not call")
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func Test_one_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `{"a": "stream"}`)
|
||||
field := iter.ReadObject()
|
||||
should.Equal("a", field)
|
||||
value := iter.ReadString()
|
||||
should.Equal("stream", value)
|
||||
field = iter.ReadObject()
|
||||
should.Equal("", field)
|
||||
iter = ParseString(ConfigDefault, `{"a": "stream"}`)
|
||||
should.True(iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||
should.Equal("a", field)
|
||||
iter.Skip()
|
||||
return true
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
func Test_two_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `{ "a": "stream" , "c": "d" }`)
|
||||
field := iter.ReadObject()
|
||||
should.Equal("a", field)
|
||||
value := iter.ReadString()
|
||||
should.Equal("stream", value)
|
||||
field = iter.ReadObject()
|
||||
should.Equal("c", field)
|
||||
value = iter.ReadString()
|
||||
should.Equal("d", value)
|
||||
field = iter.ReadObject()
|
||||
should.Equal("", field)
|
||||
iter = ParseString(ConfigDefault, `{"field1": "1", "field2": 2}`)
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
switch field {
|
||||
case "field1":
|
||||
iter.ReadString()
|
||||
case "field2":
|
||||
iter.ReadInt64()
|
||||
default:
|
||||
iter.ReportError("bind object", "unexpected field")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_object_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 []int
|
||||
Field2 []int
|
||||
}
|
||||
any := Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
|
||||
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
should.Contains(any.Keys(), "Field2")
|
||||
should.NotContains(any.Keys(), "Field3")
|
||||
|
||||
//should.Contains(any.GetObject()["Field1"].GetArray()[0], 1)
|
||||
}
|
||||
|
||||
func Test_write_object(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096)
|
||||
stream.WriteObjectStart()
|
||||
stream.WriteObjectField("hello")
|
||||
stream.WriteInt(1)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("world")
|
||||
stream.WriteInt(2)
|
||||
stream.WriteObjectEnd()
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
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) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
}
|
||||
obj := TestObject{}
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{}`, str)
|
||||
}
|
||||
|
||||
func Test_write_val_one_field_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 string `json:"field-1"`
|
||||
}
|
||||
obj := TestObject{"hello"}
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-1":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_mixed(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type AA struct {
|
||||
ID int `json:"id"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
aa := AA{}
|
||||
err := UnmarshalFromString(` {"id":1, "payload":{"account":"123","password":"456"}}`, &aa)
|
||||
should.Nil(err)
|
||||
should.Equal(1, aa.ID)
|
||||
should.Equal("123", aa.Payload["account"])
|
||||
}
|
||||
|
||||
func Test_omit_empty(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 string `json:"field-1,omitempty"`
|
||||
Field2 string `json:"field-2,omitempty"`
|
||||
Field3 string `json:"field-3,omitempty"`
|
||||
}
|
||||
obj := TestObject{}
|
||||
obj.Field2 = "hello"
|
||||
str, err := MarshalToString(&obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-2":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_ignore_field_on_not_valid_type(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 string `json:"field-1,omitempty"`
|
||||
Field2 func() `json:"-"`
|
||||
}
|
||||
obj := TestObject{}
|
||||
obj.Field1 = "hello world"
|
||||
obj.Field2 = func() {}
|
||||
str, err := MarshalToString(&obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-1":"hello world"}`, str)
|
||||
}
|
||||
|
||||
func Test_recursive_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 string
|
||||
Me *TestObject
|
||||
}
|
||||
obj := TestObject{}
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"Field1":""`)
|
||||
should.Contains(str, `"Me":null`)
|
||||
err = UnmarshalFromString(str, &obj)
|
||||
should.Nil(err)
|
||||
}
|
||||
|
||||
func Test_encode_anonymous_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field string
|
||||
}
|
||||
str, err := MarshalToString(struct {
|
||||
TestObject
|
||||
Field int
|
||||
}{
|
||||
Field: 100,
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":100}`, str)
|
||||
}
|
||||
|
||||
func Test_decode_anonymous_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type Inner struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type Outer struct {
|
||||
Inner
|
||||
}
|
||||
var outer Outer
|
||||
j := []byte("{\"key\":\"value\"}")
|
||||
should.Nil(Unmarshal(j, &outer))
|
||||
should.Equal("value", outer.Key)
|
||||
}
|
||||
|
||||
func Test_multiple_level_anonymous_struct(t *testing.T) {
|
||||
type Level1 struct {
|
||||
Field1 string
|
||||
}
|
||||
type Level2 struct {
|
||||
Level1
|
||||
Field2 string
|
||||
}
|
||||
type Level3 struct {
|
||||
Level2
|
||||
Field3 string
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := Level3{Level2{Level1{"1"}, "2"}, "3"}
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field1":"1","Field2":"2","Field3":"3"}`, output)
|
||||
}
|
||||
|
||||
func Test_multiple_level_anonymous_struct_with_ptr(t *testing.T) {
|
||||
type Level1 struct {
|
||||
Field1 string
|
||||
Field2 string
|
||||
Field4 string
|
||||
}
|
||||
type Level2 struct {
|
||||
*Level1
|
||||
Field2 string
|
||||
Field3 string
|
||||
}
|
||||
type Level3 struct {
|
||||
*Level2
|
||||
Field3 string
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := Level3{&Level2{&Level1{"1", "", "4"}, "2", ""}, "3"}
|
||||
output, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Contains(output, `"Field1":"1"`)
|
||||
should.Contains(output, `"Field2":"2"`)
|
||||
should.Contains(output, `"Field3":"3"`)
|
||||
should.Contains(output, `"Field4":"4"`)
|
||||
}
|
||||
|
||||
func Test_shadow_struct_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type omit *struct{}
|
||||
type CacheItem struct {
|
||||
Key string `json:"key"`
|
||||
MaxAge int `json:"cacheAge"`
|
||||
}
|
||||
output, err := MarshalToString(struct {
|
||||
*CacheItem
|
||||
|
||||
// Omit bad keys
|
||||
OmitMaxAge omit `json:"cacheAge,omitempty"`
|
||||
|
||||
// Add nice keys
|
||||
MaxAge int `json:"max_age"`
|
||||
}{
|
||||
CacheItem: &CacheItem{
|
||||
Key: "value",
|
||||
MaxAge: 100,
|
||||
},
|
||||
MaxAge: 20,
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Contains(output, `"key":"value"`)
|
||||
should.Contains(output, `"max_age":20`)
|
||||
}
|
||||
|
||||
func Test_embedded_order(t *testing.T) {
|
||||
type A struct {
|
||||
Field2 string
|
||||
}
|
||||
|
||||
type C struct {
|
||||
Field5 string
|
||||
}
|
||||
|
||||
type B struct {
|
||||
Field4 string
|
||||
C
|
||||
Field6 string
|
||||
}
|
||||
|
||||
type TestObject struct {
|
||||
Field1 string
|
||||
A
|
||||
Field3 string
|
||||
B
|
||||
Field7 string
|
||||
}
|
||||
should := require.New(t)
|
||||
s := TestObject{}
|
||||
output, err := MarshalToString(s)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field1":"","Field2":"","Field3":"","Field4":"","Field5":"","Field6":"","Field7":""}`, output)
|
||||
}
|
||||
|
||||
func Test_decode_nested(t *testing.T) {
|
||||
type StructOfString struct {
|
||||
Field1 string
|
||||
Field2 string
|
||||
}
|
||||
iter := ParseString(ConfigDefault, `[{"field1": "hello"}, null, {"field2": "world"}]`)
|
||||
slice := []*StructOfString{}
|
||||
iter.ReadVal(&slice)
|
||||
if len(slice) != 3 {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(len(slice))
|
||||
}
|
||||
if slice[0].Field1 != "hello" {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(slice[0])
|
||||
}
|
||||
if slice[1] != nil {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(slice[1])
|
||||
}
|
||||
if slice[2].Field2 != "world" {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(slice[2])
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_encode_optional_int_pointer(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var ptr *int
|
||||
str, err := MarshalToString(ptr)
|
||||
should.Nil(err)
|
||||
should.Equal("null", str)
|
||||
val := 100
|
||||
ptr = &val
|
||||
str, err = MarshalToString(ptr)
|
||||
should.Nil(err)
|
||||
should.Equal("100", str)
|
||||
}
|
||||
|
||||
func Test_decode_struct_with_optional_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 *string
|
||||
Field2 *string
|
||||
}
|
||||
obj := TestObject{}
|
||||
UnmarshalFromString(`{"field1": null, "field2": "world"}`, &obj)
|
||||
should.Nil(obj.Field1)
|
||||
should.Equal("world", *obj.Field2)
|
||||
}
|
||||
|
||||
func Test_encode_struct_with_optional_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 *string
|
||||
Field2 *string
|
||||
}
|
||||
obj := TestObject{}
|
||||
world := "world"
|
||||
obj.Field2 = &world
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"Field1":null`)
|
||||
should.Contains(str, `"Field2":"world"`)
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_json_RawMessage(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var data json.RawMessage
|
||||
should.Nil(Unmarshal([]byte(`[1,2,3]`), &data))
|
||||
should.Equal(`[1,2,3]`, string(data))
|
||||
str, err := MarshalToString(data)
|
||||
should.Nil(err)
|
||||
should.Equal(`[1,2,3]`, str)
|
||||
}
|
||||
|
||||
func Test_jsoniter_RawMessage(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var data RawMessage
|
||||
should.Nil(Unmarshal([]byte(`[1,2,3]`), &data))
|
||||
should.Equal(`[1,2,3]`, string(data))
|
||||
str, err := MarshalToString(data)
|
||||
should.Nil(err)
|
||||
should.Equal(`[1,2,3]`, str)
|
||||
}
|
||||
|
||||
func Test_json_RawMessage_in_struct(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field1 string
|
||||
Field2 json.RawMessage
|
||||
}
|
||||
should := require.New(t)
|
||||
var data TestObject
|
||||
should.Nil(Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data))
|
||||
should.Equal(` [1,2,3]`, string(data.Field2))
|
||||
should.Equal(`hello`, data.Field1)
|
||||
}
|
||||
|
||||
func Test_decode_map_of_raw_message(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type RawMap map[string]*json.RawMessage
|
||||
b := []byte("{\"test\":[{\"key\":\"value\"}]}")
|
||||
var rawMap RawMap
|
||||
should.Nil(Unmarshal(b, &rawMap))
|
||||
should.Equal(`[{"key":"value"}]`, string(*rawMap["test"]))
|
||||
type Inner struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
var inner []Inner
|
||||
Unmarshal(*rawMap["test"], &inner)
|
||||
should.Equal("value", inner[0].Key)
|
||||
}
|
||||
|
||||
func Test_encode_map_of_raw_message(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type RawMap map[string]*json.RawMessage
|
||||
value := json.RawMessage("[]")
|
||||
rawMap := RawMap{"hello": &value}
|
||||
output, err := MarshalToString(rawMap)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"hello":[]}`, output)
|
||||
}
|
||||
|
||||
func Test_encode_map_of_jsoniter_raw_message(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type RawMap map[string]*RawMessage
|
||||
value := RawMessage("[]")
|
||||
rawMap := RawMap{"hello": &value}
|
||||
output, err := MarshalToString(rawMap)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"hello":[]}`, output)
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_reader_and_load_more(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
CreatedAt time.Time
|
||||
}
|
||||
reader := strings.NewReader(`
|
||||
{
|
||||
"agency": null,
|
||||
"candidateId": 0,
|
||||
"candidate": "Blah Blah",
|
||||
"bookingId": 0,
|
||||
"shiftId": 1,
|
||||
"shiftTypeId": 0,
|
||||
"shift": "Standard",
|
||||
"bonus": 0,
|
||||
"bonusNI": 0,
|
||||
"days": [],
|
||||
"totalHours": 27,
|
||||
"expenses": [],
|
||||
"weekEndingDateSystem": "2016-10-09",
|
||||
"weekEndingDateClient": "2016-10-09",
|
||||
"submittedAt": null,
|
||||
"submittedById": null,
|
||||
"approvedAt": "2016-10-10T18:38:04Z",
|
||||
"approvedById": 0,
|
||||
"authorisedAt": "2016-10-10T18:38:04Z",
|
||||
"authorisedById": 0,
|
||||
"invoicedAt": "2016-10-10T20:00:00Z",
|
||||
"revokedAt": null,
|
||||
"revokedById": null,
|
||||
"revokeReason": null,
|
||||
"rejectedAt": null,
|
||||
"rejectedById": null,
|
||||
"rejectReasonCode": null,
|
||||
"rejectReason": null,
|
||||
"createdAt": "2016-10-03T00:00:00Z",
|
||||
"updatedAt": "2016-11-09T10:26:13Z",
|
||||
"updatedById": null,
|
||||
"overrides": [],
|
||||
"bookingApproverId": null,
|
||||
"bookingApprover": null,
|
||||
"status": "approved"
|
||||
}
|
||||
`)
|
||||
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
|
||||
obj := TestObject{}
|
||||
should.Nil(decoder.Decode(&obj))
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_reflect_str(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `"hello"`)
|
||||
str := ""
|
||||
iter.ReadVal(&str)
|
||||
if str != "hello" {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(str)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_ptr_str(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `"hello"`)
|
||||
var str *string
|
||||
iter.ReadVal(&str)
|
||||
if *str != "hello" {
|
||||
t.Fatal(str)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_int(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := int(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_int8(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := int8(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_int16(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := int16(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_int32(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := int32(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_int64(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := int64(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_uint(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := uint(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_uint8(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := uint8(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_uint16(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := uint16(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_uint32(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := uint32(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_uint64(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := uint64(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_byte(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `123`)
|
||||
val := byte(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 123 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_float32(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `1.23`)
|
||||
val := float32(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 1.23 {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_float64(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `1.23`)
|
||||
val := float64(0)
|
||||
iter.ReadVal(&val)
|
||||
if val != 1.23 {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_reflect_bool(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `true`)
|
||||
val := false
|
||||
iter.ReadVal(&val)
|
||||
if val != true {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
// +build go1.8
|
||||
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_string(t *testing.T) {
|
||||
badInputs := []string{
|
||||
``,
|
||||
`"`,
|
||||
`"\"`,
|
||||
`"\\\"`,
|
||||
"\"\n\"",
|
||||
}
|
||||
for i := 0; i < 32; i++ {
|
||||
// control characters are invalid
|
||||
badInputs = append(badInputs, string([]byte{'"', byte(i), '"'}))
|
||||
}
|
||||
|
||||
for _, input := range badInputs {
|
||||
testReadString(t, input, "", true, "json.Unmarshal", json.Unmarshal)
|
||||
testReadString(t, input, "", true, "jsoniter.Unmarshal", Unmarshal)
|
||||
testReadString(t, input, "", true, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", ConfigCompatibleWithStandardLibrary.Unmarshal)
|
||||
}
|
||||
|
||||
goodInputs := []struct {
|
||||
input string
|
||||
expectValue string
|
||||
}{
|
||||
{`""`, ""},
|
||||
{`"a"`, "a"},
|
||||
{`null`, ""},
|
||||
{`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"},
|
||||
}
|
||||
|
||||
for _, tc := range goodInputs {
|
||||
testReadString(t, tc.input, tc.expectValue, false, "json.Unmarshal", json.Unmarshal)
|
||||
testReadString(t, tc.input, tc.expectValue, false, "jsoniter.Unmarshal", Unmarshal)
|
||||
testReadString(t, tc.input, tc.expectValue, false, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", ConfigCompatibleWithStandardLibrary.Unmarshal)
|
||||
}
|
||||
}
|
||||
|
||||
func testReadString(t *testing.T, input string, expectValue string, expectError bool, marshalerName string, marshaler func([]byte, interface{}) error) {
|
||||
var value string
|
||||
err := marshaler([]byte(input), &value)
|
||||
if expectError != (err != nil) {
|
||||
t.Errorf("%q: %s: expected error %v, got %v", input, marshalerName, expectError, err)
|
||||
return
|
||||
}
|
||||
if value != expectValue {
|
||||
t.Errorf("%q: %s: expected %q, got %q", input, marshalerName, expectValue, value)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_normal_string(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
`"0123456789012345678901234567890123456789"`: `0123456789012345678901234567890123456789`,
|
||||
`""`: ``,
|
||||
`"hello"`: `hello`,
|
||||
}
|
||||
for input, output := range cases {
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, input)
|
||||
should.Equal(output, iter.ReadString())
|
||||
})
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
|
||||
should.Equal(output, iter.ReadString())
|
||||
})
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, input)
|
||||
should.Equal(output, string(iter.ReadStringAsSlice()))
|
||||
})
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
|
||||
should.Equal(output, string(iter.ReadStringAsSlice()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_exotic_string(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
`"hel\"lo"`: `hel"lo`,
|
||||
`"hel\\\/lo"`: `hel\/lo`,
|
||||
`"hel\\blo"`: `hel\blo`,
|
||||
`"hel\\\blo"`: "hel\\\blo",
|
||||
`"hel\\nlo"`: `hel\nlo`,
|
||||
`"hel\\\nlo"`: "hel\\\nlo",
|
||||
`"hel\\tlo"`: `hel\tlo`,
|
||||
`"hel\\flo"`: `hel\flo`,
|
||||
`"hel\\\flo"`: "hel\\\flo",
|
||||
`"hel\\\rlo"`: "hel\\\rlo",
|
||||
`"hel\\\tlo"`: "hel\\\tlo",
|
||||
`"\u4e2d\u6587"`: "中文",
|
||||
`"\ud83d\udc4a"`: "\xf0\x9f\x91\x8a", // surrogate
|
||||
}
|
||||
for input, output := range cases {
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, input)
|
||||
should.Equal(output, iter.ReadString())
|
||||
})
|
||||
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
|
||||
should.Equal(output, iter.ReadString())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_string_as_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `"hello"`)
|
||||
should.Equal("hello", iter.Read())
|
||||
}
|
||||
|
||||
func Test_write_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
str, err := MarshalToString("hello")
|
||||
should.Equal(`"hello"`, str)
|
||||
should.Nil(err)
|
||||
str, err = MarshalToString(`hel"lo`)
|
||||
should.Equal(`"hel\"lo"`, str)
|
||||
should.Nil(err)
|
||||
}
|
||||
|
||||
func Test_write_val_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(ConfigDefault, buf, 4096)
|
||||
stream.WriteVal("hello")
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal(`"hello"`, buf.String())
|
||||
}
|
||||
|
||||
func Test_decode_slash(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var obj interface{}
|
||||
should.NotNil(json.Unmarshal([]byte("\\"), &obj))
|
||||
should.NotNil(UnmarshalFromString("\\", &obj))
|
||||
}
|
||||
|
||||
func Test_html_escape(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := json.Marshal(`>`)
|
||||
should.Nil(err)
|
||||
should.Equal(`"\u003e"`, string(output))
|
||||
output, err = ConfigCompatibleWithStandardLibrary.Marshal(`>`)
|
||||
should.Nil(err)
|
||||
should.Equal(`"\u003e"`, string(output))
|
||||
type MyString string
|
||||
output, err = ConfigCompatibleWithStandardLibrary.Marshal(MyString(`>`))
|
||||
should.Nil(err)
|
||||
should.Equal(`"\u003e"`, string(output))
|
||||
}
|
||||
|
||||
func Test_string_encode_with_std(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for i := 0; i < utf8.RuneSelf; i++ {
|
||||
input := string([]byte{byte(i)})
|
||||
stdOutputBytes, err := json.Marshal(input)
|
||||
should.Nil(err)
|
||||
stdOutput := string(stdOutputBytes)
|
||||
jsoniterOutputBytes, err := ConfigCompatibleWithStandardLibrary.Marshal(input)
|
||||
should.Nil(err)
|
||||
jsoniterOutput := string(jsoniterOutputBytes)
|
||||
should.Equal(stdOutput, jsoniterOutput)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_unicode(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
||||
should.Equal(`{"a":"数字山谷"}`, output)
|
||||
output, _ = Config{EscapeHTML: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
||||
should.Equal(`{"a":"数字山谷"}`, output)
|
||||
}
|
||||
|
||||
func Test_unicode_and_escape(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := MarshalToString(`"数字山谷"`)
|
||||
should.Nil(err)
|
||||
should.Equal(`"\"数字山谷\""`, output)
|
||||
output, err = ConfigFastest.MarshalToString(`"数字山谷"`)
|
||||
should.Nil(err)
|
||||
should.Equal(`"\"数字山谷\""`, output)
|
||||
}
|
||||
|
||||
func Test_unsafe_unicode(t *testing.T) {
|
||||
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
should := require.New(t)
|
||||
output, err := ConfigDefault.MarshalToString("he\u2029\u2028he")
|
||||
should.Nil(err)
|
||||
should.Equal(`"he\u2029\u2028he"`, output)
|
||||
output, err = ConfigFastest.MarshalToString("he\u2029\u2028he")
|
||||
should.Nil(err)
|
||||
should.Equal("\"he\u2029\u2028he\"", output)
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_unicode(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(ConfigDefault, `"\ud83d\udc4a"`)
|
||||
iter.ReadString()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_ascii(b *testing.B) {
|
||||
iter := NewIterator(ConfigDefault)
|
||||
input := []byte(`"hello, world! hello, world!"`)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(input)
|
||||
iter.ReadString()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_string_as_bytes(b *testing.B) {
|
||||
iter := ParseString(ConfigDefault, `"hello, world!"`)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(iter.buf)
|
||||
iter.ReadStringAsSlice()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_unicode(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := ""
|
||||
json.Unmarshal([]byte(`"\ud83d\udc4a"`), &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_ascii(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := ""
|
||||
json.Unmarshal([]byte(`"hello"`), &result)
|
||||
}
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
package jsoniter
|
||||
package misc_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_empty_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
|
||||
cont := iter.ReadArray()
|
||||
should.False(cont)
|
||||
iter = ParseString(ConfigDefault, `[]`)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
|
||||
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
|
||||
should.FailNow("should not call")
|
||||
return true
|
||||
})
|
||||
@ -21,12 +23,12 @@ func Test_empty_array(t *testing.T) {
|
||||
|
||||
func Test_one_element(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[1]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(1, iter.ReadInt())
|
||||
should.False(iter.ReadArray())
|
||||
iter = ParseString(ConfigDefault, `[1]`)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
|
||||
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
|
||||
should.Equal(1, iter.ReadInt())
|
||||
return true
|
||||
})
|
||||
@ -34,18 +36,18 @@ func Test_one_element(t *testing.T) {
|
||||
|
||||
func Test_two_elements(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(ConfigDefault, `[1,2]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1,2]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(int64(1), iter.ReadInt64())
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(int64(2), iter.ReadInt64())
|
||||
should.False(iter.ReadArray())
|
||||
iter = ParseString(ConfigDefault, `[1,2]`)
|
||||
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[1,2]`)
|
||||
should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
|
||||
}
|
||||
|
||||
func Test_whitespace_in_head(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, ` [1]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, ` [1]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
@ -56,7 +58,7 @@ func Test_whitespace_in_head(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_whitespace_after_array_start(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `[ 1]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[ 1]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
@ -67,7 +69,7 @@ func Test_whitespace_after_array_start(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_whitespace_before_array_end(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `[1 ]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1 ]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
@ -82,7 +84,7 @@ func Test_whitespace_before_array_end(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_whitespace_before_comma(t *testing.T) {
|
||||
iter := ParseString(ConfigDefault, `[1 ,2]`)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1 ,2]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
@ -106,7 +108,7 @@ func Test_whitespace_before_comma(t *testing.T) {
|
||||
func Test_write_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096)
|
||||
stream := jsoniter.NewStream(jsoniter.Config{IndentionStep: 2}.Froze(), buf, 4096)
|
||||
stream.WriteArrayStart()
|
||||
stream.WriteInt(1)
|
||||
stream.WriteMore()
|
||||
@ -120,7 +122,7 @@ func Test_write_array(t *testing.T) {
|
||||
func Test_write_val_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []int{1, 2, 3}
|
||||
str, err := MarshalToString(&val)
|
||||
str, err := jsoniter.MarshalToString(&val)
|
||||
should.Nil(err)
|
||||
should.Equal("[1,2,3]", str)
|
||||
}
|
||||
@ -128,7 +130,7 @@ func Test_write_val_array(t *testing.T) {
|
||||
func Test_write_val_empty_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []int{}
|
||||
str, err := MarshalToString(val)
|
||||
str, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal("[]", str)
|
||||
}
|
||||
@ -140,7 +142,7 @@ func Test_write_array_of_interface_in_struct(t *testing.T) {
|
||||
Field2 string
|
||||
}
|
||||
val := TestObject{[]interface{}{1, 2}, ""}
|
||||
str, err := MarshalToString(val)
|
||||
str, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"Field":[1,2]`)
|
||||
should.Contains(str, `"Field2":""`)
|
||||
@ -151,18 +153,61 @@ func Test_encode_byte_array(t *testing.T) {
|
||||
bytes, err := json.Marshal([]byte{1, 2, 3})
|
||||
should.Nil(err)
|
||||
should.Equal(`"AQID"`, string(bytes))
|
||||
bytes, err = Marshal([]byte{1, 2, 3})
|
||||
bytes, err = jsoniter.Marshal([]byte{1, 2, 3})
|
||||
should.Nil(err)
|
||||
should.Equal(`"AQID"`, string(bytes))
|
||||
}
|
||||
|
||||
func Test_decode_byte_array(t *testing.T) {
|
||||
func Test_encode_empty_byte_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
bytes, err := json.Marshal([]byte{})
|
||||
should.Nil(err)
|
||||
should.Equal(`""`, string(bytes))
|
||||
bytes, err = jsoniter.Marshal([]byte{})
|
||||
should.Nil(err)
|
||||
should.Equal(`""`, string(bytes))
|
||||
}
|
||||
|
||||
func Test_encode_nil_byte_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var nilSlice []byte
|
||||
bytes, err := json.Marshal(nilSlice)
|
||||
should.Nil(err)
|
||||
should.Equal(`null`, string(bytes))
|
||||
bytes, err = jsoniter.Marshal(nilSlice)
|
||||
should.Nil(err)
|
||||
should.Equal(`null`, string(bytes))
|
||||
}
|
||||
|
||||
func Test_decode_byte_array_from_base64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
data := []byte{}
|
||||
err := json.Unmarshal([]byte(`"AQID"`), &data)
|
||||
should.Nil(err)
|
||||
should.Equal([]byte{1, 2, 3}, data)
|
||||
err = Unmarshal([]byte(`"AQID"`), &data)
|
||||
err = jsoniter.Unmarshal([]byte(`"AQID"`), &data)
|
||||
should.Nil(err)
|
||||
should.Equal([]byte{1, 2, 3}, data)
|
||||
}
|
||||
|
||||
func Test_decode_byte_array_from_base64_with_newlines(t *testing.T) {
|
||||
should := require.New(t)
|
||||
data := []byte{}
|
||||
err := json.Unmarshal([]byte(`"A\rQ\nID"`), &data)
|
||||
should.Nil(err)
|
||||
should.Equal([]byte{1, 2, 3}, data)
|
||||
err = jsoniter.Unmarshal([]byte(`"A\rQ\nID"`), &data)
|
||||
should.Nil(err)
|
||||
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 = jsoniter.Unmarshal([]byte(`[1,2,3]`), &data)
|
||||
should.Nil(err)
|
||||
should.Equal([]byte{1, 2, 3}, data)
|
||||
}
|
||||
@ -170,21 +215,21 @@ func Test_decode_byte_array(t *testing.T) {
|
||||
func Test_decode_slice(t *testing.T) {
|
||||
should := require.New(t)
|
||||
slice := make([]string, 0, 5)
|
||||
UnmarshalFromString(`["hello", "world"]`, &slice)
|
||||
jsoniter.UnmarshalFromString(`["hello", "world"]`, &slice)
|
||||
should.Equal([]string{"hello", "world"}, slice)
|
||||
}
|
||||
|
||||
func Test_decode_large_slice(t *testing.T) {
|
||||
should := require.New(t)
|
||||
slice := make([]int, 0, 1)
|
||||
UnmarshalFromString(`[1,2,3,4,5,6,7,8,9]`, &slice)
|
||||
jsoniter.UnmarshalFromString(`[1,2,3,4,5,6,7,8,9]`, &slice)
|
||||
should.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, slice)
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_array(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
input := []byte(`[1,2,3,4,5,6,7,8,9]`)
|
||||
iter := ParseBytes(ConfigDefault, input)
|
||||
iter := jsoniter.ParseBytes(jsoniter.ConfigDefault, input)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(input)
|
47
misc_tests/jsoniter_bool_test.go
Normal file
47
misc_tests/jsoniter_bool_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package misc_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_true(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `true`)
|
||||
should.True(iter.ReadBool())
|
||||
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `true`)
|
||||
should.Equal(true, iter.Read())
|
||||
}
|
||||
|
||||
func Test_false(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `false`)
|
||||
should.False(iter.ReadBool())
|
||||
}
|
||||
|
||||
func Test_write_true_false(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
|
||||
stream.WriteTrue()
|
||||
stream.WriteFalse()
|
||||
stream.WriteBool(false)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("truefalsefalse", buf.String())
|
||||
}
|
||||
|
||||
func Test_write_val_bool(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
|
||||
stream.WriteVal(true)
|
||||
should.Equal(stream.Buffered(), 4)
|
||||
stream.Flush()
|
||||
should.Equal(stream.Buffered(), 0)
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("true", buf.String())
|
||||
}
|
116
misc_tests/jsoniter_float_test.go
Normal file
116
misc_tests/jsoniter_float_test.go
Normal file
@ -0,0 +1,116 @@
|
||||
package misc_tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_big_float(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`)
|
||||
val := iter.ReadBigFloat()
|
||||
val64, _ := val.Float64()
|
||||
should.Equal(12.3, val64)
|
||||
}
|
||||
|
||||
func Test_read_big_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`)
|
||||
val := iter.ReadBigInt()
|
||||
should.NotNil(val)
|
||||
should.Equal(`92233720368547758079223372036854775807`, val.String())
|
||||
}
|
||||
|
||||
func Test_read_float_as_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`)
|
||||
should.Equal(float64(12.3), iter.Read())
|
||||
}
|
||||
|
||||
func Test_wrap_float(t *testing.T) {
|
||||
should := require.New(t)
|
||||
str, err := jsoniter.MarshalToString(jsoniter.WrapFloat64(12.3))
|
||||
should.Nil(err)
|
||||
should.Equal("12.3", str)
|
||||
}
|
||||
|
||||
func Test_read_float64_cursor(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, "[1.23456789\n,2,3]")
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(1.23456789, iter.Read())
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(float64(2), iter.Read())
|
||||
}
|
||||
|
||||
func Test_read_float_scientific(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var obj interface{}
|
||||
should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj))
|
||||
should.Equal(float64(10), obj)
|
||||
should.NoError(json.Unmarshal([]byte(`1e1`), &obj))
|
||||
should.Equal(float64(10), obj)
|
||||
should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
|
||||
should.Equal(float64(10), obj)
|
||||
should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj))
|
||||
should.Equal(float64(10), obj)
|
||||
}
|
||||
|
||||
func Test_lossy_float_marshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
api := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze()
|
||||
output, err := api.MarshalToString(float64(0.1234567))
|
||||
should.Nil(err)
|
||||
should.Equal("0.123457", output)
|
||||
output, err = api.MarshalToString(float32(0.1234567))
|
||||
should.Nil(err)
|
||||
should.Equal("0.123457", output)
|
||||
}
|
||||
|
||||
func Test_read_number(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`)
|
||||
val := iter.ReadNumber()
|
||||
should.Equal(`92233720368547758079223372036854775807`, string(val))
|
||||
}
|
||||
|
||||
func Test_encode_inf(t *testing.T) {
|
||||
should := require.New(t)
|
||||
_, err := json.Marshal(math.Inf(1))
|
||||
should.Error(err)
|
||||
_, err = jsoniter.Marshal(float32(math.Inf(1)))
|
||||
should.Error(err)
|
||||
_, err = jsoniter.Marshal(math.Inf(-1))
|
||||
should.Error(err)
|
||||
}
|
||||
|
||||
func Test_encode_nan(t *testing.T) {
|
||||
should := require.New(t)
|
||||
_, err := json.Marshal(math.NaN())
|
||||
should.Error(err)
|
||||
_, err = jsoniter.Marshal(float32(math.NaN()))
|
||||
should.Error(err)
|
||||
_, err = jsoniter.Marshal(math.NaN())
|
||||
should.Error(err)
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_float(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
input := []byte(`1.1123,`)
|
||||
iter := jsoniter.NewIterator(jsoniter.ConfigDefault)
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(input)
|
||||
iter.ReadFloat64()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_float(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := float64(0)
|
||||
json.Unmarshal([]byte(`1.1`), &result)
|
||||
}
|
||||
}
|
192
misc_tests/jsoniter_int_test.go
Normal file
192
misc_tests/jsoniter_int_test.go
Normal file
@ -0,0 +1,192 @@
|
||||
// +build go1.8
|
||||
|
||||
package misc_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_uint64_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, ",")
|
||||
iter.ReadUint64()
|
||||
should.NotNil(iter.Error)
|
||||
}
|
||||
|
||||
func Test_read_int32_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
input := `[123,456,789]`
|
||||
val := make([]int32, 0)
|
||||
jsoniter.UnmarshalFromString(input, &val)
|
||||
should.Equal(3, len(val))
|
||||
}
|
||||
|
||||
func Test_read_int64_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
input := `[123,456,789]`
|
||||
val := make([]int64, 0)
|
||||
jsoniter.UnmarshalFromString(input, &val)
|
||||
should.Equal(3, len(val))
|
||||
}
|
||||
|
||||
func Test_wrap_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
str, err := jsoniter.MarshalToString(jsoniter.WrapInt64(100))
|
||||
should.Nil(err)
|
||||
should.Equal("100", str)
|
||||
}
|
||||
|
||||
func Test_write_val_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
|
||||
stream.WriteVal(1001)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("1001", buf.String())
|
||||
}
|
||||
|
||||
func Test_write_val_int_ptr(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
|
||||
val := 1001
|
||||
stream.WriteVal(&val)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("1001", buf.String())
|
||||
}
|
||||
|
||||
func Test_float_as_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var i int
|
||||
should.NotNil(jsoniter.Unmarshal([]byte(`1.1`), &i))
|
||||
}
|
||||
|
||||
// chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
|
||||
// It simulates chunked data on from HTTP server, which is commonly used by net/http package.
|
||||
type chunkedData struct {
|
||||
chunkSize int
|
||||
data []byte
|
||||
head int
|
||||
}
|
||||
|
||||
// Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
|
||||
func (c *chunkedData) Read(p []byte) (n int, err error) {
|
||||
to := c.head + int(rand.Int31n(int32(c.chunkSize))+1)
|
||||
|
||||
// copy does not copy more data then p can consume
|
||||
n = copy(p, c.data[c.head:to])
|
||||
c.head = c.head + n
|
||||
if c.head >= len(c.data) {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where:
|
||||
// - it reads data from io.Reader,
|
||||
// - expected value is 0 (zero)
|
||||
// - Iterator.tail == Iterator.head
|
||||
// - Iterator.tail < len(Iterator.buf)
|
||||
// - value in buffer after Iterator.tail is presented from previous read and has '.' character.
|
||||
func TestIterator_ReadInt_chunkedInput(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
data := &chunkedData{
|
||||
data: jsonFloatIntArray(t, 10),
|
||||
}
|
||||
|
||||
// because this test is rely on randomness of chunkedData, we are doing multiple iterations to
|
||||
// be sure, that we can hit a required case.
|
||||
for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ {
|
||||
data.head = 0
|
||||
|
||||
iter := jsoniter.Parse(jsoniter.ConfigDefault, data, data.chunkSize)
|
||||
i := 0
|
||||
for iter.ReadArray() {
|
||||
// every even item is float, let's just skip it.
|
||||
if i%2 == 0 {
|
||||
iter.Skip()
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
should.Zero(iter.ReadInt())
|
||||
should.NoError(iter.Error)
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// jsonFloatIntArray generates JSON array where every
|
||||
// - even item is float 0.1
|
||||
// - odd item is integer 0
|
||||
//
|
||||
// [0.1, 0, 0.1, 0]
|
||||
func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte {
|
||||
t.Helper()
|
||||
numbers := make([]jsoniter.Any, numberOfItems)
|
||||
for i := range numbers {
|
||||
switch i % 2 {
|
||||
case 0:
|
||||
numbers[i] = jsoniter.WrapFloat64(0.1)
|
||||
default:
|
||||
numbers[i] = jsoniter.WrapInt64(0)
|
||||
}
|
||||
}
|
||||
|
||||
fixture, err := jsoniter.ConfigFastest.Marshal(numbers)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
require.NoError(
|
||||
t,
|
||||
json.Compact(b, fixture),
|
||||
"json should be compactable",
|
||||
)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_encode_int(b *testing.B) {
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, ioutil.Discard, 64)
|
||||
for n := 0; n < b.N; n++ {
|
||||
stream.Reset(nil)
|
||||
stream.WriteUint64(0xffffffff)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_itoa(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
strconv.FormatInt(0xffffffff, 10)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_int(b *testing.B) {
|
||||
iter := jsoniter.NewIterator(jsoniter.ConfigDefault)
|
||||
input := []byte(`100`)
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(input)
|
||||
iter.ReadInt64()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_int(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := int64(0)
|
||||
json.Unmarshal([]byte(`-100`), &result)
|
||||
}
|
||||
}
|
178
misc_tests/jsoniter_interface_test.go
Normal file
178
misc_tests/jsoniter_interface_test.go
Normal file
@ -0,0 +1,178 @@
|
||||
package misc_tests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_nil_non_empty_interface(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field []io.Closer
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
b := []byte(`{"Field":["AAA"]}`)
|
||||
should.NotNil(json.Unmarshal(b, &obj))
|
||||
should.NotNil(jsoniter.Unmarshal(b, &obj))
|
||||
}
|
||||
|
||||
func Test_nil_out_null_interface(t *testing.T) {
|
||||
type TestData struct {
|
||||
Field interface{} `json:"field"`
|
||||
}
|
||||
should := require.New(t)
|
||||
|
||||
var boolVar bool
|
||||
obj := TestData{
|
||||
Field: &boolVar,
|
||||
}
|
||||
|
||||
data1 := []byte(`{"field": true}`)
|
||||
|
||||
err := jsoniter.Unmarshal(data1, &obj)
|
||||
should.NoError(err)
|
||||
should.Equal(true, *(obj.Field.(*bool)))
|
||||
|
||||
data2 := []byte(`{"field": null}`)
|
||||
|
||||
err = jsoniter.Unmarshal(data2, &obj)
|
||||
should.NoError(err)
|
||||
should.Nil(obj.Field)
|
||||
|
||||
// Checking stdlib behavior matches.
|
||||
obj2 := TestData{
|
||||
Field: &boolVar,
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data1, &obj2)
|
||||
should.NoError(err)
|
||||
should.Equal(true, *(obj2.Field.(*bool)))
|
||||
|
||||
err = json.Unmarshal(data2, &obj2)
|
||||
should.NoError(err)
|
||||
should.Equal(nil, obj2.Field)
|
||||
}
|
||||
|
||||
func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
|
||||
type Wrapper struct {
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
}
|
||||
type Payload struct {
|
||||
Value int `json:"val,omitempty"`
|
||||
}
|
||||
|
||||
should := require.New(t)
|
||||
|
||||
payload := &Payload{}
|
||||
wrapper := &Wrapper{
|
||||
Payload: &payload,
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Equal(&payload, wrapper.Payload)
|
||||
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
|
||||
|
||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Equal(&payload, wrapper.Payload)
|
||||
should.Equal((*Payload)(nil), payload)
|
||||
|
||||
payload = &Payload{}
|
||||
wrapper = &Wrapper{
|
||||
Payload: &payload,
|
||||
}
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.Equal(nil, err)
|
||||
should.Equal(&payload, wrapper.Payload)
|
||||
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Equal(&payload, wrapper.Payload)
|
||||
should.Equal((*Payload)(nil), payload)
|
||||
}
|
||||
|
||||
func Test_overwrite_interface_value_with_nil(t *testing.T) {
|
||||
type Wrapper struct {
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
}
|
||||
type Payload struct {
|
||||
Value int `json:"val,omitempty"`
|
||||
}
|
||||
|
||||
should := require.New(t)
|
||||
|
||||
payload := &Payload{}
|
||||
wrapper := &Wrapper{
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Equal(42, wrapper.Payload.(*Payload).Value)
|
||||
|
||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Equal(nil, wrapper.Payload)
|
||||
should.Equal(42, payload.Value)
|
||||
|
||||
payload = &Payload{}
|
||||
wrapper = &Wrapper{
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.Equal(nil, err)
|
||||
should.Equal(42, wrapper.Payload.(*Payload).Value)
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.Equal(nil, err)
|
||||
should.Equal(nil, wrapper.Payload)
|
||||
should.Equal(42, payload.Value)
|
||||
}
|
||||
|
||||
func Test_unmarshal_into_nil(t *testing.T) {
|
||||
type Payload struct {
|
||||
Value int `json:"val,omitempty"`
|
||||
}
|
||||
type Wrapper struct {
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
should := require.New(t)
|
||||
|
||||
var payload *Payload
|
||||
wrapper := &Wrapper{
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.NotNil(wrapper.Payload)
|
||||
should.Nil(payload)
|
||||
|
||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Nil(wrapper.Payload)
|
||||
should.Nil(payload)
|
||||
|
||||
payload = nil
|
||||
wrapper = &Wrapper{
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.NotNil(wrapper.Payload)
|
||||
should.Nil(payload)
|
||||
|
||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||
should.NoError(err)
|
||||
should.Nil(wrapper.Payload)
|
||||
should.Nil(payload)
|
||||
}
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user