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

142 Commits

Author SHA1 Message Date
e7c7f3b337 fix coverage 2018-02-14 14:06:32 +08:00
75810179f6 remove n from stream 2018-02-14 13:58:51 +08:00
6a8f9fa342 Merge branch 'float-allocs' of git://github.com/brian-brazil/go into 1.1 2018-02-14 11:37:27 +08:00
24bb2eee9f fix #236 case sensitive when both upper case and lower case presents 2018-02-14 11:33:17 +08:00
64cc784089 remove special handling for field 2018-02-14 10:31:55 +08:00
477be43d00 consolidate more tests 2018-02-14 10:13:34 +08:00
a8708bca85 consolidate more tests 2018-02-14 08:58:59 +08:00
658ff9ef15 consolidate more tests 2018-02-14 08:48:12 +08:00
64c1c67885 consolidate more tests 2018-02-14 08:39:18 +08:00
e3bc511e5a consolidate more tests 2018-02-14 08:28:17 +08:00
8fa357ab7b consolidate mor tests 2018-02-13 23:49:40 +08:00
761ce8cce2 consolidate more tests 2018-02-13 20:58:29 +08:00
c3b6c1e845 consolidate skip tests 2018-02-13 20:41:21 +08:00
0ed9de94f2 support asymmetric tests 2018-02-13 20:25:27 +08:00
6fded6eb5f consolidate struct tags tests 2018-02-13 17:22:47 +08:00
dc3395f770 consolidate struct tests 2018-02-13 17:06:28 +08:00
bd4e013f98 consolidate slice tests 2018-02-13 16:20:08 +08:00
48a4a1e4db consolidate map tests 2018-02-13 16:07:14 +08:00
9bc223734a consolidate marshaler tests 2018-02-13 16:00:08 +08:00
eb9aeccee2 consolidate builtin tests 2018-02-13 15:48:39 +08:00
28adca2a14 consolidate array test 2018-02-13 15:43:10 +08:00
a9b3f36b2f add test framework 2018-02-13 15:32:21 +08:00
1e8e785321 Remove allocs from WriteFloat32/WriteFloat64
The use of strconv.FormatFloat causes a string allocation,
by setting aside a reusable buffer and using strconv.AppendFloat
this can be avoided.

Before:
BenchmarkRespond-4           300           5392189 ns/op          618936 B/op      20010 allocs/op

After:
BenchmarkRespond-4           300           4713746 ns/op          139744 B/op         10 allocs/op

This benchmark is using a custom encoder that calls WriteFloat64 20k
times, which is the bulk of the work.
2018-02-07 17:30:57 +00:00
002b5ae342 fix tests 2018-02-05 23:45:42 +08:00
07f99a1124 fix build 2018-02-05 23:05:57 +08:00
71f74dc71e implement #230 DisallowUnknownFields option added 2018-02-05 23:03:53 +08:00
7990317be5 gofmt 2018-02-05 22:45:04 +08:00
9edd73f752 fix build 2018-02-05 22:26:39 +08:00
3d5ee1098a Merge branch 'master' of https://github.com/json-iterator/go 2018-02-05 21:43:52 +08:00
ee8cfb7547 cache frozenConfig 2018-02-05 21:43:37 +08:00
bca911dae0 Update README.md 2018-01-28 22:27:09 +08:00
28452fcdec cow cache is not same, as map read will modify the underlying map. use sync.Map for 1.9 and above, and mutex if sync.Map not available 2018-01-28 17:00:11 +08:00
ea8c33040f fix #228 2018-01-27 16:25:48 +08:00
358cfc3929 Merge branch 'master' of https://github.com/json-iterator/go 2018-01-25 14:48:02 +08:00
c39a632e65 fix #227, fix empty json.Number 2018-01-25 14:47:50 +08:00
e31252f2e2 Merge pull request #225 from mgood/empty-array-fix
Fix encoding 0-length arrays
2018-01-23 23:31:07 +08:00
807e4a8b20 Optimize 0-length array case
Instead of checking the array length in encode, this can be checked up
front in `encoderOfArray` since the array type has a fixed length
determined at compile time. So return an `emptyArrayEncoder` that simply
writes an empty array to the stream.
2018-01-22 14:03:50 -08:00
e78b7e89b6 Merge branch 'master' of https://github.com/json-iterator/go 2018-01-21 20:59:32 +08:00
945d1aaa19 fix #140 uintptr will no lock the address from gc 2018-01-21 20:59:18 +08:00
ba3857729b Fix encoding 0-length arrays
The array encoder assumed that arrays had at least one value, so it
would serialize them with a zero-value for the array, such as `[0]`.

This adds a test to reproduce the issue, and updates the encoder to
write an empty array if the length is 0.
2018-01-16 11:02:03 -08:00
c3ed5e85e0 Merge pull request #222 from neverlee/mydev
加入一个OnlyTaggedField选项
2018-01-09 18:30:25 +08:00
c27f6f9350 config: add OnlyTaggedField config, only process tagged fields in struct 2018-01-09 17:29:47 +08:00
0ab880662f fix #219 should check real value for empty instead of just the pointer for nested field 2018-01-07 13:57:46 +08:00
6dad2de6cc fix build 2018-01-04 17:18:16 +08:00
11c1cce0d8 fix #217 when input is null, non-decodable type should not be considered as error, to be compatible with stdlib 2018-01-04 16:19:26 +08:00
96fcb84835 fix #215 lazy load more 2017-12-23 10:52:17 +08:00
e7a8aea845 Merge branch 'master' of https://github.com/json-iterator/go 2017-12-21 22:18:40 +08:00
60a9df5ebc fix #214 report EOF like stdlib 2017-12-21 22:18:28 +08:00
7b060ec866 Merge pull request #210 from coocood/master
add ReadNumber for Iterator.
2017-12-18 08:22:47 +09:00
25f147f530 add ReadNumber for Iterator. 2017-12-17 16:44:04 +08:00
a9b9c73b4d fix #207 delay unsupported type error reporting 2017-12-15 10:13:11 +08:00
e0df39fda2 fix #206, do not allow nil pointer as unmarshal input 2017-12-14 17:18:05 +08:00
13f86432b8 do not use defer() in read int 2017-12-12 18:52:41 +08:00
d2a7335211 fix #202 #203 #204 map encoder not proplery initialized 2017-12-08 21:18:59 +08:00
b2a706d14b reverse last commit, need a better fix 2017-12-08 06:15:49 +08:00
23078876c5 fix #203 consider MarshalJSON as non empty 2017-12-07 23:20:43 +08:00
051434fab7 fix #198, use dep for vendoring 2017-11-30 10:42:24 +08:00
be6688fc1a fix #200, do not use symbolic link in the code 2017-11-30 10:34:05 +08:00
ff2b70c1db support config level extension 2017-11-23 00:09:35 +08:00
f7279a603e fix out of range 2017-11-15 23:34:21 +08:00
9f088cbcc4 fix #195 when decode float as int, report it clearly 2017-11-15 23:25:12 +08:00
3c0e5762c4 fix #196 do not hard code 1 << 49 2017-11-15 23:15:31 +08:00
d394a135a1 #197 fix place holder encoder to use EncodeInterface, WriteToStream is unsafe when the real encoder is unknown 2017-11-15 22:56:23 +08:00
9fddff05f0 try to fix #194 with larger array 2017-11-11 08:31:44 +08:00
b1b003864e expose OptionalEncoder&OptionalDecoder; add attachment to Stream&Iterator for customized decoder/encoder 2017-11-08 11:41:45 +08:00
aed5a81f09 fix #190 handle empty input 2017-10-31 22:47:02 +08:00
f1258b01aa fix #191 do not always assume the object field is simple string 2017-10-31 22:38:41 +08:00
fbd210edfc Merge pull request #189 from ggaaooppeenngg/compatible-with-map
Fix standard compatiblility
2017-10-26 18:39:38 -05:00
640251ab91 Fix standard compatiblility
Non-nil but empty map with omitempty should be ignored.

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

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
2017-10-26 15:15:36 +08:00
7e3b776024 change jsoniter-sloppy to jsoniter_sloppy 2017-10-23 15:03:44 +08:00
6240e1e798 #185 add jsoniter.Valid 2017-10-10 08:57:02 +08:00
0149a5cf4a fix #183 error message not only show expectation, but also the actual value 2017-10-09 08:24:51 +08:00
5068c8baaf #183 limit error message size 2017-10-09 08:16:52 +08:00
16f78601b5 fix #184, support null as number 2017-10-07 09:29:32 +08:00
8f50a91be2 fix #181, support string as json.Number and jsoniter.Number 2017-10-06 18:08:14 +08:00
73c7bc881e fix #180, add missing methods to jsoniter.Number 2017-10-06 17:56:36 +08:00
4de15a3a87 Merge pull request #182 from MOZGIII/patch-1
Used writeTwoBytes in Stream.WriteEmptyArray
2017-10-04 02:25:32 -05:00
14b28b2226 Used writeTwoBytes in Stream.WriteEmptyArray 2017-10-03 22:14:36 +03:00
abe3c4016b fix #179 2017-09-26 15:35:55 +08:00
dbb1ef3f63 #177 flush buffer should check available again 2017-09-21 21:04:45 +08:00
46b20bbbec #178 SkipAndReturnBytes should return copy of memory 2017-09-21 20:18:45 +08:00
fdfe0b9a69 Merge branch 'olegshaldybin-skip-unexported-fields' 2017-09-19 10:06:59 +08:00
faa3dcf46a do not report error when field is unexported 2017-09-19 10:06:34 +08:00
1f58120d43 Always skip unexported fields when encoding
Skip creating encoders for unexported fields. They are not participating
in JSON marshaling anyway. This allows using unexported fields of
non-marshalable types in structs.

As a side-effect of this change it's no longer possible to marshal
unexported JSON fields by adding a custom type extenstion. It seems this
is desired behavior since it matches standard library and jsoniter
already disallows `json:"-"` fields from participating in custom
extensions.

Fixes #174.
2017-09-18 11:02:15 -07:00
6ed27152e0 Update README.md 2017-09-17 16:07:42 +08:00
3c298d8a76 Merge pull request #172 from olegshaldybin/more-stdlib-compat
Improve stdlib compatibility
2017-09-17 03:05:36 -05:00
9f6e5962a9 Improve stdlib compatibility
1. Null values for primitive types no longer clear the original value in
the destination object.

2. Dereference multiple levels of pointers in the destination interface{}
type before unmarshaling into it. This is needed to match stdlib
behavior when working with nested interface{} fields. If the destination
object is a pointer to interface{} then the incoming nil value should
nil out the destination object but keep the reference to that nil value
on its parent object. However if the destination object is an
interface{} value it should set the reference to nil but keep the
original object intact.

3. Correctly handle typed nil decode destinations.
2017-09-16 16:57:51 -07:00
c463aa12c4 Merge pull request #173 from toffaletti/more-nil-interface-fixes
More nil interface fixes
2017-09-16 18:36:43 -05:00
b5d2607a6d replace should.Equal(nil, err) with should.NoError(err) 2017-09-16 16:30:04 -07:00
48cc4d965a improve test 2017-09-16 16:27:32 -07:00
c59c42fda0 fix decoding of nil non-empty interface 2017-09-16 16:24:55 -07:00
8324374402 add tests for decoding nil interfaces 2017-09-16 16:24:27 -07:00
2017f3866b fix encoding of nil marshaler interface 2017-09-16 16:08:32 -07:00
ddc5af4512 fix encoding of nil non-empty interface 2017-09-16 16:04:36 -07:00
2f7e5c8dd7 add failing tests for nil non-empty interfaces 2017-09-16 16:00:48 -07:00
92772579dd Merge pull request #170 from olegshaldybin/marshal-enum-pointer
Fix custom marshaler for enum types
2017-09-15 09:08:45 -05:00
ae57d167e8 Fix custom marshaler for enum types
When MarshalJSON was defined on a pointer receiver custom enum type
marshaling/unmarshaling was panicing since the underlying primitive type
was treated as a pointer.

Since method set for pointer receivers includes value receiver methods
we don't really need optionalEncoder and can just use marshalEncoder
directly.
2017-09-14 23:26:12 -07:00
eef35e549b Merge pull request #169 from toffaletti/fix-nil-interface
Fix handling of nil empty interface
2017-09-15 00:45:11 -05:00
005d86dc44 fix handling of nil empty interface 2017-09-14 21:32:42 -07:00
e658f6597a add failing test for handling of nil interface with omitempty 2017-09-14 20:44:42 -07:00
f8eb43eda3 Merge pull request #168 from olegshaldybin/null-booleans
Allow null booleans
2017-09-14 20:58:31 -05:00
18a241d40b Allow null booleans
Make sure we do the same thing as stdlib with null booleans by not
touching the original value and discarding the null.

Another somewhat related change is nulling out null interface values in
the original structure. This also matches stdlib behavior.
2017-09-14 16:47:35 -07:00
0fdf883ac0 Merge pull request #167 from olegshaldybin/shorter-sleep
Shorter sleep while waiting for encoder/decoder
2017-09-14 18:23:29 -05:00
34fbec74ad Shorter sleep while waiting for encoder/decoder
If the client is using the same jsoniter config with multiple goroutines
it's very likely that few initial operations will encounter a placeholder
encoder/decoder while the real one is being created by another
goroutine. Having a full second sleep seems too conservative, since
encoder/decoder will be created in a very short time. This is very easy
to reproduce in any real environment with a few concurrent requests of
the same type. A few initial requests will have 1s+ response time.

Changing to 10ms should smooth out marshal/unmarshal times for these
initial concurrent requests.
2017-09-14 12:37:47 -07:00
90574c5ca3 #166 support ValidateJsonRawMessage in ConfigCompatibleWithStandardLibrary 2017-09-14 23:54:40 +08:00
6a4ba7bfa9 Merge branch 'master' of https://github.com/json-iterator/go 2017-09-09 08:46:07 +08:00
0828e559d0 #164 support interface{} with ptr 2017-09-09 08:45:57 +08:00
2c67d0f68a Merge pull request #163 from dvrkps/patch-2
travis: add 1.x to go versions
2017-09-07 11:25:07 -05:00
f29a0391bc travis: add 1.x to go versions 2017-09-07 17:12:42 +02:00
374e68a144 Merge pull request #162 from cch123/fix-bool-to-number
fix fuzzy decoder from bool value to number
2017-09-06 00:19:18 -05:00
b134d86290 optimize code 2017-09-06 13:18:05 +08:00
bc3221879d fix fuzzy decoder from bool value to number 2017-09-06 12:31:56 +08:00
8c7fc7584a #159 fix fuzzy decoder, the newIter assigned io.EOF error to original iterator, which stopped further processing 2017-09-06 00:31:25 +08:00
db32ee8c2d #157 number can be null 2017-09-05 13:00:03 +08:00
d80309af3b #156 invoke Marshaler defined on pointer types 2017-09-01 15:44:12 +08:00
36b14963da #153 fix invalid utf8 using same implementation as the standard library 2017-08-29 23:58:51 +08:00
f706335302 #153 fix critical bug: infinite loop when write string is invalid utf8 2017-08-29 23:39:43 +08:00
2dc0031b26 #152 gofmt 2017-08-25 12:53:23 +08:00
cdbd2ed810 #145 interface {} customizatoin is recursive 2017-08-22 10:39:01 +08:00
39e9d67807 Merge branch 'master' of https://github.com/json-iterator/go 2017-08-22 00:12:18 +08:00
2066b01acb #146 support config TagKey 2017-08-22 00:12:09 +08:00
ac3b3cd160 test []interface{} 2017-08-21 22:43:51 +08:00
887789156a Merge pull request #147 from thockin/output_tests
Add tests for int64
2017-08-11 12:55:48 +08:00
7df5a67d0d Add tests for int64 2017-08-10 20:58:49 -07:00
9c358632dc #144 make []byte support Unmarshaler&Marshaler 2017-08-09 13:59:40 +08:00
1cfa233923 #143 make jsoniter.Number same meaning as json.Number, however UseNumber still returns json.Number. 1.9 alias support should be added later 2017-08-05 07:22:53 +08:00
d249b05a85 rename ValueType, to avoid collision with json.Number 2017-08-05 07:10:15 +08:00
abbd16da6c #140 blind fix 2017-08-02 09:20:43 +08:00
b67201557a avoid gc issue 2017-08-01 08:34:38 +08:00
5124683f24 #140 try fix: maybe memory collected before assigned to existing object graph 2017-07-31 23:24:58 +08:00
4892de725b add ad 2017-07-31 21:49:02 +08:00
34a2174be3 #142 decode struct field should be case insensitiveyet another fix 2017-07-31 21:48:22 +08:00
24ecaff2a1 #142 decode struct field should be case insensitive, the bug only happen for struct with more than 10 fields 2017-07-31 20:50:07 +08:00
c15b4d116c #139 unmarshal non base64 into []byte 2017-07-19 12:04:22 +08:00
12cd299fa8 add benchmark for Skip() 2017-07-19 00:22:41 +08:00
60ba332980 acknowledge @mattn for #138 #137 #136 #135 2017-07-19 00:09:50 +08:00
f705934fbf #138 fix - without following digits; fix 1.e1 2017-07-18 23:48:40 +08:00
17a26a6e20 remove debug print 2017-07-18 23:24:21 +08:00
156284b028 #137 fix unicode surrogate incompatibility 2017-07-18 23:17:52 +08:00
6b6938829d #136 strconv.ParseFloat can not validate 1. , added extra validation for this special case 2017-07-18 22:19:52 +08:00
692 changed files with 6316 additions and 48474 deletions

5
.gitignore vendored
View File

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

View File

@ -2,6 +2,7 @@ language: go
go: go:
- 1.8.x - 1.8.x
- 1.x
before_install: before_install:
- go get -t -v ./... - go get -t -v ./...

33
Gopkg.lock generated Normal file
View File

@ -0,0 +1,33 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/google/gofuzz"
packages = ["."]
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert","require"]
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "f8b7cf3941d3792cbbd570bb53c093adaf774334d1162c651565c97a58dc9d09"
solver-name = "gps-cdcl"
solver-version = 1

33
Gopkg.toml Normal file
View File

@ -0,0 +1,33 @@
# 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"
[[constraint]]
name = "github.com/davecgh/go-spew"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/google/gofuzz"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.1.4"

View File

@ -8,6 +8,12 @@
A high-performance 100% compatible drop-in replacement of "encoding/json" A high-performance 100% compatible drop-in replacement of "encoding/json"
You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go)
```
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
```
# Benchmark # Benchmark
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
@ -40,7 +46,9 @@ with
```go ```go
import "github.com/json-iterator/go" import "github.com/json-iterator/go"
jsoniter.Marshal(&data)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Marshal(&data)
``` ```
Replace Replace
@ -54,7 +62,9 @@ with
```go ```go
import "github.com/json-iterator/go" import "github.com/json-iterator/go"
jsoniter.Unmarshal(input, &data)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(input, &data)
``` ```
[More documentation](http://jsoniter.com/migrate-from-go-std.html) [More documentation](http://jsoniter.com/migrate-from-go-std.html)
@ -70,6 +80,9 @@ go get github.com/json-iterator/go
Contributors Contributors
* [thockin](https://github.com/thockin) * [thockin](https://github.com/thockin)
* [mattn](https://github.com/mattn)
* [cch123](https://github.com/cch123) * [cch123](https://github.com/cch123)
* [Oleg Shaldybin](https://github.com/olegshaldybin)
* [Jason Toffaletti](https://github.com/toffaletti)
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)

View File

@ -1,18 +1,19 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
func Test_read_empty_array_as_any(t *testing.T) { func Test_read_empty_array_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := jsoniter.Get([]byte("[]"))
should.Equal(Array, any.Get().ValueType()) should.Equal(jsoniter.ArrayValue, any.Get().ValueType())
should.Equal(Invalid, any.Get(0.3).ValueType()) should.Equal(jsoniter.InvalidValue, any.Get(0.3).ValueType())
should.Equal(0, any.Size()) should.Equal(0, any.Size())
should.Equal(Array, any.ValueType()) should.Equal(jsoniter.ArrayValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32()) should.Equal(int32(0), any.ToInt32())
@ -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) { func Test_read_one_element_array_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[1]")) any := jsoniter.Get([]byte("[1]"))
should.Equal(1, any.Size()) should.Equal(1, any.Size())
} }
func Test_read_two_element_array_as_any(t *testing.T) { func Test_read_two_element_array_as_any(t *testing.T) {
should := require.New(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(1, any.Get(0).ToInt())
should.Equal(2, any.Size()) should.Equal(2, any.Size())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(1, any.ToInt()) should.Equal(1, any.ToInt())
should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface()) should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface())
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("[1,2]", string(stream.Buffer())) should.Equal("[1,2]", string(stream.Buffer()))
arr := []int{} 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) { func Test_wrap_array_and_convert_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Wrap([]int{1, 2, 3}) any := jsoniter.Wrap([]int{1, 2, 3})
any2 := Wrap([]int{}) any2 := jsoniter.Wrap([]int{})
should.Equal("[1,2,3]", any.ToString()) should.Equal("[1,2,3]", any.ToString())
should.True(any.ToBool()) 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) { func Test_array_lazy_any_get(t *testing.T) {
should := require.New(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(3, any.Get(1, 1).ToInt())
should.Equal("[1,[2,3],4]", any.ToString()) should.Equal("[1,[2,3],4]", any.ToString())
} }
func Test_array_lazy_any_get_all(t *testing.T) { func Test_array_lazy_any_get_all(t *testing.T) {
should := require.New(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()) 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()) should.Equal("[1,2,3]", any.ToString())
} }
func Test_array_wrapper_any_get_all(t *testing.T) { func Test_array_wrapper_any_get_all(t *testing.T) {
should := require.New(t) should := require.New(t)
any := wrapArray([][]int{ any := jsoniter.Wrap([][]int{
{1, 2}, {1, 2},
{3, 4}, {3, 4},
{5, 6}, {5, 6},
}) })
should.Equal("[1,3,5]", any.Get('*', 0).ToString()) should.Equal("[1,3,5]", any.Get('*', 0).ToString())
should.Equal(Array, any.ValueType()) should.Equal(jsoniter.ArrayValue, any.ValueType())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(1, any.Get(0, 0).ToInt()) should.Equal(1, any.Get(0, 0).ToInt())
} }
func Test_array_lazy_any_get_invalid(t *testing.T) { func Test_array_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := jsoniter.Get([]byte("[]"))
should.Equal(Invalid, any.Get(1, 1).ValueType()) should.Equal(jsoniter.InvalidValue, any.Get(1, 1).ValueType())
should.NotNil(any.Get(1, 1).LastError()) should.NotNil(any.Get(1, 1).LastError())
should.Equal(Invalid, any.Get("1").ValueType()) should.Equal(jsoniter.InvalidValue, any.Get("1").ValueType())
should.NotNil(any.Get("1").LastError()) should.NotNil(any.Get("1").LastError())
} }
func Test_invalid_array(t *testing.T) { func Test_invalid_array(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("["), 0) any := jsoniter.Get([]byte("["), 0)
should.Equal(Invalid, any.ValueType()) should.Equal(jsoniter.InvalidValue, any.ValueType())
} }

View File

@ -1,10 +1,11 @@
package jsoniter package any_tests
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
var boolConvertMap = map[string]bool{ var boolConvertMap = map[string]bool{
@ -35,9 +36,9 @@ var boolConvertMap = map[string]bool{
func Test_read_bool_as_any(t *testing.T) { func Test_read_bool_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
var any Any var any jsoniter.Any
for k, v := range boolConvertMap { for k, v := range boolConvertMap {
any = Get([]byte(k)) any = jsoniter.Get([]byte(k))
if v { if v {
should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k)) should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k))
} else { } else {
@ -49,16 +50,16 @@ func Test_read_bool_as_any(t *testing.T) {
func Test_write_bool_to_stream(t *testing.T) { func Test_write_bool_to_stream(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("true")) any := jsoniter.Get([]byte("true"))
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("true", string(stream.Buffer())) should.Equal("true", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), jsoniter.BoolValue)
any = Get([]byte("false")) any = jsoniter.Get([]byte("false"))
stream = NewStream(ConfigDefault, nil, 32) stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("false", string(stream.Buffer())) should.Equal("false", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), jsoniter.BoolValue)
} }

View File

@ -1,9 +1,10 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
var floatConvertMap = map[string]float64{ var floatConvertMap = map[string]float64{
@ -67,22 +68,22 @@ var floatConvertMap = map[string]float64{
func Test_read_any_to_float(t *testing.T) { func Test_read_any_to_float(t *testing.T) {
should := require.New(t) should := require.New(t)
for k, v := range floatConvertMap { 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) should.Equal(float64(v), any.ToFloat64(), "the original val is "+k)
} }
for k, v := range floatConvertMap { 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) should.Equal(float32(v), any.ToFloat32(), "the original val is "+k)
} }
} }
func Test_read_float_to_any(t *testing.T) { func Test_read_float_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapFloat64(12.3) any := jsoniter.WrapFloat64(12.3)
anyFloat64 := float64(12.3) anyFloat64 := float64(12.3)
//negaAnyFloat64 := float64(-1.1) //negaAnyFloat64 := float64(-1.1)
any2 := WrapFloat64(-1.1) any2 := jsoniter.WrapFloat64(-1.1)
should.Equal(float64(12.3), any.ToFloat64()) should.Equal(float64(12.3), any.ToFloat64())
//should.Equal("12.3", any.ToString()) //should.Equal("12.3", any.ToString())
should.True(any.ToBool()) should.True(any.ToBool())
@ -96,7 +97,7 @@ func Test_read_float_to_any(t *testing.T) {
should.Equal(uint(0), any2.ToUint()) should.Equal(uint(0), any2.ToUint())
should.Equal(uint32(0), any2.ToUint32()) should.Equal(uint32(0), any2.ToUint32())
should.Equal(uint64(0), any2.ToUint64()) should.Equal(uint64(0), any2.ToUint64())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal("1.23E+01", any.ToString()) should.Equal("1.23E+01", any.ToString())
} }

View File

@ -1,10 +1,11 @@
package jsoniter package any_tests
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
var intConvertMap = map[string]int{ var intConvertMap = map[string]int{
@ -41,19 +42,19 @@ func Test_read_any_to_int(t *testing.T) {
// int // int
for k, v := range intConvertMap { 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)) should.Equal(v, any.ToInt(), fmt.Sprintf("origin val %v", k))
} }
// int32 // int32
for k, v := range intConvertMap { 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)) should.Equal(int32(v), any.ToInt32(), fmt.Sprintf("original val is %v", k))
} }
// int64 // int64
for k, v := range intConvertMap { 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)) 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) should := require.New(t)
for k, v := range uintConvertMap { 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)) should.Equal(uint64(v), any.ToUint64(), fmt.Sprintf("origin val %v", k))
} }
for k, v := range uintConvertMap { 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)) should.Equal(uint32(v), any.ToUint32(), fmt.Sprintf("origin val %v", k))
} }
for k, v := range uintConvertMap { 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)) 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) { func Test_read_int64_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapInt64(12345) any := jsoniter.WrapInt64(12345)
should.Equal(12345, any.ToInt()) should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32()) should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64()) 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(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
} }
func Test_read_int32_to_any(t *testing.T) { func Test_read_int32_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapInt32(12345) any := jsoniter.WrapInt32(12345)
should.Equal(12345, any.ToInt()) should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32()) should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64()) 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(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
} }
func Test_read_uint32_to_any(t *testing.T) { func Test_read_uint32_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapUint32(12345) any := jsoniter.WrapUint32(12345)
should.Equal(12345, any.ToInt()) should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32()) should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64()) 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(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
} }
func Test_read_uint64_to_any(t *testing.T) { func Test_read_uint64_to_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapUint64(12345) any := jsoniter.WrapUint64(12345)
should.Equal(12345, any.ToInt()) should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32()) should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64()) 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(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
stream = NewStream(ConfigDefault, nil, 32) stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
stream.WriteUint(uint(123)) stream.WriteUint(uint(123))
should.Equal("123", string(stream.Buffer())) should.Equal("123", string(stream.Buffer()))
} }
func Test_int_lazy_any_get(t *testing.T) { func Test_int_lazy_any_get(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("1234")) any := jsoniter.Get([]byte("1234"))
// panic!! // panic!!
//should.Equal(any.LastError(), io.EOF) //should.Equal(any.LastError(), io.EOF)
should.Equal(Invalid, any.Get(1, "2").ValueType()) should.Equal(jsoniter.InvalidValue, any.Get(1, "2").ValueType())
} }

View File

@ -0,0 +1,28 @@
package any_tests
import (
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
)
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), "")
}

View File

@ -1,13 +1,14 @@
package jsoniter package any_tests
import ( import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"testing" "testing"
"github.com/json-iterator/go"
) )
func Test_read_null_as_any(t *testing.T) { func Test_read_null_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte(`null`)) any := jsoniter.Get([]byte(`null`))
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(float64(0), any.ToFloat64()) should.Equal(float64(0), any.ToFloat64())
should.Equal("", any.ToString()) should.Equal("", any.ToString())

View File

@ -1,26 +1,27 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
func Test_read_object_as_any(t *testing.T) { func Test_read_object_as_any(t *testing.T) {
should := require.New(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()) should.Equal(`{"a":"stream","c":"d"}`, any.ToString())
// partial parse // partial parse
should.Equal("stream", any.Get("a").ToString()) should.Equal("stream", any.Get("a").ToString())
should.Equal("d", any.Get("c").ToString()) should.Equal("d", any.Get("c").ToString())
should.Equal(2, len(any.Keys())) should.Equal(2, len(any.Keys()))
any = Get([]byte(`{"a":"stream","c":"d"}`)) any = jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
// full parse // full parse
should.Equal(2, len(any.Keys())) should.Equal(2, len(any.Keys()))
should.Equal(2, any.Size()) should.Equal(2, any.Size())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(Object, any.ValueType()) should.Equal(jsoniter.ObjectValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
obj := struct { obj := struct {
A string A string
@ -31,26 +32,26 @@ func Test_read_object_as_any(t *testing.T) {
func Test_object_lazy_any_get(t *testing.T) { func Test_object_lazy_any_get(t *testing.T) {
should := require.New(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()) should.Equal("d", any.Get("a", "stream", "c").ToString())
} }
func Test_object_lazy_any_get_all(t *testing.T) { func Test_object_lazy_any_get_all(t *testing.T) {
should := require.New(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`) should.Contains(any.Get('*', 0).ToString(), `"a":0`)
} }
func Test_object_lazy_any_get_invalid(t *testing.T) { func Test_object_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte(`{}`)) any := jsoniter.Get([]byte(`{}`))
should.Equal(Invalid, any.Get("a", "stream", "c").ValueType()) should.Equal(jsoniter.InvalidValue, any.Get("a", "stream", "c").ValueType())
should.Equal(Invalid, any.Get(1).ValueType()) should.Equal(jsoniter.InvalidValue, any.Get(1).ValueType())
} }
func Test_wrap_map_and_convert_to_any(t *testing.T) { func Test_wrap_map_and_convert_to_any(t *testing.T) {
should := require.New(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.True(any.ToBool())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32()) should.Equal(int32(0), any.ToInt32())
@ -68,9 +69,9 @@ func Test_wrap_object_and_convert_to_any(t *testing.T) {
Field1 string Field1 string
field2 string field2 string
} }
any := Wrap(TestObject{"hello", "world"}) any := jsoniter.Wrap(TestObject{"hello", "world"})
should.Equal("hello", any.Get("Field1").ToString()) 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(2, any.Size())
should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString()) should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString())
@ -96,12 +97,27 @@ func Test_wrap_object_and_convert_to_any(t *testing.T) {
func Test_any_within_struct(t *testing.T) { func Test_any_within_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {
Field1 Any Field1 jsoniter.Any
Field2 Any Field2 jsoniter.Any
} }
obj := TestObject{} 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.Nil(err)
should.Equal("hello", obj.Field1.ToString()) should.Equal("hello", obj.Field1.ToString())
should.Equal("[1,2,3]", obj.Field2.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")
//should.Contains(any.GetObject()["Field1"].GetArray()[0], 1)
}

View File

@ -1,9 +1,10 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
var stringConvertMap = map[string]string{ var stringConvertMap = map[string]string{
@ -31,26 +32,27 @@ var stringConvertMap = map[string]string{
func Test_read_any_to_string(t *testing.T) { func Test_read_any_to_string(t *testing.T) {
should := require.New(t) should := require.New(t)
for k, v := range stringConvertMap { for k, v := range stringConvertMap {
any := Get([]byte(k)) any := jsoniter.Get([]byte(k))
should.Equal(v, any.ToString(), "original val "+k) should.Equal(v, any.ToString(), "original val "+k)
} }
} }
func Test_read_string_as_any(t *testing.T) { func Test_read_string_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte(`"hello"`)) any := jsoniter.Get([]byte(`"hello"`))
should.Equal("hello", any.ToString()) should.Equal("hello", any.ToString())
should.True(any.ToBool()) should.True(any.ToBool())
any = Get([]byte(`" "`)) any = jsoniter.Get([]byte(`" "`))
should.False(any.ToBool()) should.False(any.ToBool())
any = Get([]byte(`"false"`)) any = jsoniter.Get([]byte(`"false"`))
should.True(any.ToBool()) should.True(any.ToBool())
any = Get([]byte(`"123"`)) any = jsoniter.Get([]byte(`"123"`))
should.Equal(123, any.ToInt()) should.Equal(123, any.ToInt())
} }
func Test_wrap_string(t *testing.T) { func Test_wrap_string(t *testing.T) {
should := require.New(t) should := require.New(t)
any := WrapString("123") any := jsoniter.Get([]byte("-32000")).MustBeValid()
should.Equal(123, any.ToInt()) should.Equal(-32000, any.ToInt())
should.NoError(any.LastError())
} }

View File

@ -1,71 +1,72 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
// if must be valid is useless, just drop this test // if must be valid is useless, just drop this test
func Test_must_be_valid(t *testing.T) { func Test_must_be_valid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("123")) any := jsoniter.Get([]byte("123"))
should.Equal(any.MustBeValid().ToInt(), 123) should.Equal(any.MustBeValid().ToInt(), 123)
any = Wrap(int8(10)) any = jsoniter.Wrap(int8(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int16(10)) any = jsoniter.Wrap(int16(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int32(10)) any = jsoniter.Wrap(int32(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int64(10)) any = jsoniter.Wrap(int64(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint(10)) any = jsoniter.Wrap(uint(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint8(10)) any = jsoniter.Wrap(uint8(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint16(10)) any = jsoniter.Wrap(uint16(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint32(10)) any = jsoniter.Wrap(uint32(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint64(10)) any = jsoniter.Wrap(uint64(10))
should.Equal(any.MustBeValid().ToInt(), 10) should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(float32(10)) any = jsoniter.Wrap(float32(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10)) should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = Wrap(float64(10)) any = jsoniter.Wrap(float64(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10)) should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = Wrap(true) any = jsoniter.Wrap(true)
should.Equal(any.MustBeValid().ToFloat64(), float64(1)) should.Equal(any.MustBeValid().ToFloat64(), float64(1))
any = Wrap(false) any = jsoniter.Wrap(false)
should.Equal(any.MustBeValid().ToFloat64(), float64(0)) should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap(nil) any = jsoniter.Wrap(nil)
should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 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)) 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)) should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap("abc") any = jsoniter.Wrap("abc")
should.Equal(any.MustBeValid().ToFloat64(), float64(0)) should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap([]int{}) any = jsoniter.Wrap([]int{})
should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 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)) should.Equal(any.MustBeValid().ToFloat64(), float64(1))
} }

View File

@ -1,116 +1,117 @@
package jsoniter package any_tests
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
func Test_wrap_and_valuetype_everything(t *testing.T) { func Test_wrap_and_valuetype_everything(t *testing.T) {
should := require.New(t) should := require.New(t)
var i interface{} var i interface{}
any := Get([]byte("123")) any := jsoniter.Get([]byte("123"))
// default of number type is float64 // default of number type is float64
i = float64(123) i = float64(123)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int8(10)) any = jsoniter.Wrap(int8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// get interface is not int8 interface // get interface is not int8 interface
// i = int8(10) // i = int8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(int16(10)) any = jsoniter.Wrap(int16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
//i = int16(10) //i = int16(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(int32(10)) any = jsoniter.Wrap(int32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int32(10) i = int32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int64(10)) any = jsoniter.Wrap(int64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int64(10) i = int64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint(10)) any = jsoniter.Wrap(uint(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = uint(10) //i = uint(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(uint8(10)) any = jsoniter.Wrap(uint8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
// i = uint8(10) // i = uint8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(uint16(10)) any = jsoniter.Wrap(uint16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(uint32(10)) any = jsoniter.Wrap(uint32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint32(10) i = uint32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint64(10)) any = jsoniter.Wrap(uint64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint64(10) i = uint64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(float32(10)) any = jsoniter.Wrap(float32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = float32(10) //i = float32(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(float64(10)) any = jsoniter.Wrap(float64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = float64(10) i = float64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(true) any = jsoniter.Wrap(true)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), jsoniter.BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = true i = true
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(false) any = jsoniter.Wrap(false)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), jsoniter.BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = false i = false
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(nil) any = jsoniter.Wrap(nil)
should.Equal(any.ValueType(), Nil) should.Equal(any.ValueType(), jsoniter.NilValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = nil i = nil
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
stream := NewStream(ConfigDefault, nil, 32) stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("null", string(stream.Buffer())) should.Equal("null", string(stream.Buffer()))
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(struct{ age int }{age: 1}) any = jsoniter.Wrap(struct{ age int }{age: 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), jsoniter.ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = struct{ age int }{age: 1} i = struct{ age int }{age: 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(map[string]interface{}{"abc": 1}) any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), jsoniter.ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = map[string]interface{}{"abc": 1} i = map[string]interface{}{"abc": 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap("abc") any = jsoniter.Wrap("abc")
i = "abc" i = "abc"
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
should.Equal(nil, any.LastError()) should.Equal(nil, any.LastError())

38
api_tests/config_test.go Normal file
View File

@ -0,0 +1,38 @@
package test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"encoding/json"
)
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)
}

59
api_tests/decoder_test.go Normal file
View File

@ -0,0 +1,59 @@
package test
import (
"bytes"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
"io/ioutil"
"encoding/json"
)
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 := jsoniter.NewDecoder(bytes.NewBufferString(`[1][2]`))
arr1 := []int{}
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{1}, arr1)
arr2 := []int{}
should.True(decoder1.More())
buffered, _ := ioutil.ReadAll(decoder1.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{1}, arr2)
should.True(decoder2.More())
buffered, _ = ioutil.ReadAll(decoder2.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{2}, arr1)
should.False(decoder1.More())
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{2}, arr2)
should.False(decoder2.More())
}
func Test_use_number(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`123`))
decoder2.UseNumber()
var obj1 interface{}
should.Nil(decoder1.Decode(&obj1))
should.Equal(json.Number("123"), obj1)
var obj2 interface{}
should.Nil(decoder2.Decode(&obj2))
should.Equal(json.Number("123"), obj2)
}

View File

@ -1,6 +1,6 @@
// +build go1.8 //+build go1.8
package jsoniter package test
import ( import (
"bytes" "bytes"
@ -9,6 +9,7 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/json-iterator/go"
) )
func Test_new_encoder(t *testing.T) { func Test_new_encoder(t *testing.T) {
@ -19,14 +20,14 @@ func Test_new_encoder(t *testing.T) {
encoder1.Encode([]int{1}) encoder1.Encode([]int{1})
should.Equal("[1]\n", buf1.String()) should.Equal("[1]\n", buf1.String())
buf2 := &bytes.Buffer{} buf2 := &bytes.Buffer{}
encoder2 := NewEncoder(buf2) encoder2 := jsoniter.NewEncoder(buf2)
encoder2.SetEscapeHTML(false) encoder2.SetEscapeHTML(false)
encoder2.Encode([]int{1}) encoder2.Encode([]int{1})
should.Equal("[1]", buf2.String()) should.Equal("[1]\n", buf2.String())
} }
func Test_string_encode_with_std_without_html_escape(t *testing.T) { func Test_string_encode_with_std_without_html_escape(t *testing.T) {
api := Config{EscapeHTML: false}.Froze() api := jsoniter.Config{EscapeHTML: false}.Froze()
should := require.New(t) should := require.New(t)
for i := 0; i < utf8.RuneSelf; i++ { for i := 0; i < utf8.RuneSelf; i++ {
input := string([]byte{byte(i)}) input := string([]byte{byte(i)})

20
api_tests/encoder_test.go Normal file
View File

@ -0,0 +1,20 @@
package test
import (
"testing"
"github.com/stretchr/testify/require"
"bytes"
"github.com/json-iterator/go"
"encoding/json"
)
// 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())
}

View File

@ -0,0 +1,36 @@
package test
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
)
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))
}

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

View File

@ -0,0 +1,158 @@
package test
import (
"encoding/json"
"io/ioutil"
"os"
"testing"
"github.com/json-iterator/go"
)
//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)
}
}
}

12
build.sh Executable file
View 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

View File

@ -0,0 +1,101 @@
package test
import (
"testing"
"unsafe"
"time"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strconv"
)
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)
}
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())
}

View File

@ -0,0 +1,75 @@
package test
import (
"unsafe"
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
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)
}
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) EncodeInterface(val interface{}, stream *jsoniter.Stream) {
jsoniter.WriteToStream(val, stream, encoder)
}
func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
if encoder.isEmptyFunc == nil {
return false
}
return encoder.isEmptyFunc(ptr)
}

View File

@ -2,11 +2,13 @@ package extra
import ( import (
"encoding/json" "encoding/json"
"github.com/json-iterator/go" "io"
"math" "math"
"reflect" "reflect"
"strings" "strings"
"unsafe" "unsafe"
"github.com/json-iterator/go"
) )
const maxUint = ^uint(0) const maxUint = ^uint(0)
@ -158,7 +160,7 @@ type tolerateEmptyArrayDecoder struct {
} }
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if iter.WhatIsNext() == jsoniter.Array { if iter.WhatIsNext() == jsoniter.ArrayValue {
iter.Skip() iter.Skip()
newIter := iter.Pool().BorrowIterator([]byte("{}")) newIter := iter.Pool().BorrowIterator([]byte("{}"))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
@ -174,11 +176,11 @@ type fuzzyStringDecoder struct {
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
*((*string)(ptr)) = string(number) *((*string)(ptr)) = string(number)
case jsoniter.String: case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString() *((*string)(ptr)) = iter.ReadString()
default: default:
iter.ReportError("fuzzyStringDecoder", "not number or string") iter.ReportError("fuzzyStringDecoder", "not number or string")
@ -193,12 +195,18 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
str = string(number) str = string(number)
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
case jsoniter.BoolValue:
if iter.ReadBool() {
str = "1"
} else {
str = "0"
}
default: default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string") iter.ReportError("fuzzyIntegerDecoder", "not number or string")
} }
@ -206,7 +214,7 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1 isFloat := strings.IndexByte(str, '.') != -1
decoder.fun(isFloat, ptr, newIter) decoder.fun(isFloat, ptr, newIter)
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
} }
@ -218,16 +226,23 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float32)(ptr)) = iter.ReadFloat32() *((*float32)(ptr)) = iter.ReadFloat32()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float32)(ptr)) = newIter.ReadFloat32() *((*float32)(ptr)) = newIter.ReadFloat32()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float32
if iter.ReadBool() {
*((*float32)(ptr)) = 1
} else {
*((*float32)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }
@ -240,16 +255,23 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float64)(ptr)) = iter.ReadFloat64() *((*float64)(ptr)) = iter.ReadFloat64()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float64)(ptr)) = newIter.ReadFloat64() *((*float64)(ptr)) = newIter.ReadFloat64()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float64
if iter.ReadBool() {
*((*float64)(ptr)) = 1
} else {
*((*float64)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }

View File

@ -38,6 +38,12 @@ func Test_any_to_int64(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int64(10), val) should.Equal(int64(10), 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.Nil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(int64(-10), val) should.Equal(int64(-10), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
@ -57,6 +63,13 @@ func Test_any_to_int(t *testing.T) {
should.Equal(10, val) should.Equal(10, val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -74,6 +87,13 @@ func Test_any_to_int16(t *testing.T) {
should.Equal(int16(10), val) should.Equal(int16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int16(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -91,6 +111,13 @@ func Test_any_to_int32(t *testing.T) {
should.Equal(int32(10), val) should.Equal(int32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int32(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -108,6 +135,13 @@ func Test_any_to_int8(t *testing.T) {
should.Equal(int8(10), val) should.Equal(int8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int8(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -125,6 +159,13 @@ func Test_any_to_uint8(t *testing.T) {
should.Equal(uint8(10), val) should.Equal(uint8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint8(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -144,6 +185,12 @@ func Test_any_to_uint64(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint64(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? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint64(0), val) should.Equal(uint64(0), val)
@ -165,6 +212,12 @@ func Test_any_to_uint32(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint32(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? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint32(0), val) should.Equal(uint32(0), val)
@ -186,6 +239,12 @@ func Test_any_to_uint16(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint16(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? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint16(0), val) should.Equal(uint16(0), val)
@ -205,6 +264,12 @@ func Test_any_to_uint(t *testing.T) {
should.Equal(uint(10), val) should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -223,6 +288,13 @@ func Test_any_to_float32(t *testing.T) {
should.Equal(float32(10.1), val) should.Equal(float32(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float32(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
} }
@ -240,6 +312,13 @@ func Test_any_to_float64(t *testing.T) {
should.Equal(float64(10.1), val) should.Equal(float64(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float64(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))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
} }
@ -257,3 +336,24 @@ func Test_empty_array_as_object(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val)) should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
should.Equal(struct{}{}, 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)
}

View File

@ -71,6 +71,11 @@ type Decoder struct {
// Decode decode JSON into interface{} // Decode decode JSON into interface{}
func (adapter *Decoder) Decode(obj interface{}) error { 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) adapter.iter.ReadVal(obj)
err := adapter.iter.Error err := adapter.iter.Error
if err == io.EOF { if err == io.EOF {
@ -90,11 +95,21 @@ func (adapter *Decoder) Buffered() io.Reader {
return bytes.NewReader(remaining) return bytes.NewReader(remaining)
} }
// UseNumber for number JSON element, use float64 or json.Number (alias of string) // UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (adapter *Decoder) UseNumber() { func (adapter *Decoder) UseNumber() {
origCfg := adapter.iter.cfg.configBeforeFrozen cfg := adapter.iter.cfg.configBeforeFrozen
origCfg.UseNumber = true cfg.UseNumber = true
adapter.iter.cfg = origCfg.Froze().(*frozenConfig) adapter.iter.cfg = cfg.frozeWithCacheReuse()
}
// 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()
} }
// NewEncoder same as json.NewEncoder // NewEncoder same as json.NewEncoder
@ -110,18 +125,26 @@ type Encoder struct {
// Encode encode interface{} as JSON to io.Writer // Encode encode interface{} as JSON to io.Writer
func (adapter *Encoder) Encode(val interface{}) error { func (adapter *Encoder) Encode(val interface{}) error {
adapter.stream.WriteVal(val) adapter.stream.WriteVal(val)
adapter.stream.WriteRaw("\n")
adapter.stream.Flush() adapter.stream.Flush()
return adapter.stream.Error return adapter.stream.Error
} }
// SetIndent set the indention. Prefix is not supported // SetIndent set the indention. Prefix is not supported
func (adapter *Encoder) SetIndent(prefix, indent string) { 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()
} }
// SetEscapeHTML escape html by default, set to false to disable // SetEscapeHTML escape html by default, set to false to disable
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) { func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
config := adapter.stream.cfg.configBeforeFrozen config := adapter.stream.cfg.configBeforeFrozen
config.EscapeHTML = escapeHTML config.EscapeHTML = escapeHTML
adapter.stream.cfg = config.Froze().(*frozenConfig) adapter.stream.cfg = config.frozeWithCacheReuse()
}
// Valid reports whether data is a valid JSON encoding.
func Valid(data []byte) bool {
return ConfigDefault.Valid(data)
} }

View File

@ -1,6 +1,7 @@
package jsoniter package jsoniter
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
@ -157,6 +158,8 @@ func (iter *Iterator) readAny() Any {
return iter.readArrayAny() return iter.readArrayAny()
case '-': case '-':
return iter.readNumberAny(false) return iter.readNumberAny(false)
case 0:
return &invalidAny{baseAny{}, errors.New("input is empty")}
default: default:
return iter.readNumberAny(true) return iter.readNumberAny(true)
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,9 @@
package jsoniter package jsoniter
import "unsafe" import (
"io"
"unsafe"
)
type numberLazyAny struct { type numberLazyAny struct {
baseAny baseAny
@ -10,7 +13,7 @@ type numberLazyAny struct {
} }
func (any *numberLazyAny) ValueType() ValueType { func (any *numberLazyAny) ValueType() ValueType {
return Number return NumberValue
} }
func (any *numberLazyAny) MustBeValid() Any { func (any *numberLazyAny) MustBeValid() Any {
@ -29,7 +32,9 @@ func (any *numberLazyAny) ToInt() int {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt() val := iter.ReadInt()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -37,7 +42,9 @@ func (any *numberLazyAny) ToInt32() int32 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt32() val := iter.ReadInt32()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -45,7 +52,9 @@ func (any *numberLazyAny) ToInt64() int64 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt64() val := iter.ReadInt64()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -53,7 +62,9 @@ func (any *numberLazyAny) ToUint() uint {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint() val := iter.ReadUint()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -61,7 +72,9 @@ func (any *numberLazyAny) ToUint32() uint32 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint32() val := iter.ReadUint32()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -69,7 +82,9 @@ func (any *numberLazyAny) ToUint64() uint64 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint64() val := iter.ReadUint64()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -77,7 +92,9 @@ func (any *numberLazyAny) ToFloat32() float32 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadFloat32() val := iter.ReadFloat32()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }
@ -85,7 +102,9 @@ func (any *numberLazyAny) ToFloat64() float64 {
iter := any.cfg.BorrowIterator(any.buf) iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
val := iter.ReadFloat64() val := iter.ReadFloat64()
any.err = iter.Error if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val return val
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -5,29 +5,22 @@ import (
"errors" "errors"
"io" "io"
"reflect" "reflect"
"sync/atomic"
"unsafe" "unsafe"
) )
// Config customize how the API should behave. // Config customize how the API should behave.
// The API is created from Config by Froze. // The API is created from Config by Froze.
type Config struct { type Config struct {
IndentionStep int IndentionStep int
MarshalFloatWith6Digits bool MarshalFloatWith6Digits bool
EscapeHTML bool EscapeHTML bool
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
} DisallowUnknownFields bool
TagKey string
type frozenConfig struct { OnlyTaggedField bool
configBeforeFrozen Config ValidateJsonRawMessage bool
sortMapKeys bool ObjectFieldMustBeSimpleString bool
indentionStep int
decoderCache unsafe.Pointer
encoderCache unsafe.Pointer
extensions []Extension
streamPool chan *Stream
iteratorPool chan *Iterator
} }
// API the public interface of this package. // API the public interface of this package.
@ -43,6 +36,8 @@ type API interface {
Get(data []byte, path ...interface{}) Any Get(data []byte, path ...interface{}) Any
NewEncoder(writer io.Writer) *Encoder NewEncoder(writer io.Writer) *Encoder
NewDecoder(reader io.Reader) *Decoder NewDecoder(reader io.Reader) *Decoder
Valid(data []byte) bool
RegisterExtension(extension Extension)
} }
// ConfigDefault the default API // ConfigDefault the default API
@ -52,51 +47,92 @@ var ConfigDefault = Config{
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior // ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
var ConfigCompatibleWithStandardLibrary = Config{ var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true, EscapeHTML: true,
SortMapKeys: true, SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze() }.Froze()
// ConfigFastest marshals float with only 6 digits precision // ConfigFastest marshals float with only 6 digits precision
var ConfigFastest = Config{ var ConfigFastest = Config{
EscapeHTML: false, EscapeHTML: false,
MarshalFloatWith6Digits: true, MarshalFloatWith6Digits: true, // will lose precession
ObjectFieldMustBeSimpleString: true, // do not unescape object field
}.Froze() }.Froze()
// Froze forge API from config // Froze forge API from config
func (cfg Config) Froze() API { func (cfg Config) Froze() API {
// TODO: cache frozen config api := &frozenConfig{
frozenConfig := &frozenConfig{ sortMapKeys: cfg.SortMapKeys,
sortMapKeys: cfg.SortMapKeys, indentionStep: cfg.IndentionStep,
indentionStep: cfg.IndentionStep, objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
streamPool: make(chan *Stream, 16), onlyTaggedField: cfg.OnlyTaggedField,
iteratorPool: make(chan *Iterator, 16), disallowUnknownFields: cfg.DisallowUnknownFields,
streamPool: make(chan *Stream, 16),
iteratorPool: make(chan *Iterator, 16),
} }
atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{})) api.initCache()
atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
if cfg.MarshalFloatWith6Digits { if cfg.MarshalFloatWith6Digits {
frozenConfig.marshalFloatWith6Digits() api.marshalFloatWith6Digits()
} }
if cfg.EscapeHTML { if cfg.EscapeHTML {
frozenConfig.escapeHTML() api.escapeHTML()
} }
if cfg.UseNumber { if cfg.UseNumber {
frozenConfig.useNumber() api.useNumber()
} }
frozenConfig.configBeforeFrozen = cfg if cfg.ValidateJsonRawMessage {
return frozenConfig api.validateJsonRawMessage()
}
api.configBeforeFrozen = cfg
return api
}
func (cfg Config) frozeWithCacheReuse() *frozenConfig {
api := getFrozenConfigFromCache(cfg)
if api != nil {
return api
}
api = cfg.Froze().(*frozenConfig)
addFrozenConfigToCache(cfg, api)
return api
}
func (cfg *frozenConfig) validateJsonRawMessage() {
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
rawMessage := *(*json.RawMessage)(ptr)
iter := cfg.BorrowIterator([]byte(rawMessage))
iter.Read()
if iter.Error != nil {
stream.WriteRaw("null")
} else {
cfg.ReturnIterator(iter)
stream.WriteRaw(string(rawMessage))
}
}, func(ptr unsafe.Pointer) bool {
return false
}}
cfg.addEncoderToCache(reflect.TypeOf((*json.RawMessage)(nil)).Elem(), encoder)
cfg.addEncoderToCache(reflect.TypeOf((*RawMessage)(nil)).Elem(), encoder)
} }
func (cfg *frozenConfig) useNumber() { func (cfg *frozenConfig) useNumber() {
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == Number { if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) *((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else { } else {
*((*interface{})(ptr)) = iter.Read() *((*interface{})(ptr)) = iter.Read()
} }
}}) }})
} }
func (cfg *frozenConfig) getTagKey() string {
tagKey := cfg.configBeforeFrozen.TagKey
if tagKey == "" {
return "json"
}
return tagKey
}
func (cfg *frozenConfig) registerExtension(extension Extension) { func (cfg *frozenConfig) RegisterExtension(extension Extension) {
cfg.extensions = append(cfg.extensions, extension) cfg.extensions = append(cfg.extensions, extension)
} }
@ -158,46 +194,6 @@ func (cfg *frozenConfig) escapeHTML() {
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{}) 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() { func (cfg *frozenConfig) cleanDecoders() {
typeDecoders = map[string]ValDecoder{} typeDecoders = map[string]ValDecoder{}
fieldDecoders = map[string]ValDecoder{} fieldDecoders = map[string]ValDecoder{}
@ -244,7 +240,7 @@ func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]
} }
newCfg := cfg.configBeforeFrozen newCfg := cfg.configBeforeFrozen
newCfg.IndentionStep = len(indent) newCfg.IndentionStep = len(indent)
return newCfg.Froze().Marshal(v) return newCfg.frozeWithCacheReuse().Marshal(v)
} }
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error { func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
@ -302,3 +298,10 @@ func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
iter := Parse(cfg, reader, 512) iter := Parse(cfg, reader, 512)
return &Decoder{iter} return &Decoder{iter}
} }
func (cfg *frozenConfig) Valid(data []byte) bool {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.Skip()
return iter.Error == nil
}

View File

@ -0,0 +1,65 @@
//+build go1.9
package jsoniter
import (
"reflect"
"sync"
)
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
decoderCache sync.Map
encoderCache sync.Map
extensions []Extension
streamPool chan *Stream
iteratorPool chan *Iterator
}
func (cfg *frozenConfig) initCache() {
cfg.decoderCache = sync.Map{}
cfg.encoderCache = sync.Map{}
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
cfg.decoderCache.Store(cacheKey, decoder)
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
cfg.encoderCache.Store(cacheKey, encoder)
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
decoder, found := cfg.decoderCache.Load(cacheKey)
if found {
return decoder.(ValDecoder)
}
return nil
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
encoder, found := cfg.encoderCache.Load(cacheKey)
if found {
return encoder.(ValEncoder)
}
return nil
}
var cfgCache = &sync.Map{}
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)
}

View File

@ -0,0 +1,71 @@
//+build !go1.9
package jsoniter
import (
"reflect"
"sync"
)
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
cacheLock *sync.RWMutex
decoderCache map[reflect.Type]ValDecoder
encoderCache map[reflect.Type]ValEncoder
extensions []Extension
streamPool chan *Stream
iteratorPool chan *Iterator
}
func (cfg *frozenConfig) initCache() {
cfg.cacheLock = &sync.RWMutex{}
cfg.decoderCache = map[reflect.Type]ValDecoder{}
cfg.encoderCache = map[reflect.Type]ValEncoder{}
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
cfg.cacheLock.Lock()
cfg.decoderCache[cacheKey] = decoder
cfg.cacheLock.Unlock()
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
cfg.cacheLock.Lock()
cfg.encoderCache[cacheKey] = encoder
cfg.cacheLock.Unlock()
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
cfg.cacheLock.RLock()
decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
cfg.cacheLock.RUnlock()
return decoder
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
cfg.cacheLock.RLock()
encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
cfg.cacheLock.RUnlock()
return encoder
}
var cfgCacheLock = &sync.RWMutex{}
var cfgCache = map[Config]*frozenConfig{}
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
cfgCacheLock.RLock()
frozenConfig := cfgCache[cfg]
cfgCacheLock.RUnlock()
return frozenConfig
}
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
cfgCacheLock.Lock()
cfgCache[cfg] = frozenConfig
cfgCacheLock.Unlock()
}

View File

@ -10,20 +10,20 @@ import (
type ValueType int type ValueType int
const ( const (
// Invalid invalid JSON element // InvalidValue invalid JSON element
Invalid ValueType = iota InvalidValue ValueType = iota
// String JSON element "string" // StringValue JSON element "string"
String StringValue
// Number JSON element 100 or 0.10 // NumberValue JSON element 100 or 0.10
Number NumberValue
// Nil JSON element null // NilValue JSON element null
Nil NilValue
// Bool JSON element true or false // BoolValue JSON element true or false
Bool BoolValue
// Array JSON element [] // ArrayValue JSON element []
Array ArrayValue
// Object JSON element {} // ObjectValue JSON element {}
Object ObjectValue
) )
var hexDigits []byte var hexDigits []byte
@ -45,25 +45,25 @@ func init() {
} }
valueTypes = make([]ValueType, 256) valueTypes = make([]ValueType, 256)
for i := 0; i < len(valueTypes); i++ { for i := 0; i < len(valueTypes); i++ {
valueTypes[i] = Invalid valueTypes[i] = InvalidValue
} }
valueTypes['"'] = String valueTypes['"'] = StringValue
valueTypes['-'] = Number valueTypes['-'] = NumberValue
valueTypes['0'] = Number valueTypes['0'] = NumberValue
valueTypes['1'] = Number valueTypes['1'] = NumberValue
valueTypes['2'] = Number valueTypes['2'] = NumberValue
valueTypes['3'] = Number valueTypes['3'] = NumberValue
valueTypes['4'] = Number valueTypes['4'] = NumberValue
valueTypes['5'] = Number valueTypes['5'] = NumberValue
valueTypes['6'] = Number valueTypes['6'] = NumberValue
valueTypes['7'] = Number valueTypes['7'] = NumberValue
valueTypes['8'] = Number valueTypes['8'] = NumberValue
valueTypes['9'] = Number valueTypes['9'] = NumberValue
valueTypes['t'] = Bool valueTypes['t'] = BoolValue
valueTypes['f'] = Bool valueTypes['f'] = BoolValue
valueTypes['n'] = Nil valueTypes['n'] = NilValue
valueTypes['['] = Array valueTypes['['] = ArrayValue
valueTypes['{'] = Object valueTypes['{'] = ObjectValue
} }
// Iterator is a io.Reader like object, with JSON specific read functions. // Iterator is a io.Reader like object, with JSON specific read functions.
@ -77,6 +77,7 @@ type Iterator struct {
captureStartedAt int captureStartedAt int
captured []byte captured []byte
Error error Error error
Attachment interface{} // open for customized decoder
} }
// NewIterator creates an empty Iterator instance // NewIterator creates an empty Iterator instance
@ -167,7 +168,7 @@ func (iter *Iterator) isObjectEnd() bool {
if c == '}' { if c == '}' {
return true return true
} }
iter.ReportError("isObjectEnd", "object ended prematurely") iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
return true return true
} }
@ -200,8 +201,22 @@ func (iter *Iterator) ReportError(operation string, msg string) {
if peekStart < 0 { if peekStart < 0 {
peekStart = 0 peekStart = 0
} }
iter.Error = fmt.Errorf("%s: %s, parsing %v ...%s... at %s", operation, msg, iter.head, peekEnd := iter.head + 10
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) 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 // CurrentBuffer gets current buffer as string for debugging purpose
@ -210,7 +225,7 @@ func (iter *Iterator) CurrentBuffer() string {
if peekStart < 0 { if peekStart < 0 {
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])) string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
} }
@ -270,29 +285,33 @@ func (iter *Iterator) unreadByte() {
func (iter *Iterator) Read() interface{} { func (iter *Iterator) Read() interface{} {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case String: case StringValue:
return iter.ReadString() return iter.ReadString()
case Number: case NumberValue:
if iter.cfg.configBeforeFrozen.UseNumber { if iter.cfg.configBeforeFrozen.UseNumber {
return json.Number(iter.readNumberAsString()) return json.Number(iter.readNumberAsString())
} }
return iter.ReadFloat64() return iter.ReadFloat64()
case Nil: case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l') iter.skipFourBytes('n', 'u', 'l', 'l')
return nil return nil
case Bool: case BoolValue:
return iter.ReadBool() return iter.ReadBool()
case Array: case ArrayValue:
arr := []interface{}{} arr := []interface{}{}
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *Iterator) bool {
arr = append(arr, iter.Read()) var elem interface{}
iter.ReadVal(&elem)
arr = append(arr, elem)
return true return true
}) })
return arr return arr
case Object: case ObjectValue:
obj := map[string]interface{}{} obj := map[string]interface{}{}
iter.ReadMapCB(func(Iter *Iterator, field string) bool { iter.ReadMapCB(func(Iter *Iterator, field string) bool {
obj[field] = iter.Read() var elem interface{}
iter.ReadVal(&elem)
obj[field] = elem
return true return true
}) })
return obj return obj

View File

@ -19,7 +19,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
case ',': case ',':
return true return true
default: 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 return
} }
} }
@ -42,7 +42,7 @@ func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
c = iter.nextToken() c = iter.nextToken()
} }
if c != ']' { if c != ']' {
iter.ReportError("ReadArrayCB", "expect ] in the end") iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
return false return false
} }
return true return true
@ -53,6 +53,6 @@ func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null 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 return false
} }

View File

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

View File

@ -115,6 +115,7 @@ func (iter *Iterator) ReadUint32() (ret uint32) {
func (iter *Iterator) readUint32(c byte) (ret uint32) { func (iter *Iterator) readUint32(c byte) (ret uint32) {
ind := intDigits[c] ind := intDigits[c]
if ind == 0 { if ind == 0 {
iter.assertInteger()
return 0 // single zero return 0 // single zero
} }
if ind == invalidCharForNumber { if ind == invalidCharForNumber {
@ -127,12 +128,14 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
ind2 := intDigits[iter.buf[i]] ind2 := intDigits[iter.buf[i]]
if ind2 == invalidCharForNumber { if ind2 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value return value
} }
i++ i++
ind3 := intDigits[iter.buf[i]] ind3 := intDigits[iter.buf[i]]
if ind3 == invalidCharForNumber { if ind3 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value*10 + uint32(ind2) return value*10 + uint32(ind2)
} }
//iter.head = i + 1 //iter.head = i + 1
@ -141,30 +144,35 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
ind4 := intDigits[iter.buf[i]] ind4 := intDigits[iter.buf[i]]
if ind4 == invalidCharForNumber { if ind4 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value*100 + uint32(ind2)*10 + uint32(ind3) return value*100 + uint32(ind2)*10 + uint32(ind3)
} }
i++ i++
ind5 := intDigits[iter.buf[i]] ind5 := intDigits[iter.buf[i]]
if ind5 == invalidCharForNumber { if ind5 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4) return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4)
} }
i++ i++
ind6 := intDigits[iter.buf[i]] ind6 := intDigits[iter.buf[i]]
if ind6 == invalidCharForNumber { if ind6 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5) return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5)
} }
i++ i++
ind7 := intDigits[iter.buf[i]] ind7 := intDigits[iter.buf[i]]
if ind7 == invalidCharForNumber { if ind7 == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6) return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6)
} }
i++ i++
ind8 := intDigits[iter.buf[i]] ind8 := intDigits[iter.buf[i]]
if ind8 == invalidCharForNumber { if ind8 == invalidCharForNumber {
iter.head = i 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) return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7)
} }
i++ i++
@ -172,6 +180,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) 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 iter.head = i
if ind9 == invalidCharForNumber { if ind9 == invalidCharForNumber {
iter.assertInteger()
return value return value
} }
} }
@ -180,6 +189,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
ind = intDigits[iter.buf[i]] ind = intDigits[iter.buf[i]]
if ind == invalidCharForNumber { if ind == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value return value
} }
if value > uint32SafeToMultiply10 { if value > uint32SafeToMultiply10 {
@ -194,6 +204,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
value = (value << 3) + (value << 1) + uint32(ind) value = (value << 3) + (value << 1) + uint32(ind)
} }
if !iter.loadMore() { if !iter.loadMore() {
iter.assertInteger()
return value return value
} }
} }
@ -226,6 +237,7 @@ func (iter *Iterator) ReadUint64() uint64 {
func (iter *Iterator) readUint64(c byte) (ret uint64) { func (iter *Iterator) readUint64(c byte) (ret uint64) {
ind := intDigits[c] ind := intDigits[c]
if ind == 0 { if ind == 0 {
iter.assertInteger()
return 0 // single zero return 0 // single zero
} }
if ind == invalidCharForNumber { if ind == invalidCharForNumber {
@ -233,11 +245,73 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
return return
} }
value := uint64(ind) 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 {
for i := iter.head; i < iter.tail; i++ { for i := iter.head; i < iter.tail; i++ {
ind = intDigits[iter.buf[i]] ind = intDigits[iter.buf[i]]
if ind == invalidCharForNumber { if ind == invalidCharForNumber {
iter.head = i iter.head = i
iter.assertInteger()
return value return value
} }
if value > uint64SafeToMultiple10 { if value > uint64SafeToMultiple10 {
@ -252,7 +326,14 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
value = (value << 3) + (value << 1) + uint64(ind) value = (value << 3) + (value << 1) + uint64(ind)
} }
if !iter.loadMore() { if !iter.loadMore() {
iter.assertInteger()
return value return value
} }
} }
} }
func (iter *Iterator) assertInteger() {
if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' {
iter.ReportError("assertInteger", "can not decode float as int")
}
}

View File

@ -3,7 +3,6 @@ package jsoniter
import ( import (
"fmt" "fmt"
"unicode" "unicode"
"unsafe"
) )
// ReadObject read one field from object. // ReadObject read one field from object.
@ -19,15 +18,25 @@ func (iter *Iterator) ReadObject() (ret string) {
c = iter.nextToken() c = iter.nextToken()
if c == '"' { if c == '"' {
iter.unreadByte() iter.unreadByte()
return string(iter.readObjectFieldAsBytes()) field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
} }
if c == '}' { if c == '}' {
return "" // end of object return "" // end of object
} }
iter.ReportError("ReadObject", `expect " after {`) iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
return return
case ',': case ',':
return string(iter.readObjectFieldAsBytes()) field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
case '}': case '}':
return "" // end of object return "" // end of object
default: default:
@ -36,62 +45,89 @@ func (iter *Iterator) ReadObject() (ret string) {
} }
} }
func (iter *Iterator) readFieldHash() int32 { // CaseInsensitive
func (iter *Iterator) readFieldHash() int64 {
hash := int64(0x811c9dc5) hash := int64(0x811c9dc5)
c := iter.nextToken() c := iter.nextToken()
if c == '"' { if c != '"' {
for { iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
for i := iter.head; i < iter.tail; i++ { return 0
// require ascii string and no escape }
b := iter.buf[i] for {
if 'A' <= b && b <= 'Z' { for i := iter.head; i < iter.tail; i++ {
b += 'a' - 'A' // require ascii string and no escape
} b := iter.buf[i]
if b == '"' { if b == '\\' {
iter.head = i + 1 iter.head = i
c = iter.nextToken() for _, b := range iter.readStringSlowPath() {
if c != ':' { if 'A' <= b && b <= 'Z' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) b += 'a' - 'A'
} }
return int32(hash) hash ^= int64(b)
hash *= 0x1000193
} }
hash ^= int64(b) c = iter.nextToken()
hash *= 0x1000193 if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
}
return hash
} }
if !iter.loadMore() { if b == '"' {
iter.ReportError("readFieldHash", `incomplete field name`) iter.head = i + 1
return 0 c = iter.nextToken()
if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
}
return hash
} }
if 'A' <= b && b <= 'Z' {
b += 'a' - 'A'
}
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 { func calcHash(str string) int64 {
hash := int64(0x811c9dc5) hash := int64(0x811c9dc5)
for _, b := range str { for _, b := range str {
hash ^= int64(unicode.ToLower(b)) hash ^= int64(unicode.ToLower(b))
hash *= 0x1000193 hash *= 0x1000193
} }
return int32(hash) return int64(hash)
} }
// ReadObjectCB read object with callback, the key is ascii only and field name not copied // ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken() c := iter.nextToken()
var field string
if c == '{' { if c == '{' {
c = iter.nextToken() c = iter.nextToken()
if c == '"' { if c == '"' {
iter.unreadByte() iter.unreadByte()
field := iter.readObjectFieldAsBytes() field = iter.ReadString()
if !callback(iter, *(*string)(unsafe.Pointer(&field))) { c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
return false return false
} }
c = iter.nextToken() c = iter.nextToken()
for c == ',' { for c == ',' {
field = iter.readObjectFieldAsBytes() field = iter.ReadString()
if !callback(iter, *(*string)(unsafe.Pointer(&field))) { c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
return false return false
} }
c = iter.nextToken() c = iter.nextToken()
@ -105,14 +141,14 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
if c == '}' { if c == '}' {
return true return true
} }
iter.ReportError("ReadObjectCB", `expect " after }`) iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c}))
return false return false
} }
if c == 'n' { if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null return true // null
} }
iter.ReportError("ReadObjectCB", `expect { or n`) iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
return false return false
} }
@ -125,7 +161,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
iter.unreadByte() iter.unreadByte()
field := iter.ReadString() field := iter.ReadString()
if iter.nextToken() != ':' { if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field") iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
return false return false
} }
if !callback(iter, field) { if !callback(iter, field) {
@ -135,7 +171,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
for c == ',' { for c == ',' {
field = iter.ReadString() field = iter.ReadString()
if iter.nextToken() != ':' { if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field") iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
return false return false
} }
if !callback(iter, field) { if !callback(iter, field) {
@ -152,14 +188,14 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
if c == '}' { if c == '}' {
return true return true
} }
iter.ReportError("ReadMapCB", `expect " after }`) iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c}))
return false return false
} }
if c == 'n' { if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null return true // null
} }
iter.ReportError("ReadMapCB", `expect { or n`) iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
return false return false
} }
@ -176,7 +212,7 @@ func (iter *Iterator) readObjectStart() bool {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return false return false
} }
iter.ReportError("readObjectStart", "expect { or n") iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
return false return false
} }
@ -192,7 +228,7 @@ func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
} }
} }
if iter.buf[iter.head] != ':' { if iter.buf[iter.head] != ':' {
iter.ReportError("readObjectFieldAsBytes", "expect : after object field") iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
return return
} }
iter.head++ iter.head++

View File

@ -14,7 +14,7 @@ func (iter *Iterator) ReadNil() (ret bool) {
return false return false
} }
// ReadBool reads a json object as Bool // ReadBool reads a json object as BoolValue
func (iter *Iterator) ReadBool() (ret bool) { func (iter *Iterator) ReadBool() (ret bool) {
c := iter.nextToken() c := iter.nextToken()
if c == 't' { if c == 't' {
@ -25,7 +25,7 @@ func (iter *Iterator) ReadBool() (ret bool) {
iter.skipFourBytes('a', 'l', 's', 'e') iter.skipFourBytes('a', 'l', 's', 'e')
return false return false
} }
iter.ReportError("ReadBool", "expect t or f") iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
return return
} }
@ -59,7 +59,9 @@ func (iter *Iterator) stopCapture() []byte {
iter.captureStartedAt = -1 iter.captureStartedAt = -1
iter.captured = nil iter.captured = nil
if len(captured) == 0 { if len(captured) == 0 {
return remaining copied := make([]byte, len(remaining))
copy(copied, remaining)
return copied
} }
captured = append(captured, remaining...) captured = append(captured, remaining...)
return captured return captured

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ func (iter *Iterator) ReadString() (ret string) {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return "" return ""
} }
iter.ReportError("ReadString", `expects " or n`) iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
return return
} }
@ -42,66 +42,75 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
} }
if c == '\\' { if c == '\\' {
c = iter.readByte() c = iter.readByte()
switch c { str = iter.readEscapedChar(c, str)
case 'u', 'U':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return
}
if c != '\\' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \ not found`)
return
}
c = iter.readByte()
if iter.Error != nil {
return
}
if c != 'u' && c != 'U' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \u not found`)
return
}
r2 := iter.readU4()
if iter.Error != nil {
return
}
combined := utf16.DecodeRune(r, r2)
str = appendRune(str, combined)
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("ReadString",
`invalid escape char after \`)
return
}
} else { } else {
str = append(str, c) str = append(str, c)
} }
} }
iter.ReportError("ReadString", "unexpected end of input") iter.ReportError("readStringSlowPath", "unexpected end of input")
return return
} }
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
switch c {
case 'u':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != '\\' {
iter.unreadByte()
str = appendRune(str, r)
return str
}
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != 'u' {
str = appendRune(str, r)
return iter.readEscapedChar(c, str)
}
r2 := iter.readU4()
if iter.Error != nil {
return nil
}
combined := utf16.DecodeRune(r, r2)
if combined == '\uFFFD' {
str = appendRune(str, r)
str = appendRune(str, r2)
} else {
str = appendRune(str, combined)
}
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("readEscapedChar",
`invalid escape char after \`)
return nil
}
return str
}
// ReadStringAsSlice read string from iterator without copying into string form. // ReadStringAsSlice read string from iterator without copying into string form.
// The []byte can not be kept, as it will change after next iterator call. // The []byte can not be kept, as it will change after next iterator call.
func (iter *Iterator) ReadStringAsSlice() (ret []byte) { func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
@ -130,7 +139,7 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
} }
return copied return copied
} }
iter.ReportError("ReadString", `expects " or n`) iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
return return
} }
@ -147,7 +156,7 @@ func (iter *Iterator) readU4() (ret rune) {
} else if c >= 'A' && c <= 'F' { } else if c >= 'A' && c <= 'F' {
ret = ret*16 + rune(c-'A'+10) ret = ret*16 + rune(c-'A'+10)
} else { } 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 return
} }
} }

31
feature_json_number.go Normal file
View File

@ -0,0 +1,31 @@
package jsoniter
import (
"encoding/json"
"strconv"
)
type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
func CastJsonNumber(val interface{}) (string, bool) {
switch typedVal := val.(type) {
case json.Number:
return string(typedVal), true
case Number:
return string(typedVal), true
}
return "", false
}

View File

@ -28,6 +28,7 @@ func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
func (cfg *frozenConfig) ReturnStream(stream *Stream) { func (cfg *frozenConfig) ReturnStream(stream *Stream) {
stream.Error = nil stream.Error = nil
stream.Attachment = nil
select { select {
case cfg.streamPool <- stream: case cfg.streamPool <- stream:
return return
@ -48,6 +49,7 @@ func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator {
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) { func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
iter.Error = nil iter.Error = nil
iter.Attachment = nil
select { select {
case cfg.iteratorPool <- iter: case cfg.iteratorPool <- iter:
return return

File diff suppressed because it is too large Load Diff

View File

@ -7,23 +7,34 @@ import (
"unsafe" "unsafe"
) )
func decoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) { func decoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
decoder, err := decoderOfType(cfg, typ.Elem()) decoder := decoderOfType(cfg, prefix+"[array]->", typ.Elem())
if err != nil { return &arrayDecoder{typ, typ.Elem(), decoder}
return nil, err
}
return &arrayDecoder{typ, typ.Elem(), decoder}, nil
} }
func encoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) { func encoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
encoder, err := encoderOfType(cfg, typ.Elem()) if typ.Len() == 0 {
if err != nil { return emptyArrayEncoder{}
return nil, err
} }
encoder := encoderOfType(cfg, prefix+"[array]->", typ.Elem())
if typ.Elem().Kind() == reflect.Map { if typ.Elem().Kind() == reflect.Map {
encoder = &optionalEncoder{encoder} encoder = &OptionalEncoder{encoder}
} }
return &arrayEncoder{typ, typ.Elem(), encoder}, nil return &arrayEncoder{typ, typ.Elem(), encoder}
}
type emptyArrayEncoder struct{}
func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteEmptyArray()
}
func (encoder emptyArrayEncoder) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteEmptyArray()
}
func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return true
} }
type arrayEncoder struct { type arrayEncoder struct {

View File

@ -161,22 +161,31 @@ func RegisterExtension(extension Extension) {
extensions = append(extensions, extension) extensions = append(extensions, extension)
} }
func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder { func getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecoder {
decoder := _getTypeDecoderFromExtension(typ) decoder := _getTypeDecoderFromExtension(cfg, typ)
if decoder != nil { if decoder != nil {
for _, extension := range extensions { for _, extension := range extensions {
decoder = extension.DecorateDecoder(typ, decoder) decoder = extension.DecorateDecoder(typ, decoder)
} }
for _, extension := range cfg.extensions {
decoder = extension.DecorateDecoder(typ, decoder)
}
} }
return decoder return decoder
} }
func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder { func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecoder {
for _, extension := range extensions { for _, extension := range extensions {
decoder := extension.CreateDecoder(typ) decoder := extension.CreateDecoder(typ)
if decoder != nil { if decoder != nil {
return decoder return decoder
} }
} }
for _, extension := range cfg.extensions {
decoder := extension.CreateDecoder(typ)
if decoder != nil {
return decoder
}
}
typeName := typ.String() typeName := typ.String()
decoder := typeDecoders[typeName] decoder := typeDecoders[typeName]
if decoder != nil { if decoder != nil {
@ -185,29 +194,38 @@ func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
decoder := typeDecoders[typ.Elem().String()] decoder := typeDecoders[typ.Elem().String()]
if decoder != nil { if decoder != nil {
return &optionalDecoder{typ.Elem(), decoder} return &OptionalDecoder{typ.Elem(), decoder}
} }
} }
return nil return nil
} }
func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder { func getTypeEncoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValEncoder {
encoder := _getTypeEncoderFromExtension(typ) encoder := _getTypeEncoderFromExtension(cfg, typ)
if encoder != nil { if encoder != nil {
for _, extension := range extensions { for _, extension := range extensions {
encoder = extension.DecorateEncoder(typ, encoder) encoder = extension.DecorateEncoder(typ, encoder)
} }
for _, extension := range cfg.extensions {
encoder = extension.DecorateEncoder(typ, encoder)
}
} }
return encoder return encoder
} }
func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder { func _getTypeEncoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValEncoder {
for _, extension := range extensions { for _, extension := range extensions {
encoder := extension.CreateEncoder(typ) encoder := extension.CreateEncoder(typ)
if encoder != nil { if encoder != nil {
return encoder return encoder
} }
} }
for _, extension := range cfg.extensions {
encoder := extension.CreateEncoder(typ)
if encoder != nil {
return encoder
}
}
typeName := typ.String() typeName := typ.String()
encoder := typeEncoders[typeName] encoder := typeEncoders[typeName]
if encoder != nil { if encoder != nil {
@ -216,28 +234,28 @@ func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
encoder := typeEncoders[typ.Elem().String()] encoder := typeEncoders[typ.Elem().String()]
if encoder != nil { if encoder != nil {
return &optionalEncoder{encoder} return &OptionalEncoder{encoder}
} }
} }
return nil return nil
} }
func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) { func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructDescriptor {
embeddedBindings := []*Binding{} embeddedBindings := []*Binding{}
bindings := []*Binding{} bindings := []*Binding{}
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
tag := field.Tag.Get("json") tag, hastag := field.Tag.Lookup(cfg.getTagKey())
if cfg.onlyTaggedField && !hastag {
continue
}
tagParts := strings.Split(tag, ",") tagParts := strings.Split(tag, ",")
if tag == "-" { if tag == "-" {
continue continue
} }
if field.Anonymous && (tag == "" || tagParts[0] == "") { if field.Anonymous && (tag == "" || tagParts[0] == "") {
if field.Type.Kind() == reflect.Struct { if field.Type.Kind() == reflect.Struct {
structDescriptor, err := describeStruct(cfg, field.Type) structDescriptor := describeStruct(cfg, prefix, field.Type)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
binding.levels = append([]int{i}, binding.levels...) binding.levels = append([]int{i}, binding.levels...)
omitempty := binding.Encoder.(*structFieldEncoder).omitempty omitempty := binding.Encoder.(*structFieldEncoder).omitempty
@ -247,16 +265,13 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
} }
continue continue
} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { } else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
structDescriptor, err := describeStruct(cfg, field.Type.Elem()) structDescriptor := describeStruct(cfg, prefix, field.Type.Elem())
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
binding.levels = append([]int{i}, binding.levels...) binding.levels = append([]int{i}, binding.levels...)
omitempty := binding.Encoder.(*structFieldEncoder).omitempty omitempty := binding.Encoder.(*structFieldEncoder).omitempty
binding.Encoder = &optionalEncoder{binding.Encoder} binding.Encoder = &dereferenceEncoder{binding.Encoder}
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty} binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder} binding.Decoder = &dereferenceDecoder{field.Type.Elem(), binding.Decoder}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder} binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
embeddedBindings = append(embeddedBindings, binding) embeddedBindings = append(embeddedBindings, binding)
} }
@ -267,22 +282,15 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name) fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
decoder := fieldDecoders[fieldCacheKey] decoder := fieldDecoders[fieldCacheKey]
if decoder == nil { if decoder == nil {
var err error decoder = decoderOfType(cfg, prefix+typ.String()+"."+field.Name+"->", field.Type)
decoder, err = decoderOfType(cfg, field.Type)
if err != nil {
return nil, err
}
} }
encoder := fieldEncoders[fieldCacheKey] encoder := fieldEncoders[fieldCacheKey]
if encoder == nil { if encoder == nil {
var err error encoder = encoderOfType(cfg, prefix+typ.String()+"."+field.Name+"->", field.Type)
encoder, err = encoderOfType(cfg, field.Type) // map is stored as pointer in the struct,
if err != nil { // and treat nil or empty map as empty field
return nil, err if encoder != nil && field.Type.Kind() == reflect.Map {
} encoder = &optionalMapEncoder{encoder}
// map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
} }
} }
binding := &Binding{ binding := &Binding{
@ -295,7 +303,7 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
binding.levels = []int{i} binding.levels = []int{i}
bindings = append(bindings, binding) bindings = append(bindings, binding)
} }
return createStructDescriptor(cfg, typ, bindings, embeddedBindings), nil return createStructDescriptor(cfg, typ, bindings, embeddedBindings)
} }
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor { func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
onePtrEmbedded := false onePtrEmbedded := false
@ -323,6 +331,9 @@ func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Bin
for _, extension := range extensions { for _, extension := range extensions {
extension.UpdateStructDescriptor(structDescriptor) extension.UpdateStructDescriptor(structDescriptor)
} }
for _, extension := range cfg.extensions {
extension.UpdateStructDescriptor(structDescriptor)
}
processTags(structDescriptor, cfg) processTags(structDescriptor, cfg)
// merge normal & embedded bindings & sort with original order // merge normal & embedded bindings & sort with original order
allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...)) allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
@ -373,7 +384,7 @@ func (bindings sortableBindings) Swap(i, j int) {
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) { func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
shouldOmitEmpty := false shouldOmitEmpty := false
tagParts := strings.Split(binding.Field.Tag.Get("json"), ",") tagParts := strings.Split(binding.Field.Tag.Get(cfg.getTagKey()), ",")
for _, tagPart := range tagParts[1:] { for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" { if tagPart == "omitempty" {
shouldOmitEmpty = true shouldOmitEmpty = true

View File

@ -9,6 +9,22 @@ import (
"unsafe" "unsafe"
) )
func decoderOfMap(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
decoder := decoderOfType(cfg, prefix+"[map]->", typ.Elem())
mapInterface := reflect.New(typ).Interface()
return &mapDecoder{typ, typ.Key(), typ.Elem(), decoder, extractInterface(mapInterface)}
}
func encoderOfMap(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
elemType := typ.Elem()
encoder := encoderOfType(cfg, prefix+"[map]->", elemType)
mapInterface := reflect.New(typ).Elem().Interface()
if cfg.sortMapKeys {
return &sortKeysMapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}
}
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}
}
type mapDecoder struct { type mapDecoder struct {
mapType reflect.Type mapType reflect.Type
keyType reflect.Type keyType reflect.Type
@ -32,7 +48,7 @@ func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
} }
iter.ReadMapCB(func(iter *Iterator, keyStr string) bool { iter.ReadMapCB(func(iter *Iterator, keyStr string) bool {
elem := reflect.New(decoder.elemType) elem := reflect.New(decoder.elemType)
decoder.elemDecoder.Decode(unsafe.Pointer(elem.Pointer()), iter) decoder.elemDecoder.Decode(extractInterface(elem.Interface()).word, iter)
// to put into map, we have to use reflection // to put into map, we have to use reflection
keyType := decoder.keyType keyType := decoder.keyType
// TODO: remove this from loop // TODO: remove this from loop

View File

@ -32,7 +32,9 @@ type intCodec struct {
} }
func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int)(ptr)) = iter.ReadInt() if !iter.ReadNil() {
*((*int)(ptr)) = iter.ReadInt()
}
} }
func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -51,7 +53,9 @@ type uintptrCodec struct {
} }
func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64()) if !iter.ReadNil() {
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
}
} }
func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -70,7 +74,9 @@ type int8Codec struct {
} }
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int8)(ptr)) = iter.ReadInt8() if !iter.ReadNil() {
*((*int8)(ptr)) = iter.ReadInt8()
}
} }
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -89,7 +95,9 @@ type int16Codec struct {
} }
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int16)(ptr)) = iter.ReadInt16() if !iter.ReadNil() {
*((*int16)(ptr)) = iter.ReadInt16()
}
} }
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -108,7 +116,9 @@ type int32Codec struct {
} }
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int32)(ptr)) = iter.ReadInt32() if !iter.ReadNil() {
*((*int32)(ptr)) = iter.ReadInt32()
}
} }
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -127,7 +137,9 @@ type int64Codec struct {
} }
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int64)(ptr)) = iter.ReadInt64() if !iter.ReadNil() {
*((*int64)(ptr)) = iter.ReadInt64()
}
} }
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -146,7 +158,10 @@ type uintCodec struct {
} }
func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint)(ptr)) = iter.ReadUint() if !iter.ReadNil() {
*((*uint)(ptr)) = iter.ReadUint()
return
}
} }
func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -165,7 +180,9 @@ type uint8Codec struct {
} }
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint8)(ptr)) = iter.ReadUint8() if !iter.ReadNil() {
*((*uint8)(ptr)) = iter.ReadUint8()
}
} }
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -184,7 +201,9 @@ type uint16Codec struct {
} }
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint16)(ptr)) = iter.ReadUint16() if !iter.ReadNil() {
*((*uint16)(ptr)) = iter.ReadUint16()
}
} }
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -203,7 +222,9 @@ type uint32Codec struct {
} }
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint32)(ptr)) = iter.ReadUint32() if !iter.ReadNil() {
*((*uint32)(ptr)) = iter.ReadUint32()
}
} }
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -222,7 +243,9 @@ type uint64Codec struct {
} }
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint64)(ptr)) = iter.ReadUint64() if !iter.ReadNil() {
*((*uint64)(ptr)) = iter.ReadUint64()
}
} }
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -241,7 +264,9 @@ type float32Codec struct {
} }
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float32)(ptr)) = iter.ReadFloat32() if !iter.ReadNil() {
*((*float32)(ptr)) = iter.ReadFloat32()
}
} }
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -260,7 +285,9 @@ type float64Codec struct {
} }
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float64)(ptr)) = iter.ReadFloat64() if !iter.ReadNil() {
*((*float64)(ptr)) = iter.ReadFloat64()
}
} }
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -279,7 +306,9 @@ type boolCodec struct {
} }
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*bool)(ptr)) = iter.ReadBool() if !iter.ReadNil() {
*((*bool)(ptr)) = iter.ReadBool()
}
} }
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -298,7 +327,42 @@ type emptyInterfaceCodec struct {
} }
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.Read() existing := *((*interface{})(ptr))
// Checking for both typed and untyped nil pointers.
if existing != nil &&
reflect.TypeOf(existing).Kind() == reflect.Ptr &&
!reflect.ValueOf(existing).IsNil() {
var ptrToExisting interface{}
for {
elem := reflect.ValueOf(existing).Elem()
if elem.Kind() != reflect.Ptr || elem.IsNil() {
break
}
ptrToExisting = existing
existing = elem.Interface()
}
if iter.ReadNil() {
if ptrToExisting != nil {
nilPtr := reflect.Zero(reflect.TypeOf(ptrToExisting).Elem())
reflect.ValueOf(ptrToExisting).Elem().Set(nilPtr)
} else {
*((*interface{})(ptr)) = nil
}
} else {
iter.ReadVal(existing)
}
return
}
if iter.ReadNil() {
*((*interface{})(ptr)) = nil
} else {
*((*interface{})(ptr)) = iter.Read()
}
} }
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -310,13 +374,19 @@ func (codec *emptyInterfaceCodec) EncodeInterface(val interface{}, stream *Strea
} }
func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool { func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
return ptr == nil emptyInterface := (*emptyInterface)(ptr)
return emptyInterface.typ == nil
} }
type nonEmptyInterfaceCodec struct { type nonEmptyInterfaceCodec struct {
} }
func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == NilValue {
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*interface{})(ptr)) = nil
return
}
nonEmptyInterface := (*nonEmptyInterface)(ptr) nonEmptyInterface := (*nonEmptyInterface)(ptr)
if nonEmptyInterface.itab == nil { if nonEmptyInterface.itab == nil {
iter.ReportError("read non-empty interface", "do not know which concrete type to decode to") iter.ReportError("read non-empty interface", "do not know which concrete type to decode to")
@ -327,15 +397,20 @@ func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator)
e.typ = nonEmptyInterface.itab.typ e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word e.word = nonEmptyInterface.word
iter.ReadVal(&i) iter.ReadVal(&i)
if e.word == nil {
nonEmptyInterface.itab = nil
}
nonEmptyInterface.word = e.word nonEmptyInterface.word = e.word
} }
func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
nonEmptyInterface := (*nonEmptyInterface)(ptr) nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{} var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i)) if nonEmptyInterface.itab != nil {
e.typ = nonEmptyInterface.itab.typ e := (*emptyInterface)(unsafe.Pointer(&i))
e.word = nonEmptyInterface.word e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
}
stream.WriteVal(i) stream.WriteVal(i)
} }
@ -371,21 +446,76 @@ type jsonNumberCodec struct {
} }
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString())) switch iter.WhatIsNext() {
case StringValue:
*((*json.Number)(ptr)) = json.Number(iter.ReadString())
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*json.Number)(ptr)) = ""
default:
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
}
} }
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*json.Number)(ptr)))) number := *((*json.Number)(ptr))
if len(number) == 0 {
stream.WriteRaw("0")
} else {
stream.WriteRaw(string(number))
}
} }
func (codec *jsonNumberCodec) EncodeInterface(val interface{}, stream *Stream) { func (codec *jsonNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(json.Number))) number := val.(json.Number)
if len(number) == 0 {
stream.WriteRaw("0")
} else {
stream.WriteRaw(string(number))
}
} }
func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.Number)(ptr))) == 0 return len(*((*json.Number)(ptr))) == 0
} }
type jsoniterNumberCodec struct {
}
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case StringValue:
*((*Number)(ptr)) = Number(iter.ReadString())
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*Number)(ptr)) = ""
default:
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
}
}
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
number := *((*Number)(ptr))
if len(number) == 0 {
stream.WriteRaw("0")
} else {
stream.WriteRaw(string(number))
}
}
func (codec *jsoniterNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
number := val.(Number)
if len(number) == 0 {
stream.WriteRaw("0")
} else {
stream.WriteRaw(string(number))
}
}
func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*Number)(ptr))) == 0
}
type jsonRawMessageCodec struct { type jsonRawMessageCodec struct {
} }
@ -425,7 +555,7 @@ func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
} }
type base64Codec struct { type base64Codec struct {
actualType reflect.Type sliceDecoder ValDecoder
} }
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
@ -436,21 +566,28 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
ptrSlice.Data = nil ptrSlice.Data = nil
return return
} }
encoding := base64.StdEncoding switch iter.WhatIsNext() {
src := iter.SkipAndReturnBytes() case StringValue:
src = src[1 : len(src)-1] encoding := base64.StdEncoding
decodedLen := encoding.DecodedLen(len(src)) src := iter.SkipAndReturnBytes()
dst := make([]byte, decodedLen) src = src[1 : len(src)-1]
len, err := encoding.Decode(dst, src) decodedLen := encoding.DecodedLen(len(src))
if err != nil { dst := make([]byte, decodedLen)
iter.ReportError("decode base64", err.Error()) len, err := encoding.Decode(dst, src)
} else { if err != nil {
dst = dst[:len] iter.ReportError("decode base64", err.Error())
dstSlice := (*sliceHeader)(unsafe.Pointer(&dst)) } else {
ptrSlice := (*sliceHeader)(ptr) dst = dst[:len]
ptrSlice.Data = dstSlice.Data dstSlice := (*sliceHeader)(unsafe.Pointer(&dst))
ptrSlice.Cap = dstSlice.Cap ptrSlice := (*sliceHeader)(ptr)
ptrSlice.Len = dstSlice.Len ptrSlice.Data = dstSlice.Data
ptrSlice.Cap = dstSlice.Cap
ptrSlice.Len = dstSlice.Len
}
case ArrayValue:
codec.sliceDecoder.Decode(ptr, iter)
default:
iter.ReportError("base64Codec", "invalid input")
} }
} }
@ -462,10 +599,10 @@ func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
} }
encoding := base64.StdEncoding encoding := base64.StdEncoding
stream.writeByte('"') stream.writeByte('"')
toGrow := encoding.EncodedLen(len(src)) size := encoding.EncodedLen(len(src))
stream.ensure(toGrow) buf := make([]byte, size)
encoding.Encode(stream.buf[stream.n:], src) encoding.Encode(buf, src)
stream.n += toGrow stream.buf = append(stream.buf, buf...)
stream.writeByte('"') stream.writeByte('"')
} }
@ -478,10 +615,10 @@ func (codec *base64Codec) EncodeInterface(val interface{}, stream *Stream) {
} }
encoding := base64.StdEncoding encoding := base64.StdEncoding
stream.writeByte('"') stream.writeByte('"')
toGrow := encoding.EncodedLen(len(src)) size := encoding.EncodedLen(len(src))
stream.ensure(toGrow) buf := make([]byte, size)
encoding.Encode(stream.buf[stream.n:], src) encoding.Encode(buf, src)
stream.n += toGrow stream.buf = append(stream.buf, buf...)
stream.writeByte('"') stream.writeByte('"')
} }
@ -496,7 +633,7 @@ type stringModeNumberDecoder struct {
func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
c := iter.nextToken() c := iter.nextToken()
if c != '"' { if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`) iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c}))
return return
} }
decoder.elemDecoder.Decode(ptr, iter) decoder.elemDecoder.Decode(ptr, iter)
@ -505,7 +642,7 @@ func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
} }
c = iter.readByte() c = iter.readByte()
if c != '"' { if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`) iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c}))
return return
} }
} }
@ -570,7 +707,12 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface := encoder.templateInterface templateInterface := encoder.templateInterface
templateInterface.word = ptr templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface)) realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler) marshaler, ok := (*realInterface).(json.Marshaler)
if !ok {
stream.WriteVal(nil)
return
}
bytes, err := marshaler.MarshalJSON() bytes, err := marshaler.MarshalJSON()
if err != nil { if err != nil {
stream.Error = err stream.Error = err

View File

@ -7,17 +7,14 @@ import (
"unsafe" "unsafe"
) )
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) { func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
type bindingTo struct { type bindingTo struct {
binding *Binding binding *Binding
toName string toName string
ignored bool ignored bool
} }
orderedBindings := []*bindingTo{} orderedBindings := []*bindingTo{}
structDescriptor, err := describeStruct(cfg, typ) structDescriptor := describeStruct(cfg, prefix, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
for _, toName := range binding.ToNames { for _, toName := range binding.ToNames {
new := &bindingTo{ new := &bindingTo{
@ -28,13 +25,13 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
if old.toName != toName { if old.toName != toName {
continue continue
} }
old.ignored, new.ignored = resolveConflictBinding(old.binding, new.binding) old.ignored, new.ignored = resolveConflictBinding(cfg, old.binding, new.binding)
} }
orderedBindings = append(orderedBindings, new) orderedBindings = append(orderedBindings, new)
} }
} }
if len(orderedBindings) == 0 { if len(orderedBindings) == 0 {
return &emptyStructEncoder{}, nil return &emptyStructEncoder{}
} }
finalOrderedFields := []structFieldTo{} finalOrderedFields := []structFieldTo{}
for _, bindingTo := range orderedBindings { for _, bindingTo := range orderedBindings {
@ -45,12 +42,13 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
}) })
} }
} }
return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil return &structEncoder{typ, structDescriptor.onePtrEmbedded,
structDescriptor.onePtrOptimization, finalOrderedFields}
} }
func resolveConflictBinding(old, new *Binding) (ignoreOld, ignoreNew bool) { func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
newTagged := new.Field.Tag.Get("json") != "" newTagged := new.Field.Tag.Get(cfg.getTagKey()) != ""
oldTagged := old.Field.Tag.Get("json") != "" oldTagged := old.Field.Tag.Get(cfg.getTagKey()) != ""
if newTagged { if newTagged {
if oldTagged { if oldTagged {
if len(old.levels) > len(new.levels) { if len(old.levels) > len(new.levels) {
@ -77,12 +75,9 @@ func resolveConflictBinding(old, new *Binding) (ignoreOld, ignoreNew bool) {
} }
} }
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) { func decoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
bindings := map[string]*Binding{} bindings := map[string]*Binding{}
structDescriptor, err := describeStruct(cfg, typ) structDescriptor := describeStruct(cfg, prefix, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
for _, fromName := range binding.FromNames { for _, fromName := range binding.FromNames {
old := bindings[fromName] old := bindings[fromName]
@ -90,7 +85,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
bindings[fromName] = binding bindings[fromName] = binding
continue continue
} }
ignoreOld, ignoreNew := resolveConflictBinding(old, binding) ignoreOld, ignoreNew := resolveConflictBinding(cfg, old, binding)
if ignoreOld { if ignoreOld {
delete(bindings, fromName) delete(bindings, fromName)
} }
@ -103,7 +98,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
for k, binding := range bindings { for k, binding := range bindings {
fields[k] = binding.Decoder.(*structFieldDecoder) fields[k] = binding.Decoder.(*structFieldDecoder)
} }
return createStructDecoder(typ, fields) return createStructDecoder(cfg, typ, fields)
} }
type structFieldEncoder struct { type structFieldEncoder struct {
@ -130,6 +125,7 @@ func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
} }
type structEncoder struct { type structEncoder struct {
typ reflect.Type
onePtrEmbedded bool onePtrEmbedded bool
onePtrOptimization bool onePtrOptimization bool
fields []structFieldTo fields []structFieldTo
@ -155,6 +151,9 @@ func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
isNotFirst = true isNotFirst = true
} }
stream.WriteObjectEnd() stream.WriteObjectEnd()
if stream.Error != nil && stream.Error != io.EOF {
stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error())
}
} }
func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) { func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {

124
feature_reflect_optional.go Normal file
View File

@ -0,0 +1,124 @@
package jsoniter
import (
"reflect"
"unsafe"
)
func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
elemType := typ.Elem()
decoder := decoderOfType(cfg, prefix, elemType)
return &OptionalDecoder{elemType, decoder}
}
func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
elemType := typ.Elem()
elemEncoder := encoderOfType(cfg, prefix, elemType)
encoder := &OptionalEncoder{elemEncoder}
if elemType.Kind() == reflect.Map {
encoder = &OptionalEncoder{encoder}
}
return encoder
}
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 dereferenceDecoder struct {
// only to deference a pointer
valueType reflect.Type
valueDecoder ValDecoder
}
func (decoder *dereferenceDecoder) 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 {
return *((*unsafe.Pointer)(ptr)) == nil
}
type dereferenceEncoder struct {
ValueEncoder ValEncoder
}
func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
if *((*unsafe.Pointer)(ptr)) == nil {
stream.WriteNil()
} else {
encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
}
}
func (encoder *dereferenceEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.ValueEncoder.IsEmpty(*((*unsafe.Pointer)(ptr)))
}
type optionalMapEncoder struct {
valueEncoder ValEncoder
}
func (encoder *optionalMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
if *((*unsafe.Pointer)(ptr)) == nil {
stream.WriteNil()
} else {
encoder.valueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
}
}
func (encoder *optionalMapEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *optionalMapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
p := *((*unsafe.Pointer)(ptr))
return p == nil || encoder.valueEncoder.IsEmpty(p)
}

View File

@ -7,23 +7,17 @@ import (
"unsafe" "unsafe"
) )
func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) { func decoderOfSlice(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
decoder, err := decoderOfType(cfg, typ.Elem()) decoder := decoderOfType(cfg, prefix+"[slice]->", typ.Elem())
if err != nil { return &sliceDecoder{typ, typ.Elem(), decoder}
return nil, err
}
return &sliceDecoder{typ, typ.Elem(), decoder}, nil
} }
func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) { func encoderOfSlice(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
encoder, err := encoderOfType(cfg, typ.Elem()) encoder := encoderOfType(cfg, prefix+"[slice]->", typ.Elem())
if err != nil {
return nil, err
}
if typ.Elem().Kind() == reflect.Map { if typ.Elem().Kind() == reflect.Map {
encoder = &optionalEncoder{encoder} encoder = &OptionalEncoder{encoder}
} }
return &sliceEncoder{typ, typ.Elem(), encoder}, nil return &sliceEncoder{typ, typ.Elem(), encoder}
} }
type sliceEncoder struct { type sliceEncoder struct {
@ -124,24 +118,26 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
} }
} }
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer()) newVal := reflect.MakeSlice(sliceType, newLen, newCap).Interface()
newValPtr := extractInterface(newVal).word
dst := (*sliceHeader)(newValPtr).Data
// copy old array into new array // copy old array into new array
originalBytesCount := uintptr(slice.Len) * elementType.Size() originalBytesCount := slice.Len * int(elementType.Size())
srcPtr := (*[1 << 30]byte)(slice.Data) srcSliceHeader := (unsafe.Pointer)(&sliceHeader{slice.Data, originalBytesCount, originalBytesCount})
dstPtr := (*[1 << 30]byte)(dst) dstSliceHeader := (unsafe.Pointer)(&sliceHeader{dst, originalBytesCount, originalBytesCount})
for i := uintptr(0); i < originalBytesCount; i++ { copy(*(*[]byte)(dstSliceHeader), *(*[]byte)(srcSliceHeader))
dstPtr[i] = srcPtr[i] slice.Data = dst
}
slice.Len = newLen slice.Len = newLen
slice.Cap = newCap slice.Cap = newCap
slice.Data = dst
} }
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) { func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
if expectedCap <= slice.Cap { if expectedCap <= slice.Cap {
return return
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer()) newVal := reflect.MakeSlice(sliceType, 0, expectedCap).Interface()
slice.Cap = expectedCap newValPtr := extractInterface(newVal).word
dst := (*sliceHeader)(newValPtr).Data
slice.Data = dst slice.Data = dst
slice.Cap = expectedCap
} }

View File

@ -4,36 +4,40 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unsafe" "unsafe"
) )
func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (ValDecoder, error) { func createStructDecoder(cfg *frozenConfig, typ reflect.Type, fields map[string]*structFieldDecoder) ValDecoder {
knownHash := map[int32]struct{}{ if cfg.disallowUnknownFields {
return &generalStructDecoder{typ: typ, fields: fields, disallowUnknownFields: true}
}
knownHash := map[int64]struct{}{
0: {}, 0: {},
} }
switch len(fields) { switch len(fields) {
case 0: case 0:
return &skipObjectDecoder{typ}, nil return &skipObjectDecoder{typ}
case 1: case 1:
for fieldName, fieldDecoder := range fields { for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder}, nil return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder}
} }
case 2: case 2:
var fieldHash1 int32 var fieldHash1 int64
var fieldHash2 int32 var fieldHash2 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
for fieldName, fieldDecoder := range fields { for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldHash1 == 0 { if fieldHash1 == 0 {
@ -44,11 +48,11 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldDecoder2 = fieldDecoder fieldDecoder2 = fieldDecoder
} }
} }
return &twoFieldsStructDecoder{typ, fieldHash1, fieldDecoder1, fieldHash2, fieldDecoder2}, nil return &twoFieldsStructDecoder{typ, fieldHash1, fieldDecoder1, fieldHash2, fieldDecoder2}
case 3: case 3:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -56,7 +60,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -71,12 +75,14 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &threeFieldsStructDecoder{typ, return &threeFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil fieldName1, fieldDecoder1,
fieldName2, fieldDecoder2,
fieldName3, fieldDecoder3}
case 4: case 4:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -85,7 +91,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -103,14 +109,16 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &fourFieldsStructDecoder{typ, return &fourFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4}, nil fieldName2, fieldDecoder2,
fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}
case 5: case 5:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -120,7 +128,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -141,15 +149,18 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &fiveFieldsStructDecoder{typ, return &fiveFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil fieldName2, fieldDecoder2,
fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5}
case 6: case 6:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldName6 int32 var fieldName6 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -160,7 +171,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -184,16 +195,20 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &sixFieldsStructDecoder{typ, return &sixFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil fieldName2, fieldDecoder2,
fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5,
fieldName6, fieldDecoder6}
case 7: case 7:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldName6 int32 var fieldName6 int64
var fieldName7 int32 var fieldName7 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -205,7 +220,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -232,18 +247,22 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &sevenFieldsStructDecoder{typ, return &sevenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6, fieldName2, fieldDecoder2,
fieldName7, fieldDecoder7}, nil fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5,
fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}
case 8: case 8:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldName6 int32 var fieldName6 int64
var fieldName7 int32 var fieldName7 int64
var fieldName8 int32 var fieldName8 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -256,7 +275,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -286,19 +305,24 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &eightFieldsStructDecoder{typ, return &eightFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6, fieldName2, fieldDecoder2,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5,
fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7,
fieldName8, fieldDecoder8}
case 9: case 9:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldName6 int32 var fieldName6 int64
var fieldName7 int32 var fieldName7 int64
var fieldName8 int32 var fieldName8 int64
var fieldName9 int32 var fieldName9 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -312,7 +336,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -345,20 +369,26 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &nineFieldsStructDecoder{typ, return &nineFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6, fieldName2, fieldDecoder2,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5,
fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7,
fieldName8, fieldDecoder8,
fieldName9, fieldDecoder9}
case 10: case 10:
var fieldName1 int32 var fieldName1 int64
var fieldName2 int32 var fieldName2 int64
var fieldName3 int32 var fieldName3 int64
var fieldName4 int32 var fieldName4 int64
var fieldName5 int32 var fieldName5 int64
var fieldName6 int32 var fieldName6 int64
var fieldName7 int32 var fieldName7 int64
var fieldName8 int32 var fieldName8 int64
var fieldName9 int32 var fieldName9 int64
var fieldName10 int32 var fieldName10 int64
var fieldDecoder1 *structFieldDecoder var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder var fieldDecoder3 *structFieldDecoder
@ -373,7 +403,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
fieldHash := calcHash(fieldName) fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash] _, known := knownHash[fieldHash]
if known { if known {
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
knownHash[fieldHash] = struct{}{} knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 { if fieldName1 == 0 {
@ -409,44 +439,73 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
} }
} }
return &tenFieldsStructDecoder{typ, return &tenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3, fieldName1, fieldDecoder1,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6, fieldName2, fieldDecoder2,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9, fieldName3, fieldDecoder3,
fieldName10, fieldDecoder10}, nil fieldName4, fieldDecoder4,
fieldName5, fieldDecoder5,
fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7,
fieldName8, fieldDecoder8,
fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}
} }
return &generalStructDecoder{typ, fields}, nil return &generalStructDecoder{typ, fields, false}
} }
type generalStructDecoder struct { type generalStructDecoder struct {
typ reflect.Type typ reflect.Type
fields map[string]*structFieldDecoder fields map[string]*structFieldDecoder
disallowUnknownFields bool
} }
func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() { if !iter.readObjectStart() {
return return
} }
fieldBytes := iter.readObjectFieldAsBytes() decoder.decodeOneField(ptr, iter)
field := *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder := decoder.fields[field]
if fieldDecoder == nil {
iter.Skip()
} else {
fieldDecoder.Decode(ptr, iter)
}
for iter.nextToken() == ',' { for iter.nextToken() == ',' {
fieldBytes = iter.readObjectFieldAsBytes() decoder.decodeOneField(ptr, iter)
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
}
}
func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
var field string
var fieldDecoder *structFieldDecoder
if iter.cfg.objectFieldMustBeSimpleString {
fieldBytes := iter.ReadStringAsSlice()
field = *(*string)(unsafe.Pointer(&fieldBytes)) field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field] fieldDecoder = decoder.fields[field]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() fieldDecoder = decoder.fields[strings.ToLower(field)]
} else { }
fieldDecoder.Decode(ptr, iter) } else {
field = iter.ReadString()
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil {
fieldDecoder = decoder.fields[strings.ToLower(field)]
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if fieldDecoder == nil {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) msg := "found unknown field: " + field
if decoder.disallowUnknownFields {
iter.ReportError("ReadObject", msg)
}
c := iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
iter.Skip()
return
} }
c := iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
fieldDecoder.Decode(ptr, iter)
} }
type skipObjectDecoder struct { type skipObjectDecoder struct {
@ -455,7 +514,7 @@ type skipObjectDecoder struct {
func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
if valueType != Object && valueType != Nil { if valueType != ObjectValue && valueType != NilValue {
iter.ReportError("skipObjectDecoder", "expect object or null") iter.ReportError("skipObjectDecoder", "expect object or null")
return return
} }
@ -464,7 +523,7 @@ func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
type oneFieldStructDecoder struct { type oneFieldStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash int32 fieldHash int64
fieldDecoder *structFieldDecoder fieldDecoder *structFieldDecoder
} }
@ -483,15 +542,15 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type twoFieldsStructDecoder struct { type twoFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
} }
@ -513,17 +572,17 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type threeFieldsStructDecoder struct { type threeFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
} }
@ -547,19 +606,19 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type fourFieldsStructDecoder struct { type fourFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
} }
@ -585,21 +644,21 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type fiveFieldsStructDecoder struct { type fiveFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
} }
@ -627,23 +686,23 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type sixFieldsStructDecoder struct { type sixFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
fieldHash6 int32 fieldHash6 int64
fieldDecoder6 *structFieldDecoder fieldDecoder6 *structFieldDecoder
} }
@ -673,25 +732,25 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type sevenFieldsStructDecoder struct { type sevenFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
fieldHash6 int32 fieldHash6 int64
fieldDecoder6 *structFieldDecoder fieldDecoder6 *structFieldDecoder
fieldHash7 int32 fieldHash7 int64
fieldDecoder7 *structFieldDecoder fieldDecoder7 *structFieldDecoder
} }
@ -723,27 +782,27 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type eightFieldsStructDecoder struct { type eightFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
fieldHash6 int32 fieldHash6 int64
fieldDecoder6 *structFieldDecoder fieldDecoder6 *structFieldDecoder
fieldHash7 int32 fieldHash7 int64
fieldDecoder7 *structFieldDecoder fieldDecoder7 *structFieldDecoder
fieldHash8 int32 fieldHash8 int64
fieldDecoder8 *structFieldDecoder fieldDecoder8 *structFieldDecoder
} }
@ -777,29 +836,29 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type nineFieldsStructDecoder struct { type nineFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
fieldHash6 int32 fieldHash6 int64
fieldDecoder6 *structFieldDecoder fieldDecoder6 *structFieldDecoder
fieldHash7 int32 fieldHash7 int64
fieldDecoder7 *structFieldDecoder fieldDecoder7 *structFieldDecoder
fieldHash8 int32 fieldHash8 int64
fieldDecoder8 *structFieldDecoder fieldDecoder8 *structFieldDecoder
fieldHash9 int32 fieldHash9 int64
fieldDecoder9 *structFieldDecoder fieldDecoder9 *structFieldDecoder
} }
@ -835,31 +894,31 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }
type tenFieldsStructDecoder struct { type tenFieldsStructDecoder struct {
typ reflect.Type typ reflect.Type
fieldHash1 int32 fieldHash1 int64
fieldDecoder1 *structFieldDecoder fieldDecoder1 *structFieldDecoder
fieldHash2 int32 fieldHash2 int64
fieldDecoder2 *structFieldDecoder fieldDecoder2 *structFieldDecoder
fieldHash3 int32 fieldHash3 int64
fieldDecoder3 *structFieldDecoder fieldDecoder3 *structFieldDecoder
fieldHash4 int32 fieldHash4 int64
fieldDecoder4 *structFieldDecoder fieldDecoder4 *structFieldDecoder
fieldHash5 int32 fieldHash5 int64
fieldDecoder5 *structFieldDecoder fieldDecoder5 *structFieldDecoder
fieldHash6 int32 fieldHash6 int64
fieldDecoder6 *structFieldDecoder fieldDecoder6 *structFieldDecoder
fieldHash7 int32 fieldHash7 int64
fieldDecoder7 *structFieldDecoder fieldDecoder7 *structFieldDecoder
fieldHash8 int32 fieldHash8 int64
fieldDecoder8 *structFieldDecoder fieldDecoder8 *structFieldDecoder
fieldHash9 int32 fieldHash9 int64
fieldDecoder9 *structFieldDecoder fieldDecoder9 *structFieldDecoder
fieldHash10 int32 fieldHash10 int64
fieldDecoder10 *structFieldDecoder fieldDecoder10 *structFieldDecoder
} }
@ -897,7 +956,7 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
} }
} }
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error()) iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
} }
} }

View File

@ -4,15 +4,15 @@ import (
"io" "io"
) )
// Stream is a io.Writer like object, with JSON specific write functions. // stream is a io.Writer like object, with JSON specific write functions.
// Error is not returned as return value, but stored as Error member on this stream instance. // Error is not returned as return value, but stored as Error member on this stream instance.
type Stream struct { type Stream struct {
cfg *frozenConfig cfg *frozenConfig
out io.Writer out io.Writer
buf []byte buf []byte
n int Error error
Error error indention int
indention int Attachment interface{} // open for customized encoder
} }
// NewStream create new stream instance. // NewStream create new stream instance.
@ -23,8 +23,7 @@ func NewStream(cfg API, out io.Writer, bufSize int) *Stream {
return &Stream{ return &Stream{
cfg: cfg.(*frozenConfig), cfg: cfg.(*frozenConfig),
out: out, out: out,
buf: make([]byte, bufSize), buf: make([]byte, 0, bufSize),
n: 0,
Error: nil, Error: nil,
indention: 0, indention: 0,
} }
@ -38,22 +37,22 @@ func (stream *Stream) Pool() StreamPool {
// Reset reuse this stream instance by assign a new writer // Reset reuse this stream instance by assign a new writer
func (stream *Stream) Reset(out io.Writer) { func (stream *Stream) Reset(out io.Writer) {
stream.out = out stream.out = out
stream.n = 0 stream.buf = stream.buf[:0]
} }
// Available returns how many bytes are unused in the buffer. // Available returns how many bytes are unused in the buffer.
func (stream *Stream) Available() int { func (stream *Stream) Available() int {
return len(stream.buf) - stream.n return cap(stream.buf) - len(stream.buf)
} }
// Buffered returns the number of bytes that have been written into the current buffer. // Buffered returns the number of bytes that have been written into the current buffer.
func (stream *Stream) Buffered() int { func (stream *Stream) Buffered() int {
return stream.n return len(stream.buf)
} }
// Buffer if writer is nil, use this method to take the result // Buffer if writer is nil, use this method to take the result
func (stream *Stream) Buffer() []byte { func (stream *Stream) Buffer() []byte {
return stream.buf[:stream.n] return stream.buf
} }
// Write writes the contents of p into the buffer. // Write writes the contents of p into the buffer.
@ -61,97 +60,34 @@ func (stream *Stream) Buffer() []byte {
// If nn < len(p), it also returns an error explaining // If nn < len(p), it also returns an error explaining
// why the write is short. // why the write is short.
func (stream *Stream) Write(p []byte) (nn int, err error) { func (stream *Stream) Write(p []byte) (nn int, err error) {
for len(p) > stream.Available() && stream.Error == nil { stream.buf = append(stream.buf, p...)
if stream.out == nil { if stream.out != nil {
stream.growAtLeast(len(p)) nn, err = stream.out.Write(stream.buf)
} else { stream.buf = stream.buf[nn:]
var n int return
if stream.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, stream.Error = stream.out.Write(p)
} else {
n = copy(stream.buf[stream.n:], p)
stream.n += n
stream.Flush()
}
nn += n
p = p[n:]
}
} }
if stream.Error != nil { return len(p), nil
return nn, stream.Error
}
n := copy(stream.buf[stream.n:], p)
stream.n += n
nn += n
return nn, nil
} }
// WriteByte writes a single byte. // WriteByte writes a single byte.
func (stream *Stream) writeByte(c byte) { func (stream *Stream) writeByte(c byte) {
if stream.Error != nil { stream.buf = append(stream.buf, c)
return
}
if stream.Available() < 1 {
stream.growAtLeast(1)
}
stream.buf[stream.n] = c
stream.n++
} }
func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) { func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) {
if stream.Error != nil { stream.buf = append(stream.buf, c1, c2)
return
}
if stream.Available() < 2 {
stream.growAtLeast(2)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.n += 2
} }
func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) { func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) {
if stream.Error != nil { stream.buf = append(stream.buf, c1, c2, c3)
return
}
if stream.Available() < 3 {
stream.growAtLeast(3)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.n += 3
} }
func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) { func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) {
if stream.Error != nil { stream.buf = append(stream.buf, c1, c2, c3, c4)
return
}
if stream.Available() < 4 {
stream.growAtLeast(4)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.buf[stream.n+3] = c4
stream.n += 4
} }
func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) { func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) {
if stream.Error != nil { stream.buf = append(stream.buf, c1, c2, c3, c4, c5)
return
}
if stream.Available() < 5 {
stream.growAtLeast(5)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.buf[stream.n+3] = c4
stream.buf[stream.n+4] = c5
stream.n += 5
} }
// Flush writes any buffered data to the underlying io.Writer. // Flush writes any buffered data to the underlying io.Writer.
@ -162,53 +98,20 @@ func (stream *Stream) Flush() error {
if stream.Error != nil { if stream.Error != nil {
return stream.Error return stream.Error
} }
if stream.n == 0 { n, err := stream.out.Write(stream.buf)
return nil
}
n, err := stream.out.Write(stream.buf[0:stream.n])
if n < stream.n && err == nil {
err = io.ErrShortWrite
}
if err != nil { if err != nil {
if n > 0 && n < stream.n { if stream.Error == nil {
copy(stream.buf[0:stream.n-n], stream.buf[n:stream.n]) stream.Error = err
} }
stream.n -= n
stream.Error = err
return err return err
} }
stream.n = 0 stream.buf = stream.buf[n:]
return nil return nil
} }
func (stream *Stream) ensure(minimal int) {
available := stream.Available()
if available < minimal {
stream.growAtLeast(minimal)
}
}
func (stream *Stream) growAtLeast(minimal int) {
if stream.out != nil {
stream.Flush()
}
toGrow := len(stream.buf)
if toGrow < minimal {
toGrow = minimal
}
newBuf := make([]byte, len(stream.buf)+toGrow)
copy(newBuf, stream.Buffer())
stream.buf = newBuf
}
// WriteRaw write string out without quotes, just like []byte // WriteRaw write string out without quotes, just like []byte
func (stream *Stream) WriteRaw(s string) { func (stream *Stream) WriteRaw(s string) {
stream.ensure(len(s)) stream.buf = append(stream.buf, s...)
if stream.Error != nil {
return
}
n := copy(stream.buf[stream.n:], s)
stream.n += n
} }
// WriteNil write null to stream // WriteNil write null to stream
@ -269,6 +172,7 @@ func (stream *Stream) WriteEmptyObject() {
func (stream *Stream) WriteMore() { func (stream *Stream) WriteMore() {
stream.writeByte(',') stream.writeByte(',')
stream.writeIndention(0) stream.writeIndention(0)
stream.Flush()
} }
// WriteArrayStart write [ with possible indention // WriteArrayStart write [ with possible indention
@ -280,8 +184,7 @@ func (stream *Stream) WriteArrayStart() {
// WriteEmptyArray write [] // WriteEmptyArray write []
func (stream *Stream) WriteEmptyArray() { func (stream *Stream) WriteEmptyArray() {
stream.writeByte('[') stream.writeTwoBytes('[', ']')
stream.writeByte(']')
} }
// WriteArrayEnd write ] with possible indention // WriteArrayEnd write ] with possible indention
@ -297,9 +200,7 @@ func (stream *Stream) writeIndention(delta int) {
} }
stream.writeByte('\n') stream.writeByte('\n')
toWrite := stream.indention - delta toWrite := stream.indention - delta
stream.ensure(toWrite) for i := 0; i < toWrite; i++ {
for i := 0; i < toWrite && stream.n < len(stream.buf); i++ { stream.buf = append(stream.buf, ' ')
stream.buf[stream.n] = ' '
stream.n++
} }
} }

View File

@ -21,7 +21,7 @@ func (stream *Stream) WriteFloat32(val float32) {
fmt = 'e' fmt = 'e'
} }
} }
stream.WriteRaw(strconv.FormatFloat(float64(val), fmt, -1, 32)) stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 32)
} }
// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster // WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster
@ -43,13 +43,12 @@ func (stream *Stream) WriteFloat32Lossy(val float32) {
return return
} }
stream.writeByte('.') stream.writeByte('.')
stream.ensure(10)
for p := precision - 1; p > 0 && fval < pow10[p]; p-- { for p := precision - 1; p > 0 && fval < pow10[p]; p-- {
stream.writeByte('0') stream.writeByte('0')
} }
stream.WriteUint64(fval) stream.WriteUint64(fval)
for stream.buf[stream.n-1] == '0' { for stream.buf[len(stream.buf)-1] == '0' {
stream.n-- stream.buf = stream.buf[:len(stream.buf)-1]
} }
} }
@ -63,7 +62,7 @@ func (stream *Stream) WriteFloat64(val float64) {
fmt = 'e' fmt = 'e'
} }
} }
stream.WriteRaw(strconv.FormatFloat(float64(val), fmt, -1, 64)) stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 64)
} }
// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster // WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster
@ -85,12 +84,11 @@ func (stream *Stream) WriteFloat64Lossy(val float64) {
return return
} }
stream.writeByte('.') stream.writeByte('.')
stream.ensure(10)
for p := precision - 1; p > 0 && fval < pow10[p]; p-- { for p := precision - 1; p > 0 && fval < pow10[p]; p-- {
stream.writeByte('0') stream.writeByte('0')
} }
stream.WriteUint64(fval) stream.WriteUint64(fval)
for stream.buf[stream.n-1] == '0' { for stream.buf[len(stream.buf)-1] == '0' {
stream.n-- stream.buf = stream.buf[:len(stream.buf)-1]
} }
} }

View File

@ -14,299 +14,169 @@ func init() {
} }
} }
func writeFirstBuf(buf []byte, v uint32, n int) int { func writeFirstBuf(space []byte, v uint32) []byte {
start := v >> 24 start := v >> 24
if start == 0 { if start == 0 {
buf[n] = byte(v >> 16) space = append(space, byte(v >> 16), byte(v >> 8))
n++
buf[n] = byte(v >> 8)
n++
} else if start == 1 { } else if start == 1 {
buf[n] = byte(v >> 8) space = append(space, byte(v >> 8))
n++
} }
buf[n] = byte(v) space = append(space, byte(v))
n++ return space
return n
} }
func writeBuf(buf []byte, v uint32, n int) { func writeBuf(buf []byte, v uint32) []byte {
buf[n] = byte(v >> 16) return append(buf, byte(v >> 16), byte(v >> 8), byte(v))
buf[n+1] = byte(v >> 8)
buf[n+2] = byte(v)
} }
// WriteUint8 write uint8 to stream // WriteUint8 write uint8 to stream
func (stream *Stream) WriteUint8(val uint8) { func (stream *Stream) WriteUint8(val uint8) {
stream.ensure(3) stream.buf = writeFirstBuf(stream.buf, digits[val])
stream.n = writeFirstBuf(stream.buf, digits[val], stream.n)
} }
// WriteInt8 write int8 to stream // WriteInt8 write int8 to stream
func (stream *Stream) WriteInt8(nval int8) { func (stream *Stream) WriteInt8(nval int8) {
stream.ensure(4)
n := stream.n
var val uint8 var val uint8
if nval < 0 { if nval < 0 {
val = uint8(-nval) val = uint8(-nval)
stream.buf[n] = '-' stream.buf = append(stream.buf, '-')
n++
} else { } else {
val = uint8(nval) val = uint8(nval)
} }
stream.n = writeFirstBuf(stream.buf, digits[val], n) stream.buf = writeFirstBuf(stream.buf, digits[val])
} }
// WriteUint16 write uint16 to stream // WriteUint16 write uint16 to stream
func (stream *Stream) WriteUint16(val uint16) { func (stream *Stream) WriteUint16(val uint16) {
stream.ensure(5)
q1 := val / 1000 q1 := val / 1000
if q1 == 0 { if q1 == 0 {
stream.n = writeFirstBuf(stream.buf, digits[val], stream.n) stream.buf = writeFirstBuf(stream.buf, digits[val])
return return
} }
r1 := val - q1*1000 r1 := val - q1*1000
n := writeFirstBuf(stream.buf, digits[q1], stream.n) stream.buf = writeFirstBuf(stream.buf, digits[q1])
writeBuf(stream.buf, digits[r1], n) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 3
return return
} }
// WriteInt16 write int16 to stream // WriteInt16 write int16 to stream
func (stream *Stream) WriteInt16(nval int16) { func (stream *Stream) WriteInt16(nval int16) {
stream.ensure(6)
n := stream.n
var val uint16 var val uint16
if nval < 0 { if nval < 0 {
val = uint16(-nval) val = uint16(-nval)
stream.buf[n] = '-' stream.buf = append(stream.buf, '-')
n++
} else { } else {
val = uint16(nval) val = uint16(nval)
} }
q1 := val / 1000 stream.WriteUint16(val)
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 // WriteUint32 write uint32 to stream
func (stream *Stream) WriteUint32(val uint32) { func (stream *Stream) WriteUint32(val uint32) {
stream.ensure(10)
n := stream.n
q1 := val / 1000 q1 := val / 1000
if q1 == 0 { if q1 == 0 {
stream.n = writeFirstBuf(stream.buf, digits[val], n) stream.buf = writeFirstBuf(stream.buf, digits[val])
return return
} }
r1 := val - q1*1000 r1 := val - q1*1000
q2 := q1 / 1000 q2 := q1 / 1000
if q2 == 0 { if q2 == 0 {
n := writeFirstBuf(stream.buf, digits[q1], n) stream.buf = writeFirstBuf(stream.buf, digits[q1])
writeBuf(stream.buf, digits[r1], n) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 3
return return
} }
r2 := q1 - q2*1000 r2 := q1 - q2*1000
q3 := q2 / 1000 q3 := q2 / 1000
if q3 == 0 { if q3 == 0 {
n = writeFirstBuf(stream.buf, digits[q2], n) stream.buf = writeFirstBuf(stream.buf, digits[q2])
} else { } else {
r3 := q2 - q3*1000 r3 := q2 - q3*1000
stream.buf[n] = byte(q3 + '0') stream.buf = append(stream.buf, byte(q3 + '0'))
n++ stream.buf = writeBuf(stream.buf, digits[r3])
writeBuf(stream.buf, digits[r3], n)
n += 3
} }
writeBuf(stream.buf, digits[r2], n) stream.buf = writeBuf(stream.buf, digits[r2])
writeBuf(stream.buf, digits[r1], n+3) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 6
} }
// WriteInt32 write int32 to stream // WriteInt32 write int32 to stream
func (stream *Stream) WriteInt32(nval int32) { func (stream *Stream) WriteInt32(nval int32) {
stream.ensure(11)
n := stream.n
var val uint32 var val uint32
if nval < 0 { if nval < 0 {
val = uint32(-nval) val = uint32(-nval)
stream.buf[n] = '-' stream.buf = append(stream.buf, '-')
n++
} else { } else {
val = uint32(nval) val = uint32(nval)
} }
q1 := val / 1000 stream.WriteUint32(val)
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 // WriteUint64 write uint64 to stream
func (stream *Stream) WriteUint64(val uint64) { func (stream *Stream) WriteUint64(val uint64) {
stream.ensure(20)
n := stream.n
q1 := val / 1000 q1 := val / 1000
if q1 == 0 { if q1 == 0 {
stream.n = writeFirstBuf(stream.buf, digits[val], n) stream.buf = writeFirstBuf(stream.buf, digits[val])
return return
} }
r1 := val - q1*1000 r1 := val - q1*1000
q2 := q1 / 1000 q2 := q1 / 1000
if q2 == 0 { if q2 == 0 {
n := writeFirstBuf(stream.buf, digits[q1], n) stream.buf = writeFirstBuf(stream.buf, digits[q1])
writeBuf(stream.buf, digits[r1], n) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 3
return return
} }
r2 := q1 - q2*1000 r2 := q1 - q2*1000
q3 := q2 / 1000 q3 := q2 / 1000
if q3 == 0 { if q3 == 0 {
n = writeFirstBuf(stream.buf, digits[q2], n) stream.buf = writeFirstBuf(stream.buf, digits[q2])
writeBuf(stream.buf, digits[r2], n) stream.buf = writeBuf(stream.buf, digits[r2])
writeBuf(stream.buf, digits[r1], n+3) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 6
return return
} }
r3 := q2 - q3*1000 r3 := q2 - q3*1000
q4 := q3 / 1000 q4 := q3 / 1000
if q4 == 0 { if q4 == 0 {
n = writeFirstBuf(stream.buf, digits[q3], n) stream.buf = writeFirstBuf(stream.buf, digits[q3])
writeBuf(stream.buf, digits[r3], n) stream.buf = writeBuf(stream.buf, digits[r3])
writeBuf(stream.buf, digits[r2], n+3) stream.buf = writeBuf(stream.buf, digits[r2])
writeBuf(stream.buf, digits[r1], n+6) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 9
return return
} }
r4 := q3 - q4*1000 r4 := q3 - q4*1000
q5 := q4 / 1000 q5 := q4 / 1000
if q5 == 0 { if q5 == 0 {
n = writeFirstBuf(stream.buf, digits[q4], n) stream.buf = writeFirstBuf(stream.buf, digits[q4])
writeBuf(stream.buf, digits[r4], n) stream.buf = writeBuf(stream.buf, digits[r4])
writeBuf(stream.buf, digits[r3], n+3) stream.buf = writeBuf(stream.buf, digits[r3])
writeBuf(stream.buf, digits[r2], n+6) stream.buf = writeBuf(stream.buf, digits[r2])
writeBuf(stream.buf, digits[r1], n+9) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 12
return return
} }
r5 := q4 - q5*1000 r5 := q4 - q5*1000
q6 := q5 / 1000 q6 := q5 / 1000
if q6 == 0 { if q6 == 0 {
n = writeFirstBuf(stream.buf, digits[q5], n) stream.buf = writeFirstBuf(stream.buf, digits[q5])
} else { } else {
n = writeFirstBuf(stream.buf, digits[q6], n) stream.buf = writeFirstBuf(stream.buf, digits[q6])
r6 := q5 - q6*1000 r6 := q5 - q6*1000
writeBuf(stream.buf, digits[r6], n) stream.buf = writeBuf(stream.buf, digits[r6])
n += 3
} }
writeBuf(stream.buf, digits[r5], n) stream.buf = writeBuf(stream.buf, digits[r5])
writeBuf(stream.buf, digits[r4], n+3) stream.buf = writeBuf(stream.buf, digits[r4])
writeBuf(stream.buf, digits[r3], n+6) stream.buf = writeBuf(stream.buf, digits[r3])
writeBuf(stream.buf, digits[r2], n+9) stream.buf = writeBuf(stream.buf, digits[r2])
writeBuf(stream.buf, digits[r1], n+12) stream.buf = writeBuf(stream.buf, digits[r1])
stream.n = n + 15
} }
// WriteInt64 write int64 to stream // WriteInt64 write int64 to stream
func (stream *Stream) WriteInt64(nval int64) { func (stream *Stream) WriteInt64(nval int64) {
stream.ensure(20)
n := stream.n
var val uint64 var val uint64
if nval < 0 { if nval < 0 {
val = uint64(-nval) val = uint64(-nval)
stream.buf[n] = '-' stream.buf = append(stream.buf, '-')
n++
} else { } else {
val = uint64(nval) val = uint64(nval)
} }
q1 := val / 1000 stream.WriteUint64(val)
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 // WriteInt write int to stream

View File

@ -219,34 +219,22 @@ var hex = "0123456789abcdef"
// WriteStringWithHTMLEscaped write string to stream with html special characters escaped // WriteStringWithHTMLEscaped write string to stream with html special characters escaped
func (stream *Stream) WriteStringWithHTMLEscaped(s string) { func (stream *Stream) WriteStringWithHTMLEscaped(s string) {
stream.ensure(32)
valLen := len(s) valLen := len(s)
toWriteLen := valLen stream.buf = append(stream.buf, '"')
bufLengthMinusTwo := len(stream.buf) - 2 // make room for the quotes
if stream.n+toWriteLen > bufLengthMinusTwo {
toWriteLen = bufLengthMinusTwo - stream.n
}
n := stream.n
stream.buf[n] = '"'
n++
// write string, the fast path, without utf8 and escape support // write string, the fast path, without utf8 and escape support
i := 0 i := 0
for ; i < toWriteLen; i++ { for ; i < valLen; i++ {
c := s[i] c := s[i]
if c < utf8.RuneSelf && htmlSafeSet[c] { if c < utf8.RuneSelf && htmlSafeSet[c] {
stream.buf[n] = c stream.buf = append(stream.buf, c)
n++
} else { } else {
break break
} }
} }
if i == valLen { if i == valLen {
stream.buf[n] = '"' stream.buf = append(stream.buf, '"')
n++
stream.n = n
return return
} }
stream.n = n
writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen) writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen)
} }
@ -289,6 +277,8 @@ func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen
if start < i { if start < i {
stream.WriteRaw(s[start:i]) stream.WriteRaw(s[start:i])
} }
stream.WriteRaw(`\ufffd`)
i++
start = i start = i
continue continue
} }
@ -319,34 +309,22 @@ func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen
// WriteString write string to stream without html escape // WriteString write string to stream without html escape
func (stream *Stream) WriteString(s string) { func (stream *Stream) WriteString(s string) {
stream.ensure(32)
valLen := len(s) valLen := len(s)
toWriteLen := valLen stream.buf = append(stream.buf, '"')
bufLengthMinusTwo := len(stream.buf) - 2 // make room for the quotes
if stream.n+toWriteLen > bufLengthMinusTwo {
toWriteLen = bufLengthMinusTwo - stream.n
}
n := stream.n
stream.buf[n] = '"'
n++
// write string, the fast path, without utf8 and escape support // write string, the fast path, without utf8 and escape support
i := 0 i := 0
for ; i < toWriteLen; i++ { for ; i < valLen; i++ {
c := s[i] c := s[i]
if c > 31 && c != '"' && c != '\\' { if c > 31 && c != '"' && c != '\\' {
stream.buf[n] = c stream.buf = append(stream.buf, c)
n++
} else { } else {
break break
} }
} }
if i == valLen { if i == valLen {
stream.buf[n] = '"' stream.buf = append(stream.buf, '"')
n++
stream.n = n
return return
} }
stream.n = n
writeStringSlowPath(stream, i, s, valLen) writeStringSlowPath(stream, i, s, valLen)
} }

View File

@ -23,10 +23,10 @@ func Test_writeBytes_should_grow_buffer(t *testing.T) {
stream := NewStream(ConfigDefault, nil, 1) stream := NewStream(ConfigDefault, nil, 1)
stream.Write([]byte{'1', '2'}) stream.Write([]byte{'1', '2'})
should.Equal("12", string(stream.Buffer())) should.Equal("12", string(stream.Buffer()))
should.Equal(3, len(stream.buf)) should.Equal(2, len(stream.buf))
stream.Write([]byte{'3', '4', '5', '6', '7'}) stream.Write([]byte{'3', '4', '5', '6', '7'})
should.Equal("1234567", string(stream.Buffer())) should.Equal("1234567", string(stream.Buffer()))
should.Equal(8, len(stream.buf)) should.Equal(7, len(stream.buf))
} }
func Test_writeIndention_should_grow_buffer(t *testing.T) { func Test_writeIndention_should_grow_buffer(t *testing.T) {
@ -51,3 +51,19 @@ func Test_writeString_should_grow_buffer(t *testing.T) {
should.Nil(stream.Error) should.Nil(stream.Error)
should.Equal(`"123"`, string(stream.Buffer())) should.Equal(`"123"`, string(stream.Buffer()))
} }
type NopWriter struct {
bufferSize int
}
func (w *NopWriter) Write(p []byte) (n int, err error) {
w.bufferSize = cap(p)
return len(p), nil
}
func Test_flush_buffer_should_stop_grow_buffer(t *testing.T) {
writer := new(NopWriter)
NewEncoder(writer).Encode(make([]int, 10000000))
should := require.New(t)
should.Equal(8, writer.bufferSize)
}

View File

@ -1,84 +0,0 @@
package jsoniter
import (
"bytes"
"encoding/json"
"github.com/stretchr/testify/require"
"io/ioutil"
"testing"
)
func Test_new_decoder(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
decoder2 := NewDecoder(bytes.NewBufferString(`[1][2]`))
arr1 := []int{}
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{1}, arr1)
arr2 := []int{}
should.True(decoder1.More())
buffered, _ := ioutil.ReadAll(decoder1.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{1}, arr2)
should.True(decoder2.More())
buffered, _ = ioutil.ReadAll(decoder2.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{2}, arr1)
should.False(decoder1.More())
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{2}, arr2)
should.False(decoder2.More())
}
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.UseNumber()
var obj1 interface{}
should.Nil(decoder1.Decode(&obj1))
should.Equal(json.Number("123"), obj1)
var obj2 interface{}
should.Nil(decoder2.Decode(&obj2))
should.Equal(json.Number("123"), obj2)
}
func Test_use_number_for_unmarshal(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))
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,20 @@
package jsoniter package misc_tests
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"testing" "testing"
"github.com/json-iterator/go"
) )
func Test_empty_array(t *testing.T) { func Test_empty_array(t *testing.T) {
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, `[]`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
cont := iter.ReadArray() cont := iter.ReadArray()
should.False(cont) should.False(cont)
iter = ParseString(ConfigDefault, `[]`) iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
should.FailNow("should not call") should.FailNow("should not call")
return true return true
}) })
@ -21,12 +22,12 @@ func Test_empty_array(t *testing.T) {
func Test_one_element(t *testing.T) { func Test_one_element(t *testing.T) {
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, `[1]`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
should.True(iter.ReadArray()) should.True(iter.ReadArray())
should.Equal(1, iter.ReadInt()) should.Equal(1, iter.ReadInt())
should.False(iter.ReadArray()) should.False(iter.ReadArray())
iter = ParseString(ConfigDefault, `[1]`) iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
should.Equal(1, iter.ReadInt()) should.Equal(1, iter.ReadInt())
return true return true
}) })
@ -34,18 +35,18 @@ func Test_one_element(t *testing.T) {
func Test_two_elements(t *testing.T) { func Test_two_elements(t *testing.T) {
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, `[1,2]`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1,2]`)
should.True(iter.ReadArray()) should.True(iter.ReadArray())
should.Equal(int64(1), iter.ReadInt64()) should.Equal(int64(1), iter.ReadInt64())
should.True(iter.ReadArray()) should.True(iter.ReadArray())
should.Equal(int64(2), iter.ReadInt64()) should.Equal(int64(2), iter.ReadInt64())
should.False(iter.ReadArray()) 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()) should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
} }
func Test_whitespace_in_head(t *testing.T) { func Test_whitespace_in_head(t *testing.T) {
iter := ParseString(ConfigDefault, ` [1]`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, ` [1]`)
cont := iter.ReadArray() cont := iter.ReadArray()
if cont != true { if cont != true {
t.FailNow() t.FailNow()
@ -56,7 +57,7 @@ func Test_whitespace_in_head(t *testing.T) {
} }
func Test_whitespace_after_array_start(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() cont := iter.ReadArray()
if cont != true { if cont != true {
t.FailNow() t.FailNow()
@ -67,7 +68,7 @@ func Test_whitespace_after_array_start(t *testing.T) {
} }
func Test_whitespace_before_array_end(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() cont := iter.ReadArray()
if cont != true { if cont != true {
t.FailNow() t.FailNow()
@ -82,7 +83,7 @@ func Test_whitespace_before_array_end(t *testing.T) {
} }
func Test_whitespace_before_comma(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() cont := iter.ReadArray()
if cont != true { if cont != true {
t.FailNow() t.FailNow()
@ -106,7 +107,7 @@ func Test_whitespace_before_comma(t *testing.T) {
func Test_write_array(t *testing.T) { func Test_write_array(t *testing.T) {
should := require.New(t) should := require.New(t)
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096) stream := jsoniter.NewStream(jsoniter.Config{IndentionStep: 2}.Froze(), buf, 4096)
stream.WriteArrayStart() stream.WriteArrayStart()
stream.WriteInt(1) stream.WriteInt(1)
stream.WriteMore() stream.WriteMore()
@ -120,7 +121,7 @@ func Test_write_array(t *testing.T) {
func Test_write_val_array(t *testing.T) { func Test_write_val_array(t *testing.T) {
should := require.New(t) should := require.New(t)
val := []int{1, 2, 3} val := []int{1, 2, 3}
str, err := MarshalToString(&val) str, err := jsoniter.MarshalToString(&val)
should.Nil(err) should.Nil(err)
should.Equal("[1,2,3]", str) should.Equal("[1,2,3]", str)
} }
@ -128,7 +129,7 @@ func Test_write_val_array(t *testing.T) {
func Test_write_val_empty_array(t *testing.T) { func Test_write_val_empty_array(t *testing.T) {
should := require.New(t) should := require.New(t)
val := []int{} val := []int{}
str, err := MarshalToString(val) str, err := jsoniter.MarshalToString(val)
should.Nil(err) should.Nil(err)
should.Equal("[]", str) should.Equal("[]", str)
} }
@ -140,7 +141,7 @@ func Test_write_array_of_interface_in_struct(t *testing.T) {
Field2 string Field2 string
} }
val := TestObject{[]interface{}{1, 2}, ""} val := TestObject{[]interface{}{1, 2}, ""}
str, err := MarshalToString(val) str, err := jsoniter.MarshalToString(val)
should.Nil(err) should.Nil(err)
should.Contains(str, `"Field":[1,2]`) should.Contains(str, `"Field":[1,2]`)
should.Contains(str, `"Field2":""`) should.Contains(str, `"Field2":""`)
@ -151,18 +152,29 @@ func Test_encode_byte_array(t *testing.T) {
bytes, err := json.Marshal([]byte{1, 2, 3}) bytes, err := json.Marshal([]byte{1, 2, 3})
should.Nil(err) should.Nil(err)
should.Equal(`"AQID"`, string(bytes)) should.Equal(`"AQID"`, string(bytes))
bytes, err = Marshal([]byte{1, 2, 3}) bytes, err = jsoniter.Marshal([]byte{1, 2, 3})
should.Nil(err) should.Nil(err)
should.Equal(`"AQID"`, string(bytes)) should.Equal(`"AQID"`, string(bytes))
} }
func Test_decode_byte_array(t *testing.T) { func Test_decode_byte_array_from_base64(t *testing.T) {
should := require.New(t) should := require.New(t)
data := []byte{} data := []byte{}
err := json.Unmarshal([]byte(`"AQID"`), &data) err := json.Unmarshal([]byte(`"AQID"`), &data)
should.Nil(err) should.Nil(err)
should.Equal([]byte{1, 2, 3}, data) 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_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.Nil(err)
should.Equal([]byte{1, 2, 3}, data) should.Equal([]byte{1, 2, 3}, data)
} }
@ -170,21 +182,21 @@ func Test_decode_byte_array(t *testing.T) {
func Test_decode_slice(t *testing.T) { func Test_decode_slice(t *testing.T) {
should := require.New(t) should := require.New(t)
slice := make([]string, 0, 5) slice := make([]string, 0, 5)
UnmarshalFromString(`["hello", "world"]`, &slice) jsoniter.UnmarshalFromString(`["hello", "world"]`, &slice)
should.Equal([]string{"hello", "world"}, slice) should.Equal([]string{"hello", "world"}, slice)
} }
func Test_decode_large_slice(t *testing.T) { func Test_decode_large_slice(t *testing.T) {
should := require.New(t) should := require.New(t)
slice := make([]int, 0, 1) 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) should.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, slice)
} }
func Benchmark_jsoniter_array(b *testing.B) { func Benchmark_jsoniter_array(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
input := []byte(`[1,2,3,4,5,6,7,8,9]`) input := []byte(`[1,2,3,4,5,6,7,8,9]`)
iter := ParseBytes(ConfigDefault, input) iter := jsoniter.ParseBytes(jsoniter.ConfigDefault, input)
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
iter.ResetBytes(input) iter.ResetBytes(input)

View File

@ -0,0 +1,47 @@
package misc_tests
import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
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())
}

View File

@ -0,0 +1,95 @@
package misc_tests
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
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.Nil(jsoniter.UnmarshalFromString(`1e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(json.Unmarshal([]byte(`1e1`), &obj))
should.Equal(float64(10), obj)
should.Nil(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(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 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)
}
}

View File

@ -0,0 +1,101 @@
// +build go1.8
package misc_tests
import (
"bytes"
"encoding/json"
"io/ioutil"
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
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))
}
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)
}
}

View File

@ -0,0 +1,224 @@
package misc_tests
import (
"encoding/json"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
type MyInterface interface {
Hello() string
}
type MyString string
func (ms MyString) Hello() string {
return string(ms)
}
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(jsoniter.UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal(MyString("hello"), obj.Field)
}
func Test_nil_non_empty_interface(t *testing.T) {
type TestObject struct {
Field []MyInterface
}
should := require.New(t)
obj := TestObject{}
b := []byte(`{"Field":["AAA"]}`)
should.NotNil(json.Unmarshal(b, &obj))
should.NotNil(jsoniter.Unmarshal(b, &obj))
}
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)
}
func Test_unmarshal_ptr_to_interface(t *testing.T) {
type TestData struct {
Name string `json:"name"`
}
should := require.New(t)
var obj interface{} = &TestData{}
err := json.Unmarshal([]byte(`{"name":"value"}`), &obj)
should.Nil(err)
should.Equal("&{value}", fmt.Sprintf("%v", obj))
obj = interface{}(&TestData{})
err = jsoniter.Unmarshal([]byte(`{"name":"value"}`), &obj)
should.Nil(err)
should.Equal("&{value}", fmt.Sprintf("%v", 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.Equal(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.Equal(nil, err)
should.Equal(&payload, wrapper.Payload)
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, 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.Equal(nil, 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.Equal(nil, err)
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, 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.Nil(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
payload = nil
wrapper = &Wrapper{
Payload: payload,
}
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Nil(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
package misc_tests
import (
"encoding/json"
"math/big"
"testing"
"github.com/stretchr/testify/require"
"strings"
"github.com/json-iterator/go"
)
func Test_decode_TextMarshaler_key_map(t *testing.T) {
should := require.New(t)
var val map[*big.Float]string
should.Nil(jsoniter.UnmarshalFromString(`{"1":"2"}`, &val))
str, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
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 := jsoniter.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"])
}

View File

@ -1,9 +1,10 @@
package jsoniter package misc_tests
import ( import (
"encoding/json" "encoding/json"
"reflect" "reflect"
"testing" "testing"
"github.com/json-iterator/go"
) )
type Level1 struct { type Level1 struct {
@ -15,7 +16,7 @@ type Level2 struct {
} }
func Test_nested(t *testing.T) { func Test_nested(t *testing.T) {
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{} l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() { for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field { switch l1Field {
@ -50,7 +51,7 @@ func Test_nested(t *testing.T) {
func Benchmark_jsoniter_nested(b *testing.B) { func Benchmark_jsoniter_nested(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`) iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{} l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() { for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field { switch l1Field {
@ -63,7 +64,7 @@ func Benchmark_jsoniter_nested(b *testing.B) {
} }
} }
func readLevel1Hello(iter *Iterator) []Level2 { func readLevel1Hello(iter *jsoniter.Iterator) []Level2 {
l2Array := make([]Level2, 0, 2) l2Array := make([]Level2, 0, 2)
for iter.ReadArray() { for iter.ReadArray() {
l2 := Level2{} l2 := Level2{}

View File

@ -0,0 +1,81 @@
package misc_tests
import (
"bytes"
"io"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
)
func Test_read_null(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `null`)
should.True(iter.ReadNil())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `null`)
should.Nil(iter.Read())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `navy`)
iter.Read()
should.True(iter.Error != nil && iter.Error != io.EOF)
iter = jsoniter.ParseString(jsoniter.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 := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
stream.WriteNil()
stream.Flush()
should.Nil(stream.Error)
should.Equal("null", buf.String())
}
func Test_decode_null_object_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.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(jsoniter.UnmarshalFromString("[null]", &objs))
should.Len(objs, 1)
}
func Test_decode_null_array_element(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
should.True(iter.ReadArray())
should.True(iter.ReadNil())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_decode_null_string(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.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 := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}

View File

@ -0,0 +1,132 @@
package misc_tests
import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"time"
"strings"
)
func Test_empty_object(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{}`)
field := iter.ReadObject()
should.Equal("", field)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `{}`)
iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
should.FailNow("should not call")
return true
})
}
func Test_one_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"a": "stream"}`)
field := iter.ReadObject()
should.Equal("a", field)
value := iter.ReadString()
should.Equal("stream", value)
field = iter.ReadObject()
should.Equal("", field)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `{"a": "stream"}`)
should.True(iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
should.Equal("a", field)
iter.Skip()
return true
}))
}
func Test_two_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.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 = jsoniter.ParseString(jsoniter.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_write_object(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.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_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 := jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
obj := TestObject{}
should.Nil(decoder.Decode(&obj))
}

Some files were not shown because too many files have changed in this diff Show More