mirror of
https://github.com/json-iterator/go.git
synced 2025-05-28 22:17:38 +02:00
Compare commits
628 Commits
jsoniter-g
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
71ac16282d | ||
|
720cd423d0 | ||
|
9b00d50ac0 | ||
|
ffc487c633 | ||
|
10b9ac0971 | ||
|
bf8b920736 | ||
|
a7b8414362 | ||
|
024077e996 | ||
|
08b5e5796b | ||
|
c6661824eb | ||
|
3c55efb1a1 | ||
|
e6b9536d36 | ||
|
a3465d79a9 | ||
|
9b79a3e192 | ||
|
1779031cda | ||
|
6821bec9fa | ||
|
9461257643 | ||
|
5bce16d299 | ||
|
a1ca083078 | ||
|
cd6773e694 | ||
|
55287ed53a | ||
|
8961be9c21 | ||
|
0f8241d334 | ||
|
53b9d06ba7 | ||
|
1f7ee05ef8 | ||
|
b22f393858 | ||
|
bede7b9e40 | ||
|
58aeb59006 | ||
|
7acbb404a4 | ||
|
69f2a91ff4 | ||
|
11a37a0774 | ||
|
91f4a6405d | ||
|
a54d350455 | ||
|
6f4c196d95 | ||
|
8302a17e8c | ||
|
3987001e27 | ||
|
78d9e97b7a | ||
|
49c900ee46 | ||
|
9c0685d8d3 | ||
|
acfec88f7a | ||
|
e88512faf8 | ||
|
b681149eae | ||
|
d1af7639b3 | ||
|
7c9f8c2d20 | ||
|
f814d6c0f1 | ||
|
aba8654400 | ||
|
a1c9557592 | ||
|
44a7e7340d | ||
|
2834c7e43c | ||
|
d296277d5c | ||
|
dc11f49689 | ||
|
83f7b825b3 | ||
|
03217c3e97 | ||
|
908eaed151 | ||
|
eec24895fe | ||
|
1ba732a07d | ||
|
819acad769 | ||
|
695ec2b83b | ||
|
028e2ef2bd | ||
|
976454858b | ||
|
27518f6661 | ||
|
94869abf43 | ||
|
459f0e30ae | ||
|
0039f4ac3d | ||
|
fb5614a4ca | ||
|
f71b9090aa | ||
|
08047c174c | ||
|
68347ec4d6 | ||
|
0fd91468bb | ||
|
1bc9828b4f | ||
|
24c3d57281 | ||
|
0ff49de124 | ||
|
5bc9320502 | ||
|
f64ce68b6e | ||
|
2d42ff74dd | ||
|
3a023a5fbc | ||
|
16aef10b2b | ||
|
ae4c002f78 | ||
|
e4aa2ec063 | ||
|
d05f387f50 | ||
|
a9403d25cd | ||
|
05d041de10 | ||
|
5916df66b3 | ||
|
2433035e51 | ||
|
6dfc0bf2dd | ||
|
b9be8dd373 | ||
|
b8d78b6aaf | ||
|
7109b5e7dd | ||
|
4cc76529e8 | ||
|
c5ddac9dc3 | ||
|
f76d712086 | ||
|
1624edc445 | ||
|
5d789e5e02 | ||
|
0260c89b54 | ||
|
10a568c511 | ||
|
ab8a2e0c74 | ||
|
2fbdfbb595 | ||
|
720ab8dc7f | ||
|
f2b4162afb | ||
|
3830516ed0 | ||
|
7cceb6c2e3 | ||
|
b92cf78708 | ||
|
8744d7c5c7 | ||
|
37cc313d18 | ||
|
2ddf6d7582 | ||
|
6a6742f0a2 | ||
|
6c702ce12a | ||
|
f88871b601 | ||
|
f246f80f14 | ||
|
51dd70305b | ||
|
a949c42748 | ||
|
f89479f5c0 | ||
|
b858ec296c | ||
|
885a41a0a6 | ||
|
9e9a97040e | ||
|
fb4d53e4cc | ||
|
b53656d459 | ||
|
8f27a81d90 | ||
|
4930b053b8 | ||
|
06e0f9391e | ||
|
ca39e5af3e | ||
|
39acec93e0 | ||
|
25fa392355 | ||
|
d51e841de0 | ||
|
3353055b2a | ||
|
455b3f8bb8 | ||
|
2a93f9003e | ||
|
9472474ffd | ||
|
ad83167dc6 | ||
|
fff342fd04 | ||
|
8d6662b81b | ||
|
a377e2656b | ||
|
0ac74bba4a | ||
|
ebe943a4a6 | ||
|
414d0307c9 | ||
|
86e9fd72bc | ||
|
be70f29b04 | ||
|
a3fdd37b9a | ||
|
d346ea6e55 | ||
|
820ec30bd6 | ||
|
df8295a48a | ||
|
99fc16a363 | ||
|
a3866383f5 | ||
|
2fcbb23d96 | ||
|
ea6403326b | ||
|
404d90796f | ||
|
b79587753b | ||
|
63ea5e3131 | ||
|
895a19f2dc | ||
|
6327145300 | ||
|
c99d73acd0 | ||
|
3d39af6dd9 | ||
|
a016e87b9f | ||
|
08218647c3 | ||
|
d6f02cbd48 | ||
|
b53aa13eb0 | ||
|
e322da5531 | ||
|
1d41f3c0ed | ||
|
29604bf5c3 | ||
|
cbc1786a76 | ||
|
5a696808d6 | ||
|
d8e64aa825 | ||
|
577ddede74 | ||
|
43d9384d67 | ||
|
2074f25bd3 | ||
|
ef3038593b | ||
|
9dafbc667f | ||
|
a7a34507ab | ||
|
0e2b54800a | ||
|
e7c7f3b337 | ||
|
75810179f6 | ||
|
6a8f9fa342 | ||
|
24bb2eee9f | ||
|
64cc784089 | ||
|
477be43d00 | ||
|
a8708bca85 | ||
|
658ff9ef15 | ||
|
64c1c67885 | ||
|
e3bc511e5a | ||
|
8fa357ab7b | ||
|
761ce8cce2 | ||
|
c3b6c1e845 | ||
|
0ed9de94f2 | ||
|
6fded6eb5f | ||
|
dc3395f770 | ||
|
bd4e013f98 | ||
|
48a4a1e4db | ||
|
9bc223734a | ||
|
eb9aeccee2 | ||
|
28adca2a14 | ||
|
a9b3f36b2f | ||
|
1e8e785321 | ||
|
002b5ae342 | ||
|
07f99a1124 | ||
|
71f74dc71e | ||
|
7990317be5 | ||
|
9edd73f752 | ||
|
3d5ee1098a | ||
|
ee8cfb7547 | ||
|
bca911dae0 | ||
|
28452fcdec | ||
|
ea8c33040f | ||
|
358cfc3929 | ||
|
c39a632e65 | ||
|
e31252f2e2 | ||
|
807e4a8b20 | ||
|
e78b7e89b6 | ||
|
945d1aaa19 | ||
|
ba3857729b | ||
|
c3ed5e85e0 | ||
|
c27f6f9350 | ||
|
0ab880662f | ||
|
6dad2de6cc | ||
|
11c1cce0d8 | ||
|
96fcb84835 | ||
|
e7a8aea845 | ||
|
60a9df5ebc | ||
|
7b060ec866 | ||
|
25f147f530 | ||
|
a9b9c73b4d | ||
|
e0df39fda2 | ||
|
13f86432b8 | ||
|
d2a7335211 | ||
|
b2a706d14b | ||
|
23078876c5 | ||
|
051434fab7 | ||
|
be6688fc1a | ||
|
ff2b70c1db | ||
|
f7279a603e | ||
|
9f088cbcc4 | ||
|
3c0e5762c4 | ||
|
d394a135a1 | ||
|
9fddff05f0 | ||
|
b1b003864e | ||
|
aed5a81f09 | ||
|
f1258b01aa | ||
|
fbd210edfc | ||
|
640251ab91 | ||
|
06b2a7cf1d | ||
|
5fffb9b8f7 | ||
|
7e3b776024 | ||
|
6240e1e798 | ||
|
0149a5cf4a | ||
|
5068c8baaf | ||
|
16f78601b5 | ||
|
8f50a91be2 | ||
|
73c7bc881e | ||
|
4de15a3a87 | ||
|
14b28b2226 | ||
|
abe3c4016b | ||
|
dbb1ef3f63 | ||
|
46b20bbbec | ||
|
fdfe0b9a69 | ||
|
faa3dcf46a | ||
|
1f58120d43 | ||
|
6ed27152e0 | ||
|
3c298d8a76 | ||
|
9f6e5962a9 | ||
|
c463aa12c4 | ||
|
b5d2607a6d | ||
|
48cc4d965a | ||
|
c59c42fda0 | ||
|
8324374402 | ||
|
2017f3866b | ||
|
ddc5af4512 | ||
|
2f7e5c8dd7 | ||
|
92772579dd | ||
|
ae57d167e8 | ||
|
eef35e549b | ||
|
005d86dc44 | ||
|
e658f6597a | ||
|
f8eb43eda3 | ||
|
18a241d40b | ||
|
0fdf883ac0 | ||
|
34fbec74ad | ||
|
90574c5ca3 | ||
|
6a4ba7bfa9 | ||
|
0828e559d0 | ||
|
2c67d0f68a | ||
|
f29a0391bc | ||
|
374e68a144 | ||
|
b134d86290 | ||
|
bc3221879d | ||
|
8c7fc7584a | ||
|
db32ee8c2d | ||
|
d80309af3b | ||
|
36b14963da | ||
|
f706335302 | ||
|
2dc0031b26 | ||
|
cdbd2ed810 | ||
|
39e9d67807 | ||
|
2066b01acb | ||
|
ac3b3cd160 | ||
|
887789156a | ||
|
7df5a67d0d | ||
|
9c358632dc | ||
|
1cfa233923 | ||
|
d249b05a85 | ||
|
abbd16da6c | ||
|
b67201557a | ||
|
5124683f24 | ||
|
4892de725b | ||
|
34a2174be3 | ||
|
24ecaff2a1 | ||
|
c15b4d116c | ||
|
12cd299fa8 | ||
|
60ba332980 | ||
|
f705934fbf | ||
|
17a26a6e20 | ||
|
156284b028 | ||
|
6b6938829d | ||
|
e066e54964 | ||
|
18d6ae2668 | ||
|
c966eaa031 | ||
|
f6da8e62c3 | ||
|
5eded4f6ae | ||
|
9b3ec40fd9 | ||
|
0d604da7d7 | ||
|
b6ace7d51b | ||
|
6a4fbb9892 | ||
|
4ae426c4b7 | ||
|
b46d0a2324 | ||
|
8b03604184 | ||
|
93ce14316d | ||
|
779c3e2164 | ||
|
4b33139ad0 | ||
|
dc388588a3 | ||
|
bd4364ab7c | ||
|
b9dc3ebda7 | ||
|
90137b4a60 | ||
|
be9d4ded4f | ||
|
7b1fd129cf | ||
|
b91b7ac682 | ||
|
845d8438db | ||
|
d37197e176 | ||
|
45c22b130b | ||
|
4a84b0b30e | ||
|
0187038bad | ||
|
c38e47d169 | ||
|
b27718d16b | ||
|
a447a8f797 | ||
|
0d6dae80e1 | ||
|
d336ee6da6 | ||
|
3606750b83 | ||
|
db3f5046d7 | ||
|
f0487718f6 | ||
|
46574e7d09 | ||
|
3a6ecf051c | ||
|
5862c51768 | ||
|
ce479f3476 | ||
|
bede1d7f40 | ||
|
d3448d3dbd | ||
|
8fbed91768 | ||
|
3b6853d209 | ||
|
4351a2e6e9 | ||
|
891d33b415 | ||
|
ad20f12c34 | ||
|
9ecb1fd36d | ||
|
6d0e6f3733 | ||
|
711f836582 | ||
|
37ba1b32b5 | ||
|
2c10d8e6bb | ||
|
aaf6160146 | ||
|
b1afefe058 | ||
|
3bb49c1e47 | ||
|
eb68fff85c | ||
|
e07a4ca5ec | ||
|
dfa4bdf888 | ||
|
b74ffb2e03 | ||
|
a46060dedc | ||
|
5eadecbb66 | ||
|
5bc013d6a3 | ||
|
f7df62f1b5 | ||
|
07f423d248 | ||
|
d4c0cb2986 | ||
|
84ed6b3caf | ||
|
ee6536c50a | ||
|
b6eb62e96b | ||
|
8675af13bf | ||
|
21ca11f96a | ||
|
dd88d25090 | ||
|
6a289f32c2 | ||
|
4907dc00f6 | ||
|
2350982504 | ||
|
27725b7139 | ||
|
ca6a524d4f | ||
|
1de44419ea | ||
|
550531a046 | ||
|
1745078ab7 | ||
|
6129e85d53 | ||
|
ee3313111c | ||
|
4e65952c09 | ||
|
3829a470ae | ||
|
3f35bed884 | ||
|
8d7efe886c | ||
|
f245011c7d | ||
|
4ea96ac7c3 | ||
|
50beb4f15d | ||
|
e5d7a65616 | ||
|
d7b6b4e0bb | ||
|
712ddb1942 | ||
|
ca8dd93d0b | ||
|
ac8dd56dfb | ||
|
d8dbf14af4 | ||
|
402c6c79e2 | ||
|
2e10d5fdad | ||
|
f0b07a2313 | ||
|
919a2eff5c | ||
|
a743df1b8a | ||
|
8700644196 | ||
|
6a7bf91c45 | ||
|
e417330822 | ||
|
af876518e1 | ||
|
21b9254da4 | ||
|
c009421781 | ||
|
c4f54740f7 | ||
|
368bd0c1d8 | ||
|
11975d2a26 | ||
|
e7404f3065 | ||
|
f60a6a17c2 | ||
|
2dfdcdd9db | ||
|
496a8ea225 | ||
|
ed6c434851 | ||
|
bf002a02be | ||
|
815aa331a8 | ||
|
54ab168362 | ||
|
87d1ea0c60 | ||
|
6268a1cbc8 | ||
|
d56566286c | ||
|
f2c50ef73b | ||
|
d0c5988985 | ||
|
17eed15fb5 | ||
|
e260979001 | ||
|
3cf9b7c253 | ||
|
800df52ccd | ||
|
e3ba0e7b7e | ||
|
e366c72b81 | ||
|
69551ef38f | ||
|
abcf9a8d76 | ||
|
fa0965a968 | ||
|
04eae11ba5 | ||
|
ea8fa7cc63 | ||
|
6540266aaf | ||
|
ddfbb0c62e | ||
|
a3a2d1cd25 | ||
|
401a56bc20 | ||
|
cfaa11f837 | ||
|
c1411e0ad5 | ||
|
9ec64591b6 | ||
|
b07d1abc4f | ||
|
79c4040505 | ||
|
7fa780bd5d | ||
|
7244d730b9 | ||
|
12be6e0d43 | ||
|
3cfe590a13 | ||
|
678c297af3 | ||
|
1253b8edd3 | ||
|
fb382c0ec1 | ||
|
09cb1d9236 | ||
|
e6c24947ee | ||
|
a6673c983a | ||
|
ec7b40d104 | ||
|
84fa033353 | ||
|
4e608af2c7 | ||
|
3458ccdb20 | ||
|
45ccfb031f | ||
|
545a32f2a1 | ||
|
08dbc98040 | ||
|
82dabdcdbf | ||
|
76e62088df | ||
|
faaa59222a | ||
|
91ef89a6a2 | ||
|
3e3caf9184 | ||
|
03a2daaeee | ||
|
4652ac6cc2 | ||
|
8a9f2b9179 | ||
|
0db2d74de8 | ||
|
d6ef711c18 | ||
|
628fedf63c | ||
|
5bb7a1f7af | ||
|
c2c9981062 | ||
|
e40d614037 | ||
|
1589ab2fd7 | ||
|
97ee4ad4a2 | ||
|
f09f778ca9 | ||
|
9fc858b117 | ||
|
f93d25f8b1 | ||
|
7cd7a6cc7c | ||
|
7d6c9374e8 | ||
|
e16ee7f8ac | ||
|
cf6367546b | ||
|
dc44e85a86 | ||
|
85f7a1b0b3 | ||
|
507a446eda | ||
|
76eefc25ba | ||
|
21a16bd252 | ||
|
0c0c9f119f | ||
|
cf77980493 | ||
|
7d681fe2c2 | ||
|
0c07128d3c | ||
|
f771d32291 | ||
|
d100b0d41f | ||
|
81e64121ba | ||
|
dcc91365ee | ||
|
8f3de9c412 | ||
|
5d3508979f | ||
|
8f8e16b4c2 | ||
|
d7ea1acd3f | ||
|
ebed7df895 | ||
|
caaa04195e | ||
|
ff3c624fa9 | ||
|
3333ec11a0 | ||
|
29a928e1d2 | ||
|
83fa27ff9a | ||
|
cefb2972fd | ||
|
486534c67c | ||
|
ed79b1726e | ||
|
85be06b145 | ||
|
086001225d | ||
|
2ea4d48e1f | ||
|
417011b497 | ||
|
ae6ce2fc3f | ||
|
8ef0c22f25 | ||
|
a5ae3a2649 | ||
|
306b2896cf | ||
|
818ae1331a | ||
|
8f6a840c63 | ||
|
be221df432 | ||
|
499412ec4c | ||
|
c36a7ed7cd | ||
|
14588726a1 | ||
|
aa01f57b7f | ||
|
b3170a8cef | ||
|
43a832beee | ||
|
39c9bb226a | ||
|
945fe53724 | ||
|
8367a97ad8 | ||
|
365d399192 | ||
|
839247df05 | ||
|
f5edf564c8 | ||
|
c3f5a2c536 | ||
|
c6a598e292 | ||
|
eecb062c32 | ||
|
50583f6bae | ||
|
3b883aeffc | ||
|
baca358b53 | ||
|
514db10f97 | ||
|
31afe6450e | ||
|
7e9017caa2 | ||
|
b6dfbbd6bc | ||
|
3ffa5af7ec | ||
|
02cf6a73cc | ||
|
15c92d48df | ||
|
a84cdaa694 | ||
|
9f9ca4c9fc | ||
|
985e263300 | ||
|
1ec246d16b | ||
|
54dbcda64d | ||
|
7a049ec79c | ||
|
77dcffe77d | ||
|
8ab46965bd | ||
|
2503ef17eb | ||
|
0195110b5b | ||
|
55fc498d27 | ||
|
50e4910c63 | ||
|
f29fe7407e | ||
|
3c8bd9ef54 | ||
|
952a42af6c | ||
|
17bd91fd71 | ||
|
3d5f6d3a4a | ||
|
b31b1301e2 | ||
|
69bc64b6d8 | ||
|
e0e2423e9a | ||
|
a6ea770365 | ||
|
5f22e50c89 | ||
|
d867c8ba5c | ||
|
d0418857ce | ||
|
48e9f6ec84 | ||
|
acddcf5bbf | ||
|
788918b85d | ||
|
6e5817b773 | ||
|
7480e41836 | ||
|
9215b3c508 | ||
|
64e500f3c8 | ||
|
3307ce3ba2 | ||
|
6f50f15678 | ||
|
cee09816e3 | ||
|
cdbad22d22 | ||
|
b0c9f047e2 | ||
|
6bd13c2948 | ||
|
84ad508437 | ||
|
4f909776cf | ||
|
962c470806 | ||
|
46d443fbad | ||
|
2608d40f2a | ||
|
3cf822853f | ||
|
26708bccc9 | ||
|
d75b539bad | ||
|
cfffa29c8a | ||
|
925df245d3 | ||
|
962a8cd303 | ||
|
6509ba05df | ||
|
579dbf3c1d | ||
|
aa5181db67 | ||
|
67be6df2b1 | ||
|
0f5379494a | ||
|
d09e2419ba | ||
|
e1a71f6ba1 | ||
|
dcb78991c4 | ||
|
9e8238cdc6 | ||
|
a4e5abf492 | ||
|
3979955e69 | ||
|
5fd09f0e02 | ||
|
af4982b22c | ||
|
29dc1d407d | ||
|
5b27aaa62c | ||
|
106636a191 | ||
|
f50c4cfbbe | ||
|
87149ae489 | ||
|
c0a4ad72e1 | ||
|
404c0ee44b | ||
|
10c1506f87 | ||
|
9a43fe6468 | ||
|
95e03f2937 | ||
|
4406ed9e62 | ||
|
ff027701f5 | ||
|
c69b61f879 | ||
|
d97f5db769 |
3
.codecov.yml
Normal file
3
.codecov.yml
Normal file
@ -0,0 +1,3 @@
|
||||
ignore:
|
||||
- "output_tests/.*"
|
||||
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
/bug_test.go
|
||||
/coverage.txt
|
||||
/.idea
|
14
.travis.yml
Normal file
14
.travis.yml
Normal file
@ -0,0 +1,14 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.x
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- ./test.sh
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
21
Gopkg.lock
generated
Normal file
21
Gopkg.lock
generated
Normal file
@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/concurrent"
|
||||
packages = ["."]
|
||||
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
|
||||
version = "1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/reflect2"
|
||||
packages = ["."]
|
||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||
version = "1.0.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
26
Gopkg.toml
Normal file
26
Gopkg.toml
Normal file
@ -0,0 +1,26 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/modern-go/reflect2"
|
||||
version = "1.0.1"
|
52
README.md
52
README.md
@ -1,6 +1,32 @@
|
||||
[](https://sourcegraph.com/github.com/json-iterator/go?badge)
|
||||
[](https://pkg.go.dev/github.com/json-iterator/go)
|
||||
[](https://travis-ci.org/json-iterator/go)
|
||||
[](https://codecov.io/gh/json-iterator/go)
|
||||
[](https://goreportcard.com/report/github.com/json-iterator/go)
|
||||
[](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE)
|
||||
[](https://gitter.im/json-iterator/Lobby)
|
||||
|
||||
jsoniter (json-iterator) is fast and flexible JSON parser available in [Java](https://github.com/json-iterator/java) and [Go](https://github.com/json-iterator/go)
|
||||
A high-performance 100% compatible drop-in replacement of "encoding/json"
|
||||
|
||||
# Benchmark
|
||||
|
||||

|
||||
|
||||
Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go
|
||||
|
||||
Raw Result (easyjson requires static code generation)
|
||||
|
||||
| | ns/op | allocation bytes | allocation times |
|
||||
| --------------- | ----------- | ---------------- | ---------------- |
|
||||
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
|
||||
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
|
||||
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
|
||||
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
|
||||
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
||||
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
||||
|
||||
Always benchmark with your own workload.
|
||||
The result depends heavily on the data input.
|
||||
|
||||
# Usage
|
||||
|
||||
@ -13,11 +39,13 @@ import "encoding/json"
|
||||
json.Marshal(&data)
|
||||
```
|
||||
|
||||
with
|
||||
with
|
||||
|
||||
```go
|
||||
import "github.com/json-iterator/go"
|
||||
jsoniter.Marshal(&data)
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
json.Marshal(&data)
|
||||
```
|
||||
|
||||
Replace
|
||||
@ -30,10 +58,14 @@ json.Unmarshal(input, &data)
|
||||
with
|
||||
|
||||
```go
|
||||
import "github.com/json-iterator/go"
|
||||
jsoniter.Unmarshal(input, &data)
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
json.Unmarshal(input, &data)
|
||||
```
|
||||
|
||||
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
|
||||
|
||||
# How to get
|
||||
|
||||
```
|
||||
@ -42,4 +74,12 @@ go get github.com/json-iterator/go
|
||||
|
||||
# Contribution Welcomed !
|
||||
|
||||
Contributors
|
||||
|
||||
- [thockin](https://github.com/thockin)
|
||||
- [mattn](https://github.com/mattn)
|
||||
- [cch123](https://github.com/cch123)
|
||||
- [Oleg Shaldybin](https://github.com/olegshaldybin)
|
||||
- [Jason Toffaletti](https://github.com/toffaletti)
|
||||
|
||||
Report issue or pull request, or email taowen@gmail.com, or [](https://gitter.im/json-iterator/Lobby)
|
||||
|
150
adapter.go
Normal file
150
adapter.go
Normal file
@ -0,0 +1,150 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// RawMessage to make replace json with jsoniter
|
||||
type RawMessage []byte
|
||||
|
||||
// Unmarshal adapts to json/encoding Unmarshal API
|
||||
//
|
||||
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||
// Refer to https://godoc.org/encoding/json#Unmarshal for more information
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
return ConfigDefault.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// UnmarshalFromString is a convenient method to read from string instead of []byte
|
||||
func UnmarshalFromString(str string, v interface{}) error {
|
||||
return ConfigDefault.UnmarshalFromString(str, v)
|
||||
}
|
||||
|
||||
// Get quick method to get value from deeply nested JSON structure
|
||||
func Get(data []byte, path ...interface{}) Any {
|
||||
return ConfigDefault.Get(data, path...)
|
||||
}
|
||||
|
||||
// Marshal adapts to json/encoding Marshal API
|
||||
//
|
||||
// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API
|
||||
// Refer to https://godoc.org/encoding/json#Marshal for more information
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return ConfigDefault.Marshal(v)
|
||||
}
|
||||
|
||||
// MarshalIndent same as json.MarshalIndent. Prefix is not supported.
|
||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
return ConfigDefault.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
// MarshalToString convenient method to write as string instead of []byte
|
||||
func MarshalToString(v interface{}) (string, error) {
|
||||
return ConfigDefault.MarshalToString(v)
|
||||
}
|
||||
|
||||
// NewDecoder adapts to json/stream NewDecoder API.
|
||||
//
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
//
|
||||
// Instead of a json/encoding Decoder, an Decoder is returned
|
||||
// Refer to https://godoc.org/encoding/json#NewDecoder for more information
|
||||
func NewDecoder(reader io.Reader) *Decoder {
|
||||
return ConfigDefault.NewDecoder(reader)
|
||||
}
|
||||
|
||||
// Decoder reads and decodes JSON values from an input stream.
|
||||
// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress)
|
||||
type Decoder struct {
|
||||
iter *Iterator
|
||||
}
|
||||
|
||||
// Decode decode JSON into interface{}
|
||||
func (adapter *Decoder) Decode(obj interface{}) error {
|
||||
if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil {
|
||||
if !adapter.iter.loadMore() {
|
||||
return io.EOF
|
||||
}
|
||||
}
|
||||
adapter.iter.ReadVal(obj)
|
||||
err := adapter.iter.Error
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return adapter.iter.Error
|
||||
}
|
||||
|
||||
// More is there more?
|
||||
func (adapter *Decoder) More() bool {
|
||||
iter := adapter.iter
|
||||
if iter.Error != nil {
|
||||
return false
|
||||
}
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return c != ']' && c != '}'
|
||||
}
|
||||
|
||||
// Buffered remaining buffer
|
||||
func (adapter *Decoder) Buffered() io.Reader {
|
||||
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
|
||||
return bytes.NewReader(remaining)
|
||||
}
|
||||
|
||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||
// Number instead of as a float64.
|
||||
func (adapter *Decoder) UseNumber() {
|
||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||
cfg.UseNumber = true
|
||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
func (adapter *Decoder) DisallowUnknownFields() {
|
||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||
cfg.DisallowUnknownFields = true
|
||||
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// NewEncoder same as json.NewEncoder
|
||||
func NewEncoder(writer io.Writer) *Encoder {
|
||||
return ConfigDefault.NewEncoder(writer)
|
||||
}
|
||||
|
||||
// Encoder same as json.Encoder
|
||||
type Encoder struct {
|
||||
stream *Stream
|
||||
}
|
||||
|
||||
// Encode encode interface{} as JSON to io.Writer
|
||||
func (adapter *Encoder) Encode(val interface{}) error {
|
||||
adapter.stream.WriteVal(val)
|
||||
adapter.stream.WriteRaw("\n")
|
||||
adapter.stream.Flush()
|
||||
return adapter.stream.Error
|
||||
}
|
||||
|
||||
// SetIndent set the indention. Prefix is not supported
|
||||
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
||||
config := adapter.stream.cfg.configBeforeFrozen
|
||||
config.IndentionStep = len(indent)
|
||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// SetEscapeHTML escape html by default, set to false to disable
|
||||
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
||||
config := adapter.stream.cfg.configBeforeFrozen
|
||||
config.EscapeHTML = escapeHTML
|
||||
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func Valid(data []byte) bool {
|
||||
return ConfigDefault.Valid(data)
|
||||
}
|
325
any.go
Normal file
325
any.go
Normal file
@ -0,0 +1,325 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/modern-go/reflect2"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Any generic object representation.
|
||||
// The lazy json implementation holds []byte and parse lazily.
|
||||
type Any interface {
|
||||
LastError() error
|
||||
ValueType() ValueType
|
||||
MustBeValid() Any
|
||||
ToBool() bool
|
||||
ToInt() int
|
||||
ToInt32() int32
|
||||
ToInt64() int64
|
||||
ToUint() uint
|
||||
ToUint32() uint32
|
||||
ToUint64() uint64
|
||||
ToFloat32() float32
|
||||
ToFloat64() float64
|
||||
ToString() string
|
||||
ToVal(val interface{})
|
||||
Get(path ...interface{}) Any
|
||||
Size() int
|
||||
Keys() []string
|
||||
GetInterface() interface{}
|
||||
WriteTo(stream *Stream)
|
||||
}
|
||||
|
||||
type baseAny struct{}
|
||||
|
||||
func (any *baseAny) Get(path ...interface{}) Any {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
||||
}
|
||||
|
||||
func (any *baseAny) Size() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *baseAny) Keys() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (any *baseAny) ToVal(obj interface{}) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// WrapInt32 turn int32 into Any interface
|
||||
func WrapInt32(val int32) Any {
|
||||
return &int32Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
// WrapInt64 turn int64 into Any interface
|
||||
func WrapInt64(val int64) Any {
|
||||
return &int64Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
// WrapUint32 turn uint32 into Any interface
|
||||
func WrapUint32(val uint32) Any {
|
||||
return &uint32Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
// WrapUint64 turn uint64 into Any interface
|
||||
func WrapUint64(val uint64) Any {
|
||||
return &uint64Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
// WrapFloat64 turn float64 into Any interface
|
||||
func WrapFloat64(val float64) Any {
|
||||
return &floatAny{baseAny{}, val}
|
||||
}
|
||||
|
||||
// WrapString turn string into Any interface
|
||||
func WrapString(val string) Any {
|
||||
return &stringAny{baseAny{}, val}
|
||||
}
|
||||
|
||||
// Wrap turn a go object into Any interface
|
||||
func Wrap(val interface{}) Any {
|
||||
if val == nil {
|
||||
return &nilAny{}
|
||||
}
|
||||
asAny, isAny := val.(Any)
|
||||
if isAny {
|
||||
return asAny
|
||||
}
|
||||
typ := reflect2.TypeOf(val)
|
||||
switch typ.Kind() {
|
||||
case reflect.Slice:
|
||||
return wrapArray(val)
|
||||
case reflect.Struct:
|
||||
return wrapStruct(val)
|
||||
case reflect.Map:
|
||||
return wrapMap(val)
|
||||
case reflect.String:
|
||||
return WrapString(val.(string))
|
||||
case reflect.Int:
|
||||
if strconv.IntSize == 32 {
|
||||
return WrapInt32(int32(val.(int)))
|
||||
}
|
||||
return WrapInt64(int64(val.(int)))
|
||||
case reflect.Int8:
|
||||
return WrapInt32(int32(val.(int8)))
|
||||
case reflect.Int16:
|
||||
return WrapInt32(int32(val.(int16)))
|
||||
case reflect.Int32:
|
||||
return WrapInt32(val.(int32))
|
||||
case reflect.Int64:
|
||||
return WrapInt64(val.(int64))
|
||||
case reflect.Uint:
|
||||
if strconv.IntSize == 32 {
|
||||
return WrapUint32(uint32(val.(uint)))
|
||||
}
|
||||
return WrapUint64(uint64(val.(uint)))
|
||||
case reflect.Uintptr:
|
||||
if ptrSize == 32 {
|
||||
return WrapUint32(uint32(val.(uintptr)))
|
||||
}
|
||||
return WrapUint64(uint64(val.(uintptr)))
|
||||
case reflect.Uint8:
|
||||
return WrapUint32(uint32(val.(uint8)))
|
||||
case reflect.Uint16:
|
||||
return WrapUint32(uint32(val.(uint16)))
|
||||
case reflect.Uint32:
|
||||
return WrapUint32(uint32(val.(uint32)))
|
||||
case reflect.Uint64:
|
||||
return WrapUint64(val.(uint64))
|
||||
case reflect.Float32:
|
||||
return WrapFloat64(float64(val.(float32)))
|
||||
case reflect.Float64:
|
||||
return WrapFloat64(val.(float64))
|
||||
case reflect.Bool:
|
||||
if val.(bool) == true {
|
||||
return &trueAny{}
|
||||
}
|
||||
return &falseAny{}
|
||||
}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
|
||||
}
|
||||
|
||||
// ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
|
||||
func (iter *Iterator) ReadAny() Any {
|
||||
return iter.readAny()
|
||||
}
|
||||
|
||||
func (iter *Iterator) readAny() Any {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case '"':
|
||||
iter.unreadByte()
|
||||
return &stringAny{baseAny{}, iter.ReadString()}
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
||||
return &nilAny{}
|
||||
case 't':
|
||||
iter.skipThreeBytes('r', 'u', 'e') // true
|
||||
return &trueAny{}
|
||||
case 'f':
|
||||
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
||||
return &falseAny{}
|
||||
case '{':
|
||||
return iter.readObjectAny()
|
||||
case '[':
|
||||
return iter.readArrayAny()
|
||||
case '-':
|
||||
return iter.readNumberAny(false)
|
||||
case 0:
|
||||
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
||||
default:
|
||||
return iter.readNumberAny(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readNumberAny(positive bool) Any {
|
||||
iter.startCapture(iter.head - 1)
|
||||
iter.skipNumber()
|
||||
lazyBuf := iter.stopCapture()
|
||||
return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectAny() Any {
|
||||
iter.startCapture(iter.head - 1)
|
||||
iter.skipObject()
|
||||
lazyBuf := iter.stopCapture()
|
||||
return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readArrayAny() Any {
|
||||
iter.startCapture(iter.head - 1)
|
||||
iter.skipArray()
|
||||
lazyBuf := iter.stopCapture()
|
||||
return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
||||
}
|
||||
|
||||
func locateObjectField(iter *Iterator, target string) []byte {
|
||||
var found []byte
|
||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||
if field == target {
|
||||
found = iter.SkipAndReturnBytes()
|
||||
return false
|
||||
}
|
||||
iter.Skip()
|
||||
return true
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func locateArrayElement(iter *Iterator, target int) []byte {
|
||||
var found []byte
|
||||
n := 0
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
if n == target {
|
||||
found = iter.SkipAndReturnBytes()
|
||||
return false
|
||||
}
|
||||
iter.Skip()
|
||||
n++
|
||||
return true
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func locatePath(iter *Iterator, path []interface{}) Any {
|
||||
for i, pathKeyObj := range path {
|
||||
switch pathKey := pathKeyObj.(type) {
|
||||
case string:
|
||||
valueBytes := locateObjectField(iter, pathKey)
|
||||
if valueBytes == nil {
|
||||
return newInvalidAny(path[i:])
|
||||
}
|
||||
iter.ResetBytes(valueBytes)
|
||||
case int:
|
||||
valueBytes := locateArrayElement(iter, pathKey)
|
||||
if valueBytes == nil {
|
||||
return newInvalidAny(path[i:])
|
||||
}
|
||||
iter.ResetBytes(valueBytes)
|
||||
case int32:
|
||||
if '*' == pathKey {
|
||||
return iter.readAny().Get(path[i:]...)
|
||||
}
|
||||
return newInvalidAny(path[i:])
|
||||
default:
|
||||
return newInvalidAny(path[i:])
|
||||
}
|
||||
}
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return &invalidAny{baseAny{}, iter.Error}
|
||||
}
|
||||
return iter.readAny()
|
||||
}
|
||||
|
||||
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
|
||||
|
||||
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
|
||||
if typ == anyType {
|
||||
return &directAnyCodec{}
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{
|
||||
valType: typ,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
|
||||
if typ == anyType {
|
||||
return &directAnyCodec{}
|
||||
}
|
||||
if typ.Implements(anyType) {
|
||||
return &anyCodec{
|
||||
valType: typ,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type anyCodec struct {
|
||||
valType reflect2.Type
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
obj := codec.valType.UnsafeIndirect(ptr)
|
||||
any := obj.(Any)
|
||||
any.WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
obj := codec.valType.UnsafeIndirect(ptr)
|
||||
any := obj.(Any)
|
||||
return any.Size() == 0
|
||||
}
|
||||
|
||||
type directAnyCodec struct {
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*(*Any)(ptr) = iter.readAny()
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
any := *(*Any)(ptr)
|
||||
if any == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
any.WriteTo(stream)
|
||||
}
|
||||
|
||||
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
any := *(*Any)(ptr)
|
||||
return any.Size() == 0
|
||||
}
|
278
any_array.go
Normal file
278
any_array.go
Normal file
@ -0,0 +1,278 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type arrayLazyAny struct {
|
||||
baseAny
|
||||
cfg *frozenConfig
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ValueType() ValueType {
|
||||
return ArrayValue
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToBool() bool {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
return iter.ReadArray()
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt() int {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt32() int32 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt64() int64 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint() uint {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint32() uint32 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint64() uint64 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat32() float32 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat64() float64 {
|
||||
if any.ToBool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToVal(val interface{}) {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(val)
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
switch firstPath := path[0].(type) {
|
||||
case int:
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
valueBytes := locateArrayElement(iter, firstPath)
|
||||
if valueBytes == nil {
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
iter.ResetBytes(valueBytes)
|
||||
return locatePath(iter, path[1:])
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
arr := make([]Any, 0)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
found := iter.readAny().Get(path[1:]...)
|
||||
if found.ValueType() != InvalidValue {
|
||||
arr = append(arr, found)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return wrapArray(arr)
|
||||
}
|
||||
return newInvalidAny(path)
|
||||
default:
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Size() int {
|
||||
size := 0
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
size++
|
||||
iter.Skip()
|
||||
return true
|
||||
})
|
||||
return size
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) GetInterface() interface{} {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
return iter.Read()
|
||||
}
|
||||
|
||||
type arrayAny struct {
|
||||
baseAny
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapArray(val interface{}) *arrayAny {
|
||||
return &arrayAny{baseAny{}, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *arrayAny) ValueType() ValueType {
|
||||
return ArrayValue
|
||||
}
|
||||
|
||||
func (any *arrayAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *arrayAny) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToBool() bool {
|
||||
return any.val.Len() != 0
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt() int {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt32() int32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt64() int64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint() uint {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint32() uint32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint64() uint64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToFloat32() float32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToFloat64() float64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToString() string {
|
||||
str, _ := MarshalToString(any.val.Interface())
|
||||
return str
|
||||
}
|
||||
|
||||
func (any *arrayAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
switch firstPath := path[0].(type) {
|
||||
case int:
|
||||
if firstPath < 0 || firstPath >= any.val.Len() {
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
return Wrap(any.val.Index(firstPath).Interface())
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
mappedAll := make([]Any, 0)
|
||||
for i := 0; i < any.val.Len(); i++ {
|
||||
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll = append(mappedAll, mapped)
|
||||
}
|
||||
}
|
||||
return wrapArray(mappedAll)
|
||||
}
|
||||
return newInvalidAny(path)
|
||||
default:
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) Size() int {
|
||||
return any.val.Len()
|
||||
}
|
||||
|
||||
func (any *arrayAny) WriteTo(stream *Stream) {
|
||||
stream.WriteVal(any.val)
|
||||
}
|
||||
|
||||
func (any *arrayAny) GetInterface() interface{} {
|
||||
return any.val.Interface()
|
||||
}
|
@ -61,7 +61,11 @@ func (any *trueAny) GetInterface() interface{} {
|
||||
}
|
||||
|
||||
func (any *trueAny) ValueType() ValueType {
|
||||
return Bool
|
||||
return BoolValue
|
||||
}
|
||||
|
||||
func (any *trueAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
type falseAny struct {
|
||||
@ -125,5 +129,9 @@ func (any *falseAny) GetInterface() interface{} {
|
||||
}
|
||||
|
||||
func (any *falseAny) ValueType() ValueType {
|
||||
return Bool
|
||||
return BoolValue
|
||||
}
|
||||
|
||||
func (any *falseAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
83
any_float.go
Normal file
83
any_float.go
Normal file
@ -0,0 +1,83 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type floatAny struct {
|
||||
baseAny
|
||||
val float64
|
||||
}
|
||||
|
||||
func (any *floatAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *floatAny) ValueType() ValueType {
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *floatAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *floatAny) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *floatAny) ToBool() bool {
|
||||
return any.ToFloat64() != 0
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt64() int64 {
|
||||
return int64(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint() uint {
|
||||
if any.val > 0 {
|
||||
return uint(any.val)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint32() uint32 {
|
||||
if any.val > 0 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint64() uint64 {
|
||||
if any.val > 0 {
|
||||
return uint64(any.val)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *floatAny) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToFloat64() float64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *floatAny) ToString() string {
|
||||
return strconv.FormatFloat(any.val, 'E', -1, 64)
|
||||
}
|
||||
|
||||
func (any *floatAny) WriteTo(stream *Stream) {
|
||||
stream.WriteFloat64(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
@ -14,7 +14,11 @@ func (any *int32Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *int32Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *int32Any) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *int32Any) ToBool() bool {
|
||||
@ -67,4 +71,4 @@ func (any *int32Any) Parse() *Iterator {
|
||||
|
||||
func (any *int32Any) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
||||
}
|
74
any_int64.go
Normal file
74
any_int64.go
Normal file
@ -0,0 +1,74 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type int64Any struct {
|
||||
baseAny
|
||||
val int64
|
||||
}
|
||||
|
||||
func (any *int64Any) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *int64Any) ValueType() ValueType {
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *int64Any) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *int64Any) ToBool() bool {
|
||||
return any.val != 0
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt64() int64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint() uint {
|
||||
return uint(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint32() uint32 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint64() uint64 {
|
||||
return uint64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToFloat64() float64 {
|
||||
return float64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToString() string {
|
||||
return strconv.FormatInt(any.val, 10)
|
||||
}
|
||||
|
||||
func (any *int64Any) WriteTo(stream *Stream) {
|
||||
stream.WriteInt64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *int64Any) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
@ -7,12 +7,20 @@ type invalidAny struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func newInvalidAny(path []interface{}) *invalidAny {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)}
|
||||
}
|
||||
|
||||
func (any *invalidAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *invalidAny) ValueType() ValueType {
|
||||
return Invalid
|
||||
return InvalidValue
|
||||
}
|
||||
|
||||
func (any *invalidAny) MustBeValid() Any {
|
||||
panic(any.err)
|
||||
}
|
||||
|
||||
func (any *invalidAny) ToBool() bool {
|
||||
@ -61,9 +69,8 @@ func (any *invalidAny) WriteTo(stream *Stream) {
|
||||
func (any *invalidAny) Get(path ...interface{}) Any {
|
||||
if any.err == nil {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)}
|
||||
} else {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)}
|
||||
}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)}
|
||||
}
|
||||
|
||||
func (any *invalidAny) Parse() *Iterator {
|
@ -9,7 +9,11 @@ func (any *nilAny) LastError() error {
|
||||
}
|
||||
|
||||
func (any *nilAny) ValueType() ValueType {
|
||||
return Nil
|
||||
return NilValue
|
||||
}
|
||||
|
||||
func (any *nilAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *nilAny) ToBool() bool {
|
||||
@ -62,4 +66,4 @@ func (any *nilAny) Parse() *Iterator {
|
||||
|
||||
func (any *nilAny) GetInterface() interface{} {
|
||||
return nil
|
||||
}
|
||||
}
|
123
any_number.go
Normal file
123
any_number.go
Normal file
@ -0,0 +1,123 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type numberLazyAny struct {
|
||||
baseAny
|
||||
cfg *frozenConfig
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ValueType() ValueType {
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToBool() bool {
|
||||
return any.ToFloat64() != 0
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToInt() int {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToInt32() int32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt32()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToInt64() int64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadInt64()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToUint() uint {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToUint32() uint32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint32()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToUint64() uint64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadUint64()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToFloat32() float32 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadFloat32()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToFloat64() float64 {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
val := iter.ReadFloat64()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
any.err = iter.Error
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *numberLazyAny) GetInterface() interface{} {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
return iter.Read()
|
||||
}
|
374
any_object.go
Normal file
374
any_object.go
Normal file
@ -0,0 +1,374 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type objectLazyAny struct {
|
||||
baseAny
|
||||
cfg *frozenConfig
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ValueType() ValueType {
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToBool() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt32() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt64() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint() uint {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint32() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint64() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToFloat32() float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToFloat64() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToVal(obj interface{}) {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(obj)
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
switch firstPath := path[0].(type) {
|
||||
case string:
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
valueBytes := locateObjectField(iter, firstPath)
|
||||
if valueBytes == nil {
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
iter.ResetBytes(valueBytes)
|
||||
return locatePath(iter, path[1:])
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
mappedAll := map[string]Any{}
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadMapCB(func(iter *Iterator, field string) bool {
|
||||
mapped := locatePath(iter, path[1:])
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[field] = mapped
|
||||
}
|
||||
return true
|
||||
})
|
||||
return wrapMap(mappedAll)
|
||||
}
|
||||
return newInvalidAny(path)
|
||||
default:
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Keys() []string {
|
||||
keys := []string{}
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadMapCB(func(iter *Iterator, field string) bool {
|
||||
iter.Skip()
|
||||
keys = append(keys, field)
|
||||
return true
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Size() int {
|
||||
size := 0
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||
iter.Skip()
|
||||
size++
|
||||
return true
|
||||
})
|
||||
return size
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) GetInterface() interface{} {
|
||||
iter := any.cfg.BorrowIterator(any.buf)
|
||||
defer any.cfg.ReturnIterator(iter)
|
||||
return iter.Read()
|
||||
}
|
||||
|
||||
type objectAny struct {
|
||||
baseAny
|
||||
err error
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapStruct(val interface{}) *objectAny {
|
||||
return &objectAny{baseAny{}, nil, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *objectAny) ValueType() ValueType {
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *objectAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *objectAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *objectAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *objectAny) ToBool() bool {
|
||||
return any.val.NumField() != 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt32() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt64() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint() uint {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint32() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint64() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToFloat32() float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToFloat64() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToString() string {
|
||||
str, err := MarshalToString(any.val.Interface())
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
|
||||
func (any *objectAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
switch firstPath := path[0].(type) {
|
||||
case string:
|
||||
field := any.val.FieldByName(firstPath)
|
||||
if !field.IsValid() {
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
return Wrap(field.Interface())
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
mappedAll := map[string]Any{}
|
||||
for i := 0; i < any.val.NumField(); i++ {
|
||||
field := any.val.Field(i)
|
||||
if field.CanInterface() {
|
||||
mapped := Wrap(field.Interface()).Get(path[1:]...)
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[any.val.Type().Field(i).Name] = mapped
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapMap(mappedAll)
|
||||
}
|
||||
return newInvalidAny(path)
|
||||
default:
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectAny) Keys() []string {
|
||||
keys := make([]string, 0, any.val.NumField())
|
||||
for i := 0; i < any.val.NumField(); i++ {
|
||||
keys = append(keys, any.val.Type().Field(i).Name)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *objectAny) Size() int {
|
||||
return any.val.NumField()
|
||||
}
|
||||
|
||||
func (any *objectAny) WriteTo(stream *Stream) {
|
||||
stream.WriteVal(any.val)
|
||||
}
|
||||
|
||||
func (any *objectAny) GetInterface() interface{} {
|
||||
return any.val.Interface()
|
||||
}
|
||||
|
||||
type mapAny struct {
|
||||
baseAny
|
||||
err error
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapMap(val interface{}) *mapAny {
|
||||
return &mapAny{baseAny{}, nil, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *mapAny) ValueType() ValueType {
|
||||
return ObjectValue
|
||||
}
|
||||
|
||||
func (any *mapAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *mapAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *mapAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *mapAny) ToBool() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt32() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt64() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint() uint {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint32() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint64() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToFloat32() float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToFloat64() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToString() string {
|
||||
str, err := MarshalToString(any.val.Interface())
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
|
||||
func (any *mapAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
switch firstPath := path[0].(type) {
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
mappedAll := map[string]Any{}
|
||||
for _, key := range any.val.MapKeys() {
|
||||
keyAsStr := key.String()
|
||||
element := Wrap(any.val.MapIndex(key).Interface())
|
||||
mapped := element.Get(path[1:]...)
|
||||
if mapped.ValueType() != InvalidValue {
|
||||
mappedAll[keyAsStr] = mapped
|
||||
}
|
||||
}
|
||||
return wrapMap(mappedAll)
|
||||
}
|
||||
return newInvalidAny(path)
|
||||
default:
|
||||
value := any.val.MapIndex(reflect.ValueOf(firstPath))
|
||||
if !value.IsValid() {
|
||||
return newInvalidAny(path)
|
||||
}
|
||||
return Wrap(value.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func (any *mapAny) Keys() []string {
|
||||
keys := make([]string, 0, any.val.Len())
|
||||
for _, key := range any.val.MapKeys() {
|
||||
keys = append(keys, key.String())
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *mapAny) Size() int {
|
||||
return any.val.Len()
|
||||
}
|
||||
|
||||
func (any *mapAny) WriteTo(stream *Stream) {
|
||||
stream.WriteVal(any.val)
|
||||
}
|
||||
|
||||
func (any *mapAny) GetInterface() interface{} {
|
||||
return any.val.Interface()
|
||||
}
|
166
any_str.go
Normal file
166
any_str.go
Normal file
@ -0,0 +1,166 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type stringAny struct {
|
||||
baseAny
|
||||
val string
|
||||
}
|
||||
|
||||
func (any *stringAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
||||
}
|
||||
|
||||
func (any *stringAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *stringAny) ValueType() ValueType {
|
||||
return StringValue
|
||||
}
|
||||
|
||||
func (any *stringAny) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *stringAny) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *stringAny) ToBool() bool {
|
||||
str := any.ToString()
|
||||
if str == "0" {
|
||||
return false
|
||||
}
|
||||
for _, c := range str {
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt() int {
|
||||
return int(any.ToInt64())
|
||||
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt32() int32 {
|
||||
return int32(any.ToInt64())
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt64() int64 {
|
||||
if any.val == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
flag := 1
|
||||
startPos := 0
|
||||
if any.val[0] == '+' || any.val[0] == '-' {
|
||||
startPos = 1
|
||||
}
|
||||
|
||||
if any.val[0] == '-' {
|
||||
flag = -1
|
||||
}
|
||||
|
||||
endPos := startPos
|
||||
for i := startPos; i < len(any.val); i++ {
|
||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
||||
endPos = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64)
|
||||
return int64(flag) * parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint() uint {
|
||||
return uint(any.ToUint64())
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint32() uint32 {
|
||||
return uint32(any.ToUint64())
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint64() uint64 {
|
||||
if any.val == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
startPos := 0
|
||||
|
||||
if any.val[0] == '-' {
|
||||
return 0
|
||||
}
|
||||
if any.val[0] == '+' {
|
||||
startPos = 1
|
||||
}
|
||||
|
||||
endPos := startPos
|
||||
for i := startPos; i < len(any.val); i++ {
|
||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
||||
endPos = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64)
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToFloat32() float32 {
|
||||
return float32(any.ToFloat64())
|
||||
}
|
||||
|
||||
func (any *stringAny) ToFloat64() float64 {
|
||||
if len(any.val) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// first char invalid
|
||||
if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') {
|
||||
return 0
|
||||
}
|
||||
|
||||
// extract valid num expression from string
|
||||
// eg 123true => 123, -12.12xxa => -12.12
|
||||
endPos := 1
|
||||
for i := 1; i < len(any.val); i++ {
|
||||
if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' {
|
||||
endPos = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
// end position is the first char which is not digit
|
||||
if any.val[i] >= '0' && any.val[i] <= '9' {
|
||||
endPos = i + 1
|
||||
} else {
|
||||
endPos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.ParseFloat(any.val[:endPos], 64)
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToString() string {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *stringAny) WriteTo(stream *Stream) {
|
||||
stream.WriteString(any.val)
|
||||
}
|
||||
|
||||
func (any *stringAny) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
123
any_tests/jsoniter_any_array_test.go
Normal file
123
any_tests/jsoniter_any_array_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_empty_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[]"))
|
||||
should.Equal(jsoniter.ArrayValue, any.Get().ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(0.3).ValueType())
|
||||
should.Equal(0, any.Size())
|
||||
should.Equal(jsoniter.ArrayValue, any.ValueType())
|
||||
should.Nil(any.LastError())
|
||||
should.Equal(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())
|
||||
}
|
||||
|
||||
func Test_read_one_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[1]"))
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_two_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[1,2]"))
|
||||
should.Equal(1, any.Get(0).ToInt())
|
||||
should.Equal(2, any.Size())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(1, any.ToInt())
|
||||
should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface())
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("[1,2]", string(stream.Buffer()))
|
||||
arr := []int{}
|
||||
any.ToVal(&arr)
|
||||
should.Equal([]int{1, 2}, arr)
|
||||
}
|
||||
|
||||
func Test_wrap_array_and_convert_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap([]int{1, 2, 3})
|
||||
any2 := jsoniter.Wrap([]int{})
|
||||
|
||||
should.Equal("[1,2,3]", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
should.False(any2.ToBool())
|
||||
|
||||
should.Equal(1, any.ToInt())
|
||||
should.Equal(0, any2.ToInt())
|
||||
should.Equal(int32(1), any.ToInt32())
|
||||
should.Equal(int32(0), any2.ToInt32())
|
||||
should.Equal(int64(1), any.ToInt64())
|
||||
should.Equal(int64(0), any2.ToInt64())
|
||||
should.Equal(uint(1), any.ToUint())
|
||||
should.Equal(uint(0), any2.ToUint())
|
||||
should.Equal(uint32(1), any.ToUint32())
|
||||
should.Equal(uint32(0), any2.ToUint32())
|
||||
should.Equal(uint64(1), any.ToUint64())
|
||||
should.Equal(uint64(0), any2.ToUint64())
|
||||
should.Equal(float32(1), any.ToFloat32())
|
||||
should.Equal(float32(0), any2.ToFloat32())
|
||||
should.Equal(float64(1), any.ToFloat64())
|
||||
should.Equal(float64(0), any2.ToFloat64())
|
||||
should.Equal(3, any.Size())
|
||||
should.Equal(0, any2.Size())
|
||||
|
||||
var i interface{} = []int{1, 2, 3}
|
||||
should.Equal(i, any.GetInterface())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[1,[2,3],4]"))
|
||||
should.Equal(3, any.Get(1, 1).ToInt())
|
||||
should.Equal("[1,[2,3],4]", any.ToString())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[[1],[2],[3,4]]"))
|
||||
should.Equal("[1,2,3]", any.Get('*', 0).ToString())
|
||||
any = jsoniter.Get([]byte("[[[1],[2],[3,4]]]"), 0, '*', 0)
|
||||
should.Equal("[1,2,3]", any.ToString())
|
||||
}
|
||||
|
||||
func Test_array_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap([][]int{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
{5, 6},
|
||||
})
|
||||
should.Equal("[1,3,5]", any.Get('*', 0).ToString())
|
||||
should.Equal(jsoniter.ArrayValue, any.ValueType())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(1, any.Get(0, 0).ToInt())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("[]"))
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1, 1).ValueType())
|
||||
should.NotNil(any.Get(1, 1).LastError())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get("1").ValueType())
|
||||
should.NotNil(any.Get("1").LastError())
|
||||
}
|
||||
|
||||
func Test_invalid_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("["), 0)
|
||||
should.Equal(jsoniter.InvalidValue, any.ValueType())
|
||||
}
|
65
any_tests/jsoniter_any_bool_test.go
Normal file
65
any_tests/jsoniter_any_bool_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var boolConvertMap = map[string]bool{
|
||||
"null": false,
|
||||
"true": true,
|
||||
"false": false,
|
||||
|
||||
`"true"`: true,
|
||||
`"false"`: true,
|
||||
|
||||
"123": true,
|
||||
`"123"`: true,
|
||||
"0": false,
|
||||
`"0"`: false,
|
||||
"-1": true,
|
||||
`"-1"`: true,
|
||||
|
||||
"1.1": true,
|
||||
"0.0": false,
|
||||
"-1.1": true,
|
||||
`""`: false,
|
||||
"[1,2]": true,
|
||||
"[]": false,
|
||||
"{}": true,
|
||||
`{"abc":1}`: true,
|
||||
}
|
||||
|
||||
func Test_read_bool_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
var any jsoniter.Any
|
||||
for k, v := range boolConvertMap {
|
||||
any = jsoniter.Get([]byte(k))
|
||||
if v {
|
||||
should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k))
|
||||
} else {
|
||||
should.False(any.ToBool(), fmt.Sprintf("origin val is %v", k))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_write_bool_to_stream(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("true"))
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("true", string(stream.Buffer()))
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
|
||||
any = jsoniter.Get([]byte("false"))
|
||||
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("false", string(stream.Buffer()))
|
||||
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
}
|
101
any_tests/jsoniter_any_float_test.go
Normal file
101
any_tests/jsoniter_any_float_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var floatConvertMap = map[string]float64{
|
||||
"null": 0,
|
||||
"true": 1,
|
||||
"false": 0,
|
||||
|
||||
`"true"`: 0,
|
||||
`"false"`: 0,
|
||||
|
||||
"1e1": 10,
|
||||
"1e+1": 10,
|
||||
"1e-1": .1,
|
||||
"1E1": 10,
|
||||
"1E+1": 10,
|
||||
"1E-1": .1,
|
||||
|
||||
"-1e1": -10,
|
||||
"-1e+1": -10,
|
||||
"-1e-1": -.1,
|
||||
"-1E1": -10,
|
||||
"-1E+1": -10,
|
||||
"-1E-1": -.1,
|
||||
|
||||
`"1e1"`: 10,
|
||||
`"1e+1"`: 10,
|
||||
`"1e-1"`: .1,
|
||||
`"1E1"`: 10,
|
||||
`"1E+1"`: 10,
|
||||
`"1E-1"`: .1,
|
||||
|
||||
`"-1e1"`: -10,
|
||||
`"-1e+1"`: -10,
|
||||
`"-1e-1"`: -.1,
|
||||
`"-1E1"`: -10,
|
||||
`"-1E+1"`: -10,
|
||||
`"-1E-1"`: -.1,
|
||||
|
||||
"123": 123,
|
||||
`"123true"`: 123,
|
||||
`"+"`: 0,
|
||||
`"-"`: 0,
|
||||
|
||||
`"-123true"`: -123,
|
||||
`"-99.9true"`: -99.9,
|
||||
"0": 0,
|
||||
`"0"`: 0,
|
||||
"-1": -1,
|
||||
|
||||
"1.1": 1.1,
|
||||
"0.0": 0,
|
||||
"-1.1": -1.1,
|
||||
`"+1.1"`: 1.1,
|
||||
`""`: 0,
|
||||
"[1,2]": 1,
|
||||
"[]": 0,
|
||||
"{}": 0,
|
||||
`{"abc":1}`: 0,
|
||||
}
|
||||
|
||||
func Test_read_any_to_float(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for k, v := range floatConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(float64(v), any.ToFloat64(), "the original val is "+k)
|
||||
}
|
||||
|
||||
for k, v := range floatConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(float32(v), any.ToFloat32(), "the original val is "+k)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_float_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.WrapFloat64(12.3)
|
||||
anyFloat64 := float64(12.3)
|
||||
any2 := jsoniter.WrapFloat64(-1.1)
|
||||
should.Equal(float64(12.3), any.ToFloat64())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(float32(anyFloat64), any.ToFloat32())
|
||||
should.Equal(int(anyFloat64), any.ToInt())
|
||||
should.Equal(int32(anyFloat64), any.ToInt32())
|
||||
should.Equal(int64(anyFloat64), any.ToInt64())
|
||||
should.Equal(uint(anyFloat64), any.ToUint())
|
||||
should.Equal(uint32(anyFloat64), any.ToUint32())
|
||||
should.Equal(uint64(anyFloat64), any.ToUint64())
|
||||
should.Equal(uint(0), any2.ToUint())
|
||||
should.Equal(uint32(0), any2.ToUint32())
|
||||
should.Equal(uint64(0), any2.ToUint64())
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
|
||||
should.Equal("1.23E+01", any.ToString())
|
||||
}
|
198
any_tests/jsoniter_any_int_test.go
Normal file
198
any_tests/jsoniter_any_int_test.go
Normal file
@ -0,0 +1,198 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var intConvertMap = map[string]int{
|
||||
"null": 0,
|
||||
"321.1": 321,
|
||||
"-321.1": -321,
|
||||
`"1.1"`: 1,
|
||||
`"-321.1"`: -321,
|
||||
"0.0": 0,
|
||||
"0": 0,
|
||||
`"0"`: 0,
|
||||
`"0.0"`: 0,
|
||||
"-1.1": -1,
|
||||
"true": 1,
|
||||
"false": 0,
|
||||
`"true"`: 0,
|
||||
`"false"`: 0,
|
||||
`"true123"`: 0,
|
||||
`"123true"`: 123,
|
||||
`"-123true"`: -123,
|
||||
`"1.2332e6"`: 1,
|
||||
`""`: 0,
|
||||
"+": 0,
|
||||
"-": 0,
|
||||
"[]": 0,
|
||||
"[1,2]": 1,
|
||||
`["1","2"]`: 1,
|
||||
// object in php cannot convert to int
|
||||
"{}": 0,
|
||||
}
|
||||
|
||||
func Test_read_any_to_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
// int
|
||||
for k, v := range intConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(v, any.ToInt(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
// int32
|
||||
for k, v := range intConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(int32(v), any.ToInt32(), fmt.Sprintf("original val is %v", k))
|
||||
}
|
||||
|
||||
// int64
|
||||
for k, v := range intConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(int64(v), any.ToInt64(), fmt.Sprintf("original val is %v", k))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var uintConvertMap = map[string]int{
|
||||
"null": 0,
|
||||
"321.1": 321,
|
||||
`"1.1"`: 1,
|
||||
`"-123.1"`: 0,
|
||||
"0.0": 0,
|
||||
"0": 0,
|
||||
`"0"`: 0,
|
||||
`"0.0"`: 0,
|
||||
`"00.0"`: 0,
|
||||
"true": 1,
|
||||
"false": 0,
|
||||
`"true"`: 0,
|
||||
`"false"`: 0,
|
||||
`"true123"`: 0,
|
||||
`"+1"`: 1,
|
||||
`"123true"`: 123,
|
||||
`"-123true"`: 0,
|
||||
`"1.2332e6"`: 1,
|
||||
`""`: 0,
|
||||
"+": 0,
|
||||
"-": 0,
|
||||
".": 0,
|
||||
"[]": 0,
|
||||
"[1,2]": 1,
|
||||
"{}": 0,
|
||||
"{1,2}": 0,
|
||||
"-1.1": 0,
|
||||
"-321.1": 0,
|
||||
}
|
||||
|
||||
func Test_read_any_to_uint(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint64(v), any.ToUint64(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint32(v), any.ToUint32(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
for k, v := range uintConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(uint(v), any.ToUint(), fmt.Sprintf("origin val %v", k))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_read_int64_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.WrapInt64(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
should.Equal(uint(12345), any.ToUint())
|
||||
should.Equal(uint32(12345), any.ToUint32())
|
||||
should.Equal(uint64(12345), any.ToUint64())
|
||||
should.Equal(float32(12345), any.ToFloat32())
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
func Test_read_int32_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.WrapInt32(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
should.Equal(uint(12345), any.ToUint())
|
||||
should.Equal(uint32(12345), any.ToUint32())
|
||||
should.Equal(uint64(12345), any.ToUint64())
|
||||
should.Equal(float32(12345), any.ToFloat32())
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_read_uint32_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.WrapUint32(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
should.Equal(uint(12345), any.ToUint())
|
||||
should.Equal(uint32(12345), any.ToUint32())
|
||||
should.Equal(uint64(12345), any.ToUint64())
|
||||
should.Equal(float32(12345), any.ToFloat32())
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_read_uint64_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.WrapUint64(12345)
|
||||
should.Equal(12345, any.ToInt())
|
||||
should.Equal(int32(12345), any.ToInt32())
|
||||
should.Equal(int64(12345), any.ToInt64())
|
||||
should.Equal(uint(12345), any.ToUint())
|
||||
should.Equal(uint32(12345), any.ToUint32())
|
||||
should.Equal(uint64(12345), any.ToUint64())
|
||||
should.Equal(float32(12345), any.ToFloat32())
|
||||
should.Equal(float64(12345), any.ToFloat64())
|
||||
should.Equal("12345", any.ToString())
|
||||
should.Equal(true, any.ToBool())
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("12345", string(stream.Buffer()))
|
||||
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
stream.WriteUint(uint(123))
|
||||
should.Equal("123", string(stream.Buffer()))
|
||||
}
|
||||
|
||||
func Test_int_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("1234"))
|
||||
// panic!!
|
||||
//should.Equal(any.LastError(), io.EOF)
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1, "2").ValueType())
|
||||
}
|
28
any_tests/jsoniter_any_map_test.go
Normal file
28
any_tests/jsoniter_any_map_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_wrap_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal("hello", any.Get("Field1").ToString())
|
||||
any = jsoniter.Wrap(map[string]string{"Field1": "hello"})
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_map_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap(map[string][]int{"Field1": {1, 2}})
|
||||
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
|
||||
// map write to
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0)
|
||||
any.WriteTo(stream)
|
||||
// TODO cannot pass
|
||||
//should.Equal(string(stream.buf), "")
|
||||
}
|
16
any_tests/jsoniter_any_null_test.go
Normal file
16
any_tests/jsoniter_any_null_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_read_null_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`null`))
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(float64(0), any.ToFloat64())
|
||||
should.Equal("", any.ToString())
|
||||
should.False(any.ToBool())
|
||||
}
|
121
any_tests/jsoniter_any_object_test.go
Normal file
121
any_tests/jsoniter_any_object_test.go
Normal file
@ -0,0 +1,121 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_read_object_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
should.Equal(`{"a":"stream","c":"d"}`, any.ToString())
|
||||
// partial parse
|
||||
should.Equal("stream", any.Get("a").ToString())
|
||||
should.Equal("d", any.Get("c").ToString())
|
||||
should.Equal(2, len(any.Keys()))
|
||||
any = jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
|
||||
// full parse
|
||||
should.Equal(2, len(any.Keys()))
|
||||
should.Equal(2, any.Size())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(jsoniter.ObjectValue, any.ValueType())
|
||||
should.Nil(any.LastError())
|
||||
obj := struct {
|
||||
A string
|
||||
}{}
|
||||
any.ToVal(&obj)
|
||||
should.Equal("stream", obj.A)
|
||||
}
|
||||
|
||||
func Test_object_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`{"a":{"stream":{"c":"d"}}}`))
|
||||
should.Equal("d", any.Get("a", "stream", "c").ToString())
|
||||
}
|
||||
|
||||
func Test_object_lazy_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`{"a":[0],"stream":[1]}`))
|
||||
should.Contains(any.Get('*', 0).ToString(), `"a":0`)
|
||||
}
|
||||
|
||||
func Test_object_lazy_any_get_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`{}`))
|
||||
should.Equal(jsoniter.InvalidValue, any.Get("a", "stream", "c").ValueType())
|
||||
should.Equal(jsoniter.InvalidValue, any.Get(1).ValueType())
|
||||
}
|
||||
|
||||
func Test_wrap_map_and_convert_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Wrap(map[string]interface{}{"a": 1})
|
||||
should.True(any.ToBool())
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(int32(0), any.ToInt32())
|
||||
should.Equal(int64(0), any.ToInt64())
|
||||
should.Equal(float32(0), any.ToFloat32())
|
||||
should.Equal(float64(0), any.ToFloat64())
|
||||
should.Equal(uint(0), any.ToUint())
|
||||
should.Equal(uint32(0), any.ToUint32())
|
||||
should.Equal(uint64(0), any.ToUint64())
|
||||
}
|
||||
|
||||
func Test_wrap_object_and_convert_to_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 string
|
||||
field2 string
|
||||
}
|
||||
any := jsoniter.Wrap(TestObject{"hello", "world"})
|
||||
should.Equal("hello", any.Get("Field1").ToString())
|
||||
any = jsoniter.Wrap(TestObject{"hello", "world"})
|
||||
should.Equal(2, any.Size())
|
||||
should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString())
|
||||
|
||||
should.Equal(0, any.ToInt())
|
||||
should.Equal(int32(0), any.ToInt32())
|
||||
should.Equal(int64(0), any.ToInt64())
|
||||
should.Equal(float32(0), any.ToFloat32())
|
||||
should.Equal(float64(0), any.ToFloat64())
|
||||
should.Equal(uint(0), any.ToUint())
|
||||
should.Equal(uint32(0), any.ToUint32())
|
||||
should.Equal(uint64(0), any.ToUint64())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(`{"Field1":"hello"}`, any.ToString())
|
||||
|
||||
// cannot pass!
|
||||
//stream := NewStream(ConfigDefault, nil, 32)
|
||||
//any.WriteTo(stream)
|
||||
//should.Equal(`{"Field1":"hello"}`, string(stream.Buffer()))
|
||||
// cannot pass!
|
||||
|
||||
}
|
||||
|
||||
func Test_any_within_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 jsoniter.Any
|
||||
Field2 jsoniter.Any
|
||||
}
|
||||
obj := TestObject{}
|
||||
err := jsoniter.UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", obj.Field1.ToString())
|
||||
should.Equal("[1,2,3]", obj.Field2.ToString())
|
||||
}
|
||||
|
||||
func Test_object_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field1 []int
|
||||
Field2 []int
|
||||
}
|
||||
any := jsoniter.Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
|
||||
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
|
||||
should.Contains(any.Keys(), "Field1")
|
||||
should.Contains(any.Keys(), "Field2")
|
||||
should.NotContains(any.Keys(), "Field3")
|
||||
}
|
58
any_tests/jsoniter_any_string_test.go
Normal file
58
any_tests/jsoniter_any_string_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var stringConvertMap = map[string]string{
|
||||
"null": "",
|
||||
"321.1": "321.1",
|
||||
`"1.1"`: "1.1",
|
||||
`"-123.1"`: "-123.1",
|
||||
"0.0": "0.0",
|
||||
"0": "0",
|
||||
`"0"`: "0",
|
||||
`"0.0"`: "0.0",
|
||||
`"00.0"`: "00.0",
|
||||
"true": "true",
|
||||
"false": "false",
|
||||
`"true"`: "true",
|
||||
`"false"`: "false",
|
||||
`"true123"`: "true123",
|
||||
`"+1"`: "+1",
|
||||
"[]": "[]",
|
||||
"[1,2]": "[1,2]",
|
||||
"{}": "{}",
|
||||
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
||||
}
|
||||
|
||||
func Test_read_any_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
for k, v := range stringConvertMap {
|
||||
any := jsoniter.Get([]byte(k))
|
||||
should.Equal(v, any.ToString(), "original val "+k)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_read_string_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte(`"hello"`))
|
||||
should.Equal("hello", any.ToString())
|
||||
should.True(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`" "`))
|
||||
should.False(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`"false"`))
|
||||
should.True(any.ToBool())
|
||||
any = jsoniter.Get([]byte(`"123"`))
|
||||
should.Equal(123, any.ToInt())
|
||||
}
|
||||
|
||||
func Test_wrap_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("-32000")).MustBeValid()
|
||||
should.Equal(-32000, any.ToInt())
|
||||
should.NoError(any.LastError())
|
||||
}
|
72
any_tests/jsoniter_must_be_valid_test.go
Normal file
72
any_tests/jsoniter_must_be_valid_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// if must be valid is useless, just drop this test
|
||||
func Test_must_be_valid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := jsoniter.Get([]byte("123"))
|
||||
should.Equal(any.MustBeValid().ToInt(), 123)
|
||||
|
||||
any = jsoniter.Wrap(int8(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(int16(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(int32(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(int64(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(uint(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(uint8(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(uint16(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(uint32(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(uint64(10))
|
||||
should.Equal(any.MustBeValid().ToInt(), 10)
|
||||
|
||||
any = jsoniter.Wrap(float32(10))
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
|
||||
|
||||
any = jsoniter.Wrap(float64(10))
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
|
||||
|
||||
any = jsoniter.Wrap(true)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
|
||||
|
||||
any = jsoniter.Wrap(false)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap(nil)
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap(struct{ age int }{age: 1})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap("abc")
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap([]int{})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
|
||||
|
||||
any = jsoniter.Wrap([]int{1, 2})
|
||||
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
|
||||
}
|
119
any_tests/jsoniter_wrap_test.go
Normal file
119
any_tests/jsoniter_wrap_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
package any_tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_wrap_and_valuetype_everything(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var i interface{}
|
||||
any := jsoniter.Get([]byte("123"))
|
||||
// default of number type is float64
|
||||
i = float64(123)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(int8(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// get interface is not int8 interface
|
||||
// i = int8(10)
|
||||
// should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(int16(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
//i = int16(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(int32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = int32(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(int64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = int64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(uint(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
//i = uint(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(uint8(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
// i = uint8(10)
|
||||
// should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(uint16(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
any = jsoniter.Wrap(uint32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = uint32(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(uint64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = uint64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(float32(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
// not equal
|
||||
//i = float32(10)
|
||||
//should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(float64(10))
|
||||
should.Equal(any.ValueType(), jsoniter.NumberValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = float64(10)
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(true)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = true
|
||||
should.Equal(i, any.GetInterface())
|
||||
any = jsoniter.Wrap(false)
|
||||
should.Equal(any.ValueType(), jsoniter.BoolValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = false
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(nil)
|
||||
should.Equal(any.ValueType(), jsoniter.NilValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = nil
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
|
||||
any.WriteTo(stream)
|
||||
should.Equal("null", string(stream.Buffer()))
|
||||
should.Equal(any.LastError(), nil)
|
||||
|
||||
any = jsoniter.Wrap(struct{ age int }{age: 1})
|
||||
should.Equal(any.ValueType(), jsoniter.ObjectValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = struct{ age int }{age: 1}
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
|
||||
should.Equal(any.ValueType(), jsoniter.ObjectValue)
|
||||
should.Equal(any.LastError(), nil)
|
||||
i = map[string]interface{}{"abc": 1}
|
||||
should.Equal(i, any.GetInterface())
|
||||
|
||||
any = jsoniter.Wrap("abc")
|
||||
i = "abc"
|
||||
should.Equal(i, any.GetInterface())
|
||||
should.Equal(nil, any.LastError())
|
||||
|
||||
}
|
@ -14,7 +14,11 @@ func (any *uint32Any) LastError() error {
|
||||
}
|
||||
|
||||
func (any *uint32Any) ValueType() ValueType {
|
||||
return Number
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *uint32Any) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *uint32Any) ToBool() bool {
|
74
any_uint64.go
Normal file
74
any_uint64.go
Normal file
@ -0,0 +1,74 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type uint64Any struct {
|
||||
baseAny
|
||||
val uint64
|
||||
}
|
||||
|
||||
func (any *uint64Any) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *uint64Any) ValueType() ValueType {
|
||||
return NumberValue
|
||||
}
|
||||
|
||||
func (any *uint64Any) MustBeValid() Any {
|
||||
return any
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToBool() bool {
|
||||
return any.val != 0
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt64() int64 {
|
||||
return int64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint() uint {
|
||||
return uint(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint32() uint32 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint64() uint64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToFloat64() float64 {
|
||||
return float64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToString() string {
|
||||
return strconv.FormatUint(any.val, 10)
|
||||
}
|
||||
|
||||
func (any *uint64Any) WriteTo(stream *Stream) {
|
||||
stream.WriteUint64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *uint64Any) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
229
api_tests/config_test.go
Normal file
229
api_tests/config_test.go
Normal file
@ -0,0 +1,229 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_use_number_for_unmarshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
api := jsoniter.Config{UseNumber: true}.Froze()
|
||||
var obj interface{}
|
||||
should.Nil(api.UnmarshalFromString("123", &obj))
|
||||
should.Equal(json.Number("123"), obj)
|
||||
}
|
||||
|
||||
func Test_customize_float_marshal(t *testing.T) {
|
||||
should := require.New(t)
|
||||
json := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze()
|
||||
str, err := json.MarshalToString(float32(1.23456789))
|
||||
should.Nil(err)
|
||||
should.Equal("1.234568", str)
|
||||
}
|
||||
|
||||
func Test_customize_tag_key(t *testing.T) {
|
||||
|
||||
type TestObject struct {
|
||||
Field string `orm:"field"`
|
||||
}
|
||||
|
||||
should := require.New(t)
|
||||
json := jsoniter.Config{TagKey: "orm"}.Froze()
|
||||
str, err := json.MarshalToString(TestObject{"hello"})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_read_large_number_as_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val interface{}
|
||||
err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
|
||||
should.Nil(err)
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`123456789123456789123456789`, output)
|
||||
}
|
||||
|
||||
type caseSensitiveStruct struct {
|
||||
A string `json:"a"`
|
||||
B string `json:"b,omitempty"`
|
||||
C *C `json:"C,omitempty"`
|
||||
}
|
||||
|
||||
type C struct {
|
||||
D int64 `json:"D,omitempty"`
|
||||
E *E `json:"e,omitempty"`
|
||||
}
|
||||
|
||||
type E struct {
|
||||
F string `json:"F,omitempty"`
|
||||
}
|
||||
|
||||
func Test_CaseSensitive(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
caseSensitive bool
|
||||
}{
|
||||
{
|
||||
input: `{"A":"foo","B":"bar"}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","b":"bar"}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||
expectedOutput: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","B":"bar","c":{"d":10}}`,
|
||||
expectedOutput: `{"a":"foo"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"d":10}}`,
|
||||
expectedOutput: `{"a":"foo","C":{}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"D":10,"e":{"f":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{}}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"A":"foo","c":{"d":10,"E":{"f":"baz"}}}`,
|
||||
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
val := caseSensitiveStruct{}
|
||||
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||
should.Nil(err)
|
||||
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(tc.expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
type structWithElevenFields struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B string `json:"B,omitempty"`
|
||||
C string `json:"C,omitempty"`
|
||||
D string `json:"d,omitempty"`
|
||||
E string `json:"e,omitempty"`
|
||||
F string `json:"f,omitempty"`
|
||||
G string `json:"g,omitempty"`
|
||||
H string `json:"h,omitempty"`
|
||||
I string `json:"i,omitempty"`
|
||||
J string `json:"j,omitempty"`
|
||||
K string `json:"k,omitempty"`
|
||||
}
|
||||
|
||||
func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
caseSensitive bool
|
||||
}{
|
||||
{
|
||||
input: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
{
|
||||
input: `{"a":"1","b":"2","c":"3","D":"4","E":"5","F":"6"}`,
|
||||
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6"}`,
|
||||
caseSensitive: false,
|
||||
},
|
||||
{
|
||||
input: `{"A":"1","b":"2","d":"4","E":"5"}`,
|
||||
expectedOutput: `{"A":"1","d":"4"}`,
|
||||
caseSensitive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
val := structWithElevenFields{}
|
||||
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||
should.Nil(err)
|
||||
|
||||
output, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(tc.expectedOutput, output)
|
||||
}
|
||||
}
|
||||
|
||||
type onlyTaggedFieldStruct struct {
|
||||
A string `json:"a"`
|
||||
B string
|
||||
FSimpl F `json:"f_simpl"`
|
||||
ISimpl I
|
||||
FPtr *F `json:"f_ptr"`
|
||||
IPtr *I
|
||||
F
|
||||
*I
|
||||
}
|
||||
|
||||
type F struct {
|
||||
G string `json:"g"`
|
||||
H string
|
||||
}
|
||||
|
||||
type I struct {
|
||||
J string `json:"j"`
|
||||
K string
|
||||
}
|
||||
|
||||
func Test_OnlyTaggedField(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
obj := onlyTaggedFieldStruct{
|
||||
A: "a",
|
||||
B: "b",
|
||||
FSimpl: F{G: "g", H: "h"},
|
||||
ISimpl: I{J: "j", K: "k"},
|
||||
FPtr: &F{G: "g", H: "h"},
|
||||
IPtr: &I{J: "j", K: "k"},
|
||||
F: F{G: "g", H: "h"},
|
||||
I: &I{J: "j", K: "k"},
|
||||
}
|
||||
|
||||
output, err := jsoniter.Config{OnlyTaggedField: true}.Froze().Marshal(obj)
|
||||
should.Nil(err)
|
||||
|
||||
m := make(map[string]interface{})
|
||||
err = jsoniter.Unmarshal(output, &m)
|
||||
should.Nil(err)
|
||||
|
||||
should.Equal(map[string]interface{}{
|
||||
"a": "a",
|
||||
"f_simpl": map[string]interface{}{
|
||||
"g": "g",
|
||||
},
|
||||
"f_ptr": map[string]interface{}{
|
||||
"g": "g",
|
||||
},
|
||||
"g": "g",
|
||||
"j": "j",
|
||||
}, m)
|
||||
}
|
64
api_tests/decoder_test.go
Normal file
64
api_tests/decoder_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_disallowUnknownFields(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct{}
|
||||
var obj TestObject
|
||||
decoder := jsoniter.NewDecoder(bytes.NewBufferString(`{"field1":100}`))
|
||||
decoder.DisallowUnknownFields()
|
||||
should.Error(decoder.Decode(&obj))
|
||||
}
|
||||
|
||||
func Test_new_decoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
|
||||
decoder2 := 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)
|
||||
}
|
||||
|
||||
func Test_decoder_more(t *testing.T) {
|
||||
should := require.New(t)
|
||||
decoder := jsoniter.NewDecoder(bytes.NewBufferString("abcde"))
|
||||
should.True(decoder.More())
|
||||
}
|
46
api_tests/encoder_18_test.go
Normal file
46
api_tests/encoder_18_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
//+build go1.8
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_new_encoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf1 := &bytes.Buffer{}
|
||||
encoder1 := json.NewEncoder(buf1)
|
||||
encoder1.SetEscapeHTML(false)
|
||||
encoder1.Encode([]int{1})
|
||||
should.Equal("[1]\n", buf1.String())
|
||||
buf2 := &bytes.Buffer{}
|
||||
encoder2 := jsoniter.NewEncoder(buf2)
|
||||
encoder2.SetEscapeHTML(false)
|
||||
encoder2.Encode([]int{1})
|
||||
should.Equal("[1]\n", buf2.String())
|
||||
}
|
||||
|
||||
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
||||
api := jsoniter.Config{EscapeHTML: false}.Froze()
|
||||
should := require.New(t)
|
||||
for i := 0; i < utf8.RuneSelf; i++ {
|
||||
input := string([]byte{byte(i)})
|
||||
buf := &bytes.Buffer{}
|
||||
encoder := json.NewEncoder(buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
err := encoder.Encode(input)
|
||||
should.Nil(err)
|
||||
stdOutput := buf.String()
|
||||
stdOutput = stdOutput[:len(stdOutput)-1]
|
||||
jsoniterOutputBytes, err := api.Marshal(input)
|
||||
should.Nil(err)
|
||||
jsoniterOutput := string(jsoniterOutputBytes)
|
||||
should.Equal(stdOutput, jsoniterOutput)
|
||||
}
|
||||
}
|
20
api_tests/encoder_test.go
Normal file
20
api_tests/encoder_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Standard Encoder has trailing newline.
|
||||
func TestEncoderHasTrailingNewline(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var buf, stdbuf bytes.Buffer
|
||||
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||
enc.Encode(1)
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.Encode(1)
|
||||
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||
}
|
36
api_tests/marshal_indent_test.go
Normal file
36
api_tests/marshal_indent_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_marshal_indent(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := struct {
|
||||
F1 int
|
||||
F2 []int
|
||||
}{1, []int{2, 3, 4}}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
output, err = jsoniter.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
|
||||
}
|
||||
|
||||
func Test_marshal_indent_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
obj := map[int]int{1: 2}
|
||||
output, err := json.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = jsoniter.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
output, err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
|
||||
should.Nil(err)
|
||||
should.Equal("{\n \"1\": 2\n}", string(output))
|
||||
}
|
47
api_tests/marshal_json_escape_test.go
Normal file
47
api_tests/marshal_json_escape_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var marshalConfig = jsoniter.Config{
|
||||
EscapeHTML: false,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
type Container struct {
|
||||
Bar interface{}
|
||||
}
|
||||
|
||||
func (c *Container) MarshalJSON() ([]byte, error) {
|
||||
return marshalConfig.Marshal(&c.Bar)
|
||||
}
|
||||
|
||||
func TestEncodeEscape(t *testing.T) {
|
||||
should := require.New(t)
|
||||
|
||||
container := &Container{
|
||||
Bar: []string{"123<ab>", "ooo"},
|
||||
}
|
||||
out, err := marshalConfig.Marshal(container)
|
||||
should.Nil(err)
|
||||
bufout := string(out)
|
||||
|
||||
var stdbuf bytes.Buffer
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.SetEscapeHTML(false)
|
||||
err = stdenc.Encode(container)
|
||||
should.Nil(err)
|
||||
stdout := string(stdbuf.Bytes())
|
||||
if stdout[len(stdout)-1:] == "\n" {
|
||||
stdout = stdout[:len(stdout)-1]
|
||||
}
|
||||
|
||||
should.Equal(stdout, bufout)
|
||||
}
|
36
api_tests/marshal_json_test.go
Normal file
36
api_tests/marshal_json_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
type Foo struct {
|
||||
Bar interface{}
|
||||
}
|
||||
|
||||
func (f Foo) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(f.Bar)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
|
||||
// Standard Encoder has trailing newline.
|
||||
func TestEncodeMarshalJSON(t *testing.T) {
|
||||
|
||||
foo := Foo {
|
||||
Bar: 123,
|
||||
}
|
||||
should := require.New(t)
|
||||
var buf, stdbuf bytes.Buffer
|
||||
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||
enc.Encode(foo)
|
||||
stdenc := json.NewEncoder(&stdbuf)
|
||||
stdenc.Encode(foo)
|
||||
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||
}
|
1017
assert/assertions.go
1017
assert/assertions.go
File diff suppressed because it is too large
Load Diff
25
benchmarks/encode_string_test.go
Normal file
25
benchmarks/encode_string_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/json-iterator/go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_encode_string_with_SetEscapeHTML(b *testing.B) {
|
||||
type V struct {
|
||||
S string
|
||||
B bool
|
||||
I int
|
||||
}
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(true)
|
||||
if err := enc.Encode(V{S: "s", B: true, I: 233}); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
158
benchmarks/jsoniter_large_file_test.go
Normal file
158
benchmarks/jsoniter_large_file_test.go
Normal file
@ -0,0 +1,158 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/json-iterator/go"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func Test_large_file(t *testing.T) {
|
||||
// file, err := os.Open("/tmp/large-file.json")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// iter := Parse(file, 4096)
|
||||
// count := 0
|
||||
// for iter.ReadArray() {
|
||||
// iter.Skip()
|
||||
// count++
|
||||
// }
|
||||
// if count != 11351 {
|
||||
// t.Fatal(count)
|
||||
// }
|
||||
//}
|
||||
|
||||
func init() {
|
||||
ioutil.WriteFile("/tmp/large-file.json", []byte(`[{
|
||||
"person": {
|
||||
"id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
|
||||
"name": {
|
||||
"fullName": "Leonid Bugaev",
|
||||
"givenName": "Leonid",
|
||||
"familyName": "Bugaev"
|
||||
},
|
||||
"email": "leonsbox@gmail.com",
|
||||
"gender": "male",
|
||||
"location": "Saint Petersburg, Saint Petersburg, RU",
|
||||
"geo": {
|
||||
"city": "Saint Petersburg",
|
||||
"state": "Saint Petersburg",
|
||||
"country": "Russia",
|
||||
"lat": 59.9342802,
|
||||
"lng": 30.3350986
|
||||
},
|
||||
"bio": "Senior engineer at Granify.com",
|
||||
"site": "http://flickfaver.com",
|
||||
"avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
|
||||
"employment": {
|
||||
"name": "www.latera.ru",
|
||||
"title": "Software Engineer",
|
||||
"domain": "gmail.com"
|
||||
},
|
||||
"facebook": {
|
||||
"handle": "leonid.bugaev"
|
||||
},
|
||||
"github": {
|
||||
"handle": "buger",
|
||||
"id": 14009,
|
||||
"avatar": "https://avatars.githubusercontent.com/u/14009?v=3",
|
||||
"company": "Granify",
|
||||
"blog": "http://leonsbox.com",
|
||||
"followers": 95,
|
||||
"following": 10
|
||||
},
|
||||
"twitter": {
|
||||
"handle": "flickfaver",
|
||||
"id": 77004410,
|
||||
"bio": null,
|
||||
"followers": 2,
|
||||
"following": 1,
|
||||
"statuses": 5,
|
||||
"favorites": 0,
|
||||
"location": "",
|
||||
"site": "http://flickfaver.com",
|
||||
"avatar": null
|
||||
},
|
||||
"linkedin": {
|
||||
"handle": "in/leonidbugaev"
|
||||
},
|
||||
"googleplus": {
|
||||
"handle": null
|
||||
},
|
||||
"angellist": {
|
||||
"handle": "leonid-bugaev",
|
||||
"id": 61541,
|
||||
"bio": "Senior engineer at Granify.com",
|
||||
"blog": "http://buger.github.com",
|
||||
"site": "http://buger.github.com",
|
||||
"followers": 41,
|
||||
"avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390"
|
||||
},
|
||||
"klout": {
|
||||
"handle": null,
|
||||
"score": null
|
||||
},
|
||||
"foursquare": {
|
||||
"handle": null
|
||||
},
|
||||
"aboutme": {
|
||||
"handle": "leonid.bugaev",
|
||||
"bio": null,
|
||||
"avatar": null
|
||||
},
|
||||
"gravatar": {
|
||||
"handle": "buger",
|
||||
"urls": [
|
||||
],
|
||||
"avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
|
||||
"avatars": [
|
||||
{
|
||||
"url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
|
||||
"type": "thumbnail"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fuzzy": false
|
||||
},
|
||||
"company": "hello"
|
||||
}]`), 0666)
|
||||
}
|
||||
|
||||
/*
|
||||
200000 8886 ns/op 4336 B/op 6 allocs/op
|
||||
50000 34244 ns/op 6744 B/op 14 allocs/op
|
||||
*/
|
||||
func Benchmark_jsoniter_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
iter := jsoniter.Parse(jsoniter.ConfigDefault, file, 4096)
|
||||
count := 0
|
||||
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
|
||||
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
|
||||
iter.Skip()
|
||||
count++
|
||||
return true
|
||||
})
|
||||
file.Close()
|
||||
if iter.Error != nil {
|
||||
b.Error(iter.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
file.Close()
|
||||
result := []struct{}{}
|
||||
err := json.Unmarshal(bytes, &result)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
128
benchmarks/stream_test.go
Normal file
128
benchmarks/stream_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func Benchmark_stream_encode_big_object(b *testing.B) {
|
||||
var buf bytes.Buffer
|
||||
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 100)
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
stream.Reset(&buf)
|
||||
encodeObject(stream)
|
||||
if stream.Error != nil {
|
||||
b.Errorf("error: %+v", stream.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeObject(t *testing.T) {
|
||||
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 100)
|
||||
encodeObject(stream)
|
||||
if stream.Error != nil {
|
||||
t.Errorf("error encoding a test object: %+v", stream.Error)
|
||||
return
|
||||
}
|
||||
var m = make(map[string]interface{})
|
||||
if err := jsoniter.Unmarshal(stream.Buffer(), &m); err != nil {
|
||||
t.Errorf("error unmarshaling a test object: %+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func encodeObject(stream *jsoniter.Stream) {
|
||||
stream.WriteObjectStart()
|
||||
|
||||
stream.WriteObjectField("objectId")
|
||||
stream.WriteUint64(8838243212)
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("name")
|
||||
stream.WriteString("Jane Doe")
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("address")
|
||||
stream.WriteObjectStart()
|
||||
for i, field := range addressFields {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(field.key)
|
||||
stream.WriteString(field.val)
|
||||
}
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("geo")
|
||||
{
|
||||
stream.WriteObjectStart()
|
||||
stream.WriteObjectField("latitude")
|
||||
stream.WriteFloat64(-154.550817)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("longitude")
|
||||
stream.WriteFloat64(-84.176159)
|
||||
stream.WriteObjectEnd()
|
||||
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("specialties")
|
||||
stream.WriteArrayStart()
|
||||
for i, s := range specialties {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteString(s)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
|
||||
stream.WriteMore()
|
||||
for i, text := range longText {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField("longText" + strconv.Itoa(i))
|
||||
stream.WriteString(text)
|
||||
}
|
||||
|
||||
for i := 0; i < 25; i++ {
|
||||
num := i * 18328
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("integerField" + strconv.Itoa(i))
|
||||
stream.WriteInt64(int64(num))
|
||||
}
|
||||
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
type field struct{ key, val string }
|
||||
|
||||
var (
|
||||
addressFields = []field{
|
||||
{"address1", "123 Example St"},
|
||||
{"address2", "Apartment 5D, Suite 3"},
|
||||
{"city", "Miami"},
|
||||
{"state", "FL"},
|
||||
{"postalCode", "33133"},
|
||||
{"country", "US"},
|
||||
}
|
||||
specialties = []string{
|
||||
"Web Design",
|
||||
"Go Programming",
|
||||
"Tennis",
|
||||
"Cycling",
|
||||
"Mixed martial arts",
|
||||
}
|
||||
longText = []string{
|
||||
`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
|
||||
`Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`,
|
||||
`But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?`,
|
||||
`At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.`,
|
||||
`On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.`,
|
||||
}
|
||||
)
|
12
build.sh
Executable file
12
build.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then
|
||||
mkdir -p /tmp/build-golang/src/github.com/json-iterator
|
||||
ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go
|
||||
fi
|
||||
export GOPATH=/tmp/build-golang
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
cd /tmp/build-golang/src/github.com/json-iterator/go
|
||||
exec $GOPATH/bin/dep ensure -update
|
375
config.go
Normal file
375
config.go
Normal file
@ -0,0 +1,375 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/modern-go/concurrent"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
// Config customize how the API should behave.
|
||||
// The API is created from Config by Froze.
|
||||
type Config struct {
|
||||
IndentionStep int
|
||||
MarshalFloatWith6Digits bool
|
||||
EscapeHTML bool
|
||||
SortMapKeys bool
|
||||
UseNumber bool
|
||||
DisallowUnknownFields bool
|
||||
TagKey string
|
||||
OnlyTaggedField bool
|
||||
ValidateJsonRawMessage bool
|
||||
ObjectFieldMustBeSimpleString bool
|
||||
CaseSensitive bool
|
||||
}
|
||||
|
||||
// API the public interface of this package.
|
||||
// Primary Marshal and Unmarshal.
|
||||
type API interface {
|
||||
IteratorPool
|
||||
StreamPool
|
||||
MarshalToString(v interface{}) (string, error)
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||
UnmarshalFromString(str string, v interface{}) error
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
Get(data []byte, path ...interface{}) Any
|
||||
NewEncoder(writer io.Writer) *Encoder
|
||||
NewDecoder(reader io.Reader) *Decoder
|
||||
Valid(data []byte) bool
|
||||
RegisterExtension(extension Extension)
|
||||
DecoderOf(typ reflect2.Type) ValDecoder
|
||||
EncoderOf(typ reflect2.Type) ValEncoder
|
||||
}
|
||||
|
||||
// ConfigDefault the default API
|
||||
var ConfigDefault = Config{
|
||||
EscapeHTML: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
||||
var ConfigCompatibleWithStandardLibrary = Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigFastest marshals float with only 6 digits precision
|
||||
var ConfigFastest = Config{
|
||||
EscapeHTML: false,
|
||||
MarshalFloatWith6Digits: true, // will lose precession
|
||||
ObjectFieldMustBeSimpleString: true, // do not unescape object field
|
||||
}.Froze()
|
||||
|
||||
type frozenConfig struct {
|
||||
configBeforeFrozen Config
|
||||
sortMapKeys bool
|
||||
indentionStep int
|
||||
objectFieldMustBeSimpleString bool
|
||||
onlyTaggedField bool
|
||||
disallowUnknownFields bool
|
||||
decoderCache *concurrent.Map
|
||||
encoderCache *concurrent.Map
|
||||
encoderExtension Extension
|
||||
decoderExtension Extension
|
||||
extraExtensions []Extension
|
||||
streamPool *sync.Pool
|
||||
iteratorPool *sync.Pool
|
||||
caseSensitive bool
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) initCache() {
|
||||
cfg.decoderCache = concurrent.NewMap()
|
||||
cfg.encoderCache = concurrent.NewMap()
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
|
||||
cfg.decoderCache.Store(cacheKey, decoder)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
|
||||
cfg.encoderCache.Store(cacheKey, encoder)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
|
||||
decoder, found := cfg.decoderCache.Load(cacheKey)
|
||||
if found {
|
||||
return decoder.(ValDecoder)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
|
||||
encoder, found := cfg.encoderCache.Load(cacheKey)
|
||||
if found {
|
||||
return encoder.(ValEncoder)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var cfgCache = concurrent.NewMap()
|
||||
|
||||
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
|
||||
obj, found := cfgCache.Load(cfg)
|
||||
if found {
|
||||
return obj.(*frozenConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
|
||||
cfgCache.Store(cfg, frozenConfig)
|
||||
}
|
||||
|
||||
// Froze forge API from config
|
||||
func (cfg Config) Froze() API {
|
||||
api := &frozenConfig{
|
||||
sortMapKeys: cfg.SortMapKeys,
|
||||
indentionStep: cfg.IndentionStep,
|
||||
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
||||
onlyTaggedField: cfg.OnlyTaggedField,
|
||||
disallowUnknownFields: cfg.DisallowUnknownFields,
|
||||
caseSensitive: cfg.CaseSensitive,
|
||||
}
|
||||
api.streamPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return NewStream(api, nil, 512)
|
||||
},
|
||||
}
|
||||
api.iteratorPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return NewIterator(api)
|
||||
},
|
||||
}
|
||||
api.initCache()
|
||||
encoderExtension := EncoderExtension{}
|
||||
decoderExtension := DecoderExtension{}
|
||||
if cfg.MarshalFloatWith6Digits {
|
||||
api.marshalFloatWith6Digits(encoderExtension)
|
||||
}
|
||||
if cfg.EscapeHTML {
|
||||
api.escapeHTML(encoderExtension)
|
||||
}
|
||||
if cfg.UseNumber {
|
||||
api.useNumber(decoderExtension)
|
||||
}
|
||||
if cfg.ValidateJsonRawMessage {
|
||||
api.validateJsonRawMessage(encoderExtension)
|
||||
}
|
||||
api.encoderExtension = encoderExtension
|
||||
api.decoderExtension = decoderExtension
|
||||
api.configBeforeFrozen = cfg
|
||||
return api
|
||||
}
|
||||
|
||||
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
|
||||
api := getFrozenConfigFromCache(cfg)
|
||||
if api != nil {
|
||||
return api
|
||||
}
|
||||
api = cfg.Froze().(*frozenConfig)
|
||||
for _, extension := range extraExtensions {
|
||||
api.RegisterExtension(extension)
|
||||
}
|
||||
addFrozenConfigToCache(cfg, api)
|
||||
return api
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
|
||||
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
|
||||
rawMessage := *(*json.RawMessage)(ptr)
|
||||
iter := cfg.BorrowIterator([]byte(rawMessage))
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.Read()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
stream.WriteRaw("null")
|
||||
} else {
|
||||
stream.WriteRaw(string(rawMessage))
|
||||
}
|
||||
}, func(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.RawMessage)(ptr))) == 0
|
||||
}}
|
||||
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
||||
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
|
||||
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
exitingValue := *((*interface{})(ptr))
|
||||
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
|
||||
iter.ReadVal(exitingValue)
|
||||
return
|
||||
}
|
||||
if iter.WhatIsNext() == NumberValue {
|
||||
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
|
||||
} else {
|
||||
*((*interface{})(ptr)) = iter.Read()
|
||||
}
|
||||
}}
|
||||
}
|
||||
func (cfg *frozenConfig) getTagKey() string {
|
||||
tagKey := cfg.configBeforeFrozen.TagKey
|
||||
if tagKey == "" {
|
||||
return "json"
|
||||
}
|
||||
return tagKey
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
|
||||
cfg.extraExtensions = append(cfg.extraExtensions, extension)
|
||||
copied := cfg.configBeforeFrozen
|
||||
cfg.configBeforeFrozen = copied
|
||||
}
|
||||
|
||||
type lossyFloat32Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat32Lossy(*((*float32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type lossyFloat64Encoder struct {
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat64Lossy(*((*float64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float64)(ptr)) == 0
|
||||
}
|
||||
|
||||
// EnableLossyFloatMarshalling keeps 10**(-6) precision
|
||||
// for float variables for better performance.
|
||||
func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
|
||||
// for better performance
|
||||
extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{}
|
||||
extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{}
|
||||
}
|
||||
|
||||
type htmlEscapedStringEncoder struct {
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
stream.WriteStringWithHTMLEscaped(str)
|
||||
}
|
||||
|
||||
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*string)(ptr)) == ""
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
|
||||
encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanDecoders() {
|
||||
typeDecoders = map[string]ValDecoder{}
|
||||
fieldDecoders = map[string]ValDecoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) cleanEncoders() {
|
||||
typeEncoders = map[string]ValEncoder{}
|
||||
fieldEncoders = map[string]ValEncoder{}
|
||||
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return "", stream.Error
|
||||
}
|
||||
return string(stream.Buffer()), nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
|
||||
stream := cfg.BorrowStream(nil)
|
||||
defer cfg.ReturnStream(stream)
|
||||
stream.WriteVal(v)
|
||||
if stream.Error != nil {
|
||||
return nil, stream.Error
|
||||
}
|
||||
result := stream.Buffer()
|
||||
copied := make([]byte, len(result))
|
||||
copy(copied, result)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
if prefix != "" {
|
||||
panic("prefix is not supported")
|
||||
}
|
||||
for _, r := range indent {
|
||||
if r != ' ' {
|
||||
panic("indent can only be space")
|
||||
}
|
||||
}
|
||||
newCfg := cfg.configBeforeFrozen
|
||||
newCfg.IndentionStep = len(indent)
|
||||
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
||||
data := []byte(str)
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(v)
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
return locatePath(iter, path)
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.ReadVal(v)
|
||||
c := iter.nextToken()
|
||||
if c == 0 {
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
|
||||
stream := NewStream(cfg, writer, 512)
|
||||
return &Encoder{stream}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
|
||||
iter := Parse(cfg, reader, 512)
|
||||
return &Decoder{iter}
|
||||
}
|
||||
|
||||
func (cfg *frozenConfig) Valid(data []byte) bool {
|
||||
iter := cfg.BorrowIterator(data)
|
||||
defer cfg.ReturnIterator(iter)
|
||||
iter.Skip()
|
||||
return iter.Error == nil
|
||||
}
|
121
example_test.go
Normal file
121
example_test.go
Normal file
@ -0,0 +1,121 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExampleMarshal() {
|
||||
type ColorGroup struct {
|
||||
ID int
|
||||
Name string
|
||||
Colors []string
|
||||
}
|
||||
group := ColorGroup{
|
||||
ID: 1,
|
||||
Name: "Reds",
|
||||
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
|
||||
}
|
||||
b, err := Marshal(group)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
// Output:
|
||||
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
|
||||
}
|
||||
|
||||
func ExampleUnmarshal() {
|
||||
var jsonBlob = []byte(`[
|
||||
{"Name": "Platypus", "Order": "Monotremata"},
|
||||
{"Name": "Quoll", "Order": "Dasyuromorphia"}
|
||||
]`)
|
||||
type Animal struct {
|
||||
Name string
|
||||
Order string
|
||||
}
|
||||
var animals []Animal
|
||||
err := Unmarshal(jsonBlob, &animals)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
fmt.Printf("%+v", animals)
|
||||
// Output:
|
||||
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
|
||||
}
|
||||
|
||||
func ExampleConfigFastest_Marshal() {
|
||||
type ColorGroup struct {
|
||||
ID int
|
||||
Name string
|
||||
Colors []string
|
||||
}
|
||||
group := ColorGroup{
|
||||
ID: 1,
|
||||
Name: "Reds",
|
||||
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
|
||||
}
|
||||
stream := ConfigFastest.BorrowStream(nil)
|
||||
defer ConfigFastest.ReturnStream(stream)
|
||||
stream.WriteVal(group)
|
||||
if stream.Error != nil {
|
||||
fmt.Println("error:", stream.Error)
|
||||
}
|
||||
os.Stdout.Write(stream.Buffer())
|
||||
// Output:
|
||||
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
|
||||
}
|
||||
|
||||
func ExampleConfigFastest_Unmarshal() {
|
||||
var jsonBlob = []byte(`[
|
||||
{"Name": "Platypus", "Order": "Monotremata"},
|
||||
{"Name": "Quoll", "Order": "Dasyuromorphia"}
|
||||
]`)
|
||||
type Animal struct {
|
||||
Name string
|
||||
Order string
|
||||
}
|
||||
var animals []Animal
|
||||
iter := ConfigFastest.BorrowIterator(jsonBlob)
|
||||
defer ConfigFastest.ReturnIterator(iter)
|
||||
iter.ReadVal(&animals)
|
||||
if iter.Error != nil {
|
||||
fmt.Println("error:", iter.Error)
|
||||
}
|
||||
fmt.Printf("%+v", animals)
|
||||
// Output:
|
||||
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
|
||||
}
|
||||
|
||||
func ExampleGet() {
|
||||
val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`)
|
||||
fmt.Printf(Get(val, "Colors", 0).ToString())
|
||||
// Output:
|
||||
// Crimson
|
||||
}
|
||||
|
||||
func ExampleMyKey() {
|
||||
hello := MyKey("hello")
|
||||
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
|
||||
fmt.Println(string(output))
|
||||
obj := map[*MyKey]string{}
|
||||
Unmarshal(output, &obj)
|
||||
for k, v := range obj {
|
||||
fmt.Println(*k, v)
|
||||
}
|
||||
// Output:
|
||||
// {"Hello":"world"}
|
||||
// Hel world
|
||||
}
|
||||
|
||||
type MyKey string
|
||||
|
||||
func (m *MyKey) MarshalText() ([]byte, error) {
|
||||
return []byte(strings.Replace(string(*m), "h", "H", -1)), nil
|
||||
}
|
||||
|
||||
func (m *MyKey) UnmarshalText(text []byte) error {
|
||||
*m = MyKey(text[:3])
|
||||
return nil
|
||||
}
|
223
extension_tests/decoder_test.go
Normal file
223
extension_tests/decoder_test.go
Normal file
@ -0,0 +1,223 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Test_customize_type_decoder(t *testing.T) {
|
||||
t.Skip()
|
||||
jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
*((*time.Time)(ptr)) = t
|
||||
})
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
val := time.Time{}
|
||||
err := jsoniter.Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
year, month, day := val.Date()
|
||||
if year != 2016 || month != 12 || day != 5 {
|
||||
t.Fatal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_customize_byte_array_encoder(t *testing.T) {
|
||||
t.Skip()
|
||||
//jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
should := require.New(t)
|
||||
jsoniter.RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
t := *((*[]byte)(ptr))
|
||||
stream.WriteString(string(t))
|
||||
}, nil)
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||
val := []byte("abc")
|
||||
str, err := jsoniter.MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal(`"abc"`, str)
|
||||
}
|
||||
|
||||
type CustomEncoderAttachmentTestStruct struct {
|
||||
Value int32 `json:"value"`
|
||||
}
|
||||
|
||||
type CustomEncoderAttachmentTestStructEncoder struct {}
|
||||
|
||||
func (c *CustomEncoderAttachmentTestStructEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
attachVal, ok := stream.Attachment.(int)
|
||||
stream.WriteRaw(`"`)
|
||||
stream.WriteRaw(fmt.Sprintf("%t %d", ok, attachVal))
|
||||
stream.WriteRaw(`"`)
|
||||
}
|
||||
|
||||
func (c *CustomEncoderAttachmentTestStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func Test_custom_encoder_attachment(t *testing.T) {
|
||||
|
||||
jsoniter.RegisterTypeEncoder("test.CustomEncoderAttachmentTestStruct", &CustomEncoderAttachmentTestStructEncoder{})
|
||||
expectedValue := 17
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := jsoniter.NewStream(jsoniter.Config{SortMapKeys: true}.Froze(), buf, 4096)
|
||||
stream.Attachment = expectedValue
|
||||
val := map[string]CustomEncoderAttachmentTestStruct{"a": {}}
|
||||
stream.WriteVal(val)
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("{\"a\":\"true 17\"}", buf.String())
|
||||
}
|
||||
|
||||
func Test_customize_field_decoder(t *testing.T) {
|
||||
type Tom struct {
|
||||
field1 string
|
||||
}
|
||||
jsoniter.RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
})
|
||||
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||
tom := Tom{}
|
||||
err := jsoniter.Unmarshal([]byte(`{"field1": 100}`), &tom)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_recursive_empty_interface_customization(t *testing.T) {
|
||||
t.Skip()
|
||||
var obj interface{}
|
||||
jsoniter.RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
switch iter.WhatIsNext() {
|
||||
case jsoniter.NumberValue:
|
||||
*(*interface{})(ptr) = iter.ReadInt64()
|
||||
default:
|
||||
*(*interface{})(ptr) = iter.Read()
|
||||
}
|
||||
})
|
||||
should := require.New(t)
|
||||
jsoniter.Unmarshal([]byte("[100]"), &obj)
|
||||
should.Equal([]interface{}{int64(100)}, obj)
|
||||
}
|
||||
|
||||
type MyInterface interface {
|
||||
Hello() string
|
||||
}
|
||||
|
||||
type MyString string
|
||||
|
||||
func (ms MyString) Hello() string {
|
||||
return string(ms)
|
||||
}
|
||||
|
||||
func Test_read_custom_interface(t *testing.T) {
|
||||
t.Skip()
|
||||
should := require.New(t)
|
||||
var val MyInterface
|
||||
jsoniter.RegisterTypeDecoderFunc("jsoniter.MyInterface", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*MyInterface)(ptr)) = MyString(iter.ReadString())
|
||||
})
|
||||
err := jsoniter.UnmarshalFromString(`"hello"`, &val)
|
||||
should.Nil(err)
|
||||
should.Equal("hello", val.Hello())
|
||||
}
|
||||
|
||||
const flow1 = `
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}`
|
||||
|
||||
const flow2 = `
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
{"A":"hello"}
|
||||
`
|
||||
|
||||
type (
|
||||
Type1 struct {
|
||||
A string
|
||||
}
|
||||
|
||||
Type2 struct {
|
||||
A string
|
||||
}
|
||||
)
|
||||
|
||||
func (t *Type2) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Type2) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestType1NoFinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow1))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type1{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType1FinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow2))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type1{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType2NoFinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow1))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type2{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestType2FinalLF(t *testing.T) {
|
||||
reader := bytes.NewReader([]byte(flow2))
|
||||
dec := jsoniter.NewDecoder(reader)
|
||||
|
||||
i := 0
|
||||
for dec.More() {
|
||||
data := &Type2{}
|
||||
if err := dec.Decode(data); err != nil {
|
||||
t.Errorf("at %v got %v", i, err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
120
extension_tests/extension_test.go
Normal file
120
extension_tests/extension_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/stretchr/testify/require"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type TestObject1 struct {
|
||||
Field1 string
|
||||
}
|
||||
|
||||
type testExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
if structDescriptor.Type.String() != "test.TestObject1" {
|
||||
return
|
||||
}
|
||||
binding := structDescriptor.GetField("Field1")
|
||||
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
str := *((*string)(ptr))
|
||||
val, _ := strconv.Atoi(str)
|
||||
stream.WriteInt(val)
|
||||
}}
|
||||
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
}}
|
||||
binding.ToNames = []string{"field-1"}
|
||||
binding.FromNames = []string{"field-1"}
|
||||
}
|
||||
|
||||
func Test_customize_field_by_extension(t *testing.T) {
|
||||
should := require.New(t)
|
||||
cfg := jsoniter.Config{}.Froze()
|
||||
cfg.RegisterExtension(&testExtension{})
|
||||
obj := TestObject1{}
|
||||
err := cfg.UnmarshalFromString(`{"field-1": 100}`, &obj)
|
||||
should.Nil(err)
|
||||
should.Equal("100", obj.Field1)
|
||||
str, err := cfg.MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"field-1":100}`, str)
|
||||
}
|
||||
|
||||
func Test_customize_map_key_encoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
cfg := jsoniter.Config{}.Froze()
|
||||
cfg.RegisterExtension(&testMapKeyExtension{})
|
||||
m := map[int]int{1: 2}
|
||||
output, err := cfg.MarshalToString(m)
|
||||
should.NoError(err)
|
||||
should.Equal(`{"2":2}`, output)
|
||||
m = map[int]int{}
|
||||
should.NoError(cfg.UnmarshalFromString(output, &m))
|
||||
should.Equal(map[int]int{1: 2}, m)
|
||||
}
|
||||
|
||||
type testMapKeyExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *testMapKeyExtension) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ.Kind() == reflect.Int {
|
||||
return &funcEncoder{
|
||||
fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
stream.WriteRaw(`"`)
|
||||
stream.WriteInt(*(*int)(ptr) + 1)
|
||||
stream.WriteRaw(`"`)
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (extension *testMapKeyExtension) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ.Kind() == reflect.Int {
|
||||
return &funcDecoder{
|
||||
fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
i, err := strconv.Atoi(iter.ReadString())
|
||||
if err != nil {
|
||||
iter.ReportError("read map key", err.Error())
|
||||
return
|
||||
}
|
||||
i--
|
||||
*(*int)(ptr) = i
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type funcDecoder struct {
|
||||
fun jsoniter.DecoderFunc
|
||||
}
|
||||
|
||||
func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
decoder.fun(ptr, iter)
|
||||
}
|
||||
|
||||
type funcEncoder struct {
|
||||
fun jsoniter.EncoderFunc
|
||||
isEmptyFunc func(ptr unsafe.Pointer) bool
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
encoder.fun(ptr, stream)
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
if encoder.isEmptyFunc == nil {
|
||||
return false
|
||||
}
|
||||
return encoder.isEmptyFunc(ptr)
|
||||
}
|
238
extra/binary_as_string_codec.go
Normal file
238
extra/binary_as_string_codec.go
Normal file
@ -0,0 +1,238 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// safeSet holds the value true if the ASCII character with the given array
|
||||
// position can be represented inside a JSON string without any further
|
||||
// escaping.
|
||||
//
|
||||
// All values are true except for the ASCII control characters (0-31), the
|
||||
// double quote ("), and the backslash character ("\").
|
||||
var safeSet = [utf8.RuneSelf]bool{
|
||||
' ': true,
|
||||
'!': true,
|
||||
'"': false,
|
||||
'#': true,
|
||||
'$': true,
|
||||
'%': true,
|
||||
'&': true,
|
||||
'\'': true,
|
||||
'(': true,
|
||||
')': true,
|
||||
'*': true,
|
||||
'+': true,
|
||||
',': true,
|
||||
'-': true,
|
||||
'.': true,
|
||||
'/': true,
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
':': true,
|
||||
';': true,
|
||||
'<': true,
|
||||
'=': true,
|
||||
'>': true,
|
||||
'?': true,
|
||||
'@': true,
|
||||
'A': true,
|
||||
'B': true,
|
||||
'C': true,
|
||||
'D': true,
|
||||
'E': true,
|
||||
'F': true,
|
||||
'G': true,
|
||||
'H': true,
|
||||
'I': true,
|
||||
'J': true,
|
||||
'K': true,
|
||||
'L': true,
|
||||
'M': true,
|
||||
'N': true,
|
||||
'O': true,
|
||||
'P': true,
|
||||
'Q': true,
|
||||
'R': true,
|
||||
'S': true,
|
||||
'T': true,
|
||||
'U': true,
|
||||
'V': true,
|
||||
'W': true,
|
||||
'X': true,
|
||||
'Y': true,
|
||||
'Z': true,
|
||||
'[': true,
|
||||
'\\': false,
|
||||
']': true,
|
||||
'^': true,
|
||||
'_': true,
|
||||
'`': true,
|
||||
'a': true,
|
||||
'b': true,
|
||||
'c': true,
|
||||
'd': true,
|
||||
'e': true,
|
||||
'f': true,
|
||||
'g': true,
|
||||
'h': true,
|
||||
'i': true,
|
||||
'j': true,
|
||||
'k': true,
|
||||
'l': true,
|
||||
'm': true,
|
||||
'n': true,
|
||||
'o': true,
|
||||
'p': true,
|
||||
'q': true,
|
||||
'r': true,
|
||||
's': true,
|
||||
't': true,
|
||||
'u': true,
|
||||
'v': true,
|
||||
'w': true,
|
||||
'x': true,
|
||||
'y': true,
|
||||
'z': true,
|
||||
'{': true,
|
||||
'|': true,
|
||||
'}': true,
|
||||
'~': true,
|
||||
'\u007f': true,
|
||||
}
|
||||
|
||||
var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
|
||||
|
||||
type BinaryAsStringExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ == binaryType {
|
||||
return &binaryAsStringCodec{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ == binaryType {
|
||||
return &binaryAsStringCodec{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type binaryAsStringCodec struct {
|
||||
}
|
||||
|
||||
func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
rawBytes := iter.ReadStringAsSlice()
|
||||
bytes := make([]byte, 0, len(rawBytes))
|
||||
for i := 0; i < len(rawBytes); i++ {
|
||||
b := rawBytes[i]
|
||||
if b == '\\' {
|
||||
b2 := rawBytes[i+1]
|
||||
if b2 != '\\' {
|
||||
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
||||
return
|
||||
}
|
||||
b3 := rawBytes[i+2]
|
||||
if b3 != 'x' {
|
||||
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
||||
return
|
||||
}
|
||||
b4 := rawBytes[i+3]
|
||||
b5 := rawBytes[i+4]
|
||||
i += 4
|
||||
b = readHex(iter, b4, b5)
|
||||
}
|
||||
bytes = append(bytes, b)
|
||||
}
|
||||
*(*[]byte)(ptr) = bytes
|
||||
}
|
||||
func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*[]byte)(ptr))) == 0
|
||||
}
|
||||
func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
|
||||
stream.SetBuffer(newBuffer)
|
||||
}
|
||||
|
||||
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
|
||||
var ret byte
|
||||
if b1 >= '0' && b1 <= '9' {
|
||||
ret = b1 - '0'
|
||||
} else if b1 >= 'a' && b1 <= 'f' {
|
||||
ret = b1 - 'a' + 10
|
||||
} else {
|
||||
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
|
||||
return 0
|
||||
}
|
||||
ret *= 16
|
||||
if b2 >= '0' && b2 <= '9' {
|
||||
ret += b2 - '0'
|
||||
} else if b2 >= 'a' && b2 <= 'f' {
|
||||
ret += b2 - 'a' + 10
|
||||
} else {
|
||||
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
|
||||
return 0
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var hex = "0123456789abcdef"
|
||||
|
||||
func writeBytes(space []byte, s []byte) []byte {
|
||||
space = append(space, '"')
|
||||
// write string, the fast path, without utf8 and escape support
|
||||
var i int
|
||||
var c byte
|
||||
for i, c = range s {
|
||||
if c < utf8.RuneSelf && safeSet[c] {
|
||||
space = append(space, c)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(s)-1 {
|
||||
space = append(space, '"')
|
||||
return space
|
||||
}
|
||||
return writeBytesSlowPath(space, s[i:])
|
||||
}
|
||||
|
||||
func writeBytesSlowPath(space []byte, s []byte) []byte {
|
||||
start := 0
|
||||
// for the remaining parts, we process them char by char
|
||||
var i int
|
||||
var b byte
|
||||
for i, b = range s {
|
||||
if b >= utf8.RuneSelf {
|
||||
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
||||
start = i + 1
|
||||
continue
|
||||
}
|
||||
if safeSet[b] {
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
space = append(space, s[start:i]...)
|
||||
}
|
||||
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
||||
start = i + 1
|
||||
}
|
||||
if start < len(s) {
|
||||
space = append(space, s[start:]...)
|
||||
}
|
||||
return append(space, '"')
|
||||
}
|
32
extra/binary_as_string_codec_test.go
Normal file
32
extra/binary_as_string_codec_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jsoniter.RegisterExtension(&BinaryAsStringExtension{})
|
||||
}
|
||||
|
||||
func TestBinaryAsStringCodec(t *testing.T) {
|
||||
t.Run("safe set", func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := jsoniter.Marshal([]byte("hello"))
|
||||
should.NoError(err)
|
||||
should.Equal(`"hello"`, string(output))
|
||||
var val []byte
|
||||
should.NoError(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal(`hello`, string(val))
|
||||
})
|
||||
t.Run("non safe set", func(t *testing.T) {
|
||||
should := require.New(t)
|
||||
output, err := jsoniter.Marshal([]byte{1, 2, 3, 23})
|
||||
should.NoError(err)
|
||||
should.Equal(`"\\x01\\x02\\x03\\x17"`, string(output))
|
||||
var val []byte
|
||||
should.NoError(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal([]byte{1, 2, 3, 23}, val)
|
||||
})
|
||||
}
|
294
extra/fuzzy_decoder.go
Normal file
294
extra/fuzzy_decoder.go
Normal file
@ -0,0 +1,294 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
)
|
||||
|
||||
const maxUint = ^uint(0)
|
||||
const maxInt = int(maxUint >> 1)
|
||||
const minInt = -maxInt - 1
|
||||
|
||||
// RegisterFuzzyDecoders decode input from PHP with tolerance.
|
||||
// It will handle string/number auto conversation, and treat empty [] as empty struct.
|
||||
func RegisterFuzzyDecoders() {
|
||||
jsoniter.RegisterExtension(&tolerateEmptyArrayExtension{})
|
||||
jsoniter.RegisterTypeDecoder("string", &fuzzyStringDecoder{})
|
||||
jsoniter.RegisterTypeDecoder("float32", &fuzzyFloat32Decoder{})
|
||||
jsoniter.RegisterTypeDecoder("float64", &fuzzyFloat64Decoder{})
|
||||
jsoniter.RegisterTypeDecoder("int", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(maxInt) || val < float64(minInt) {
|
||||
iter.ReportError("fuzzy decode int", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*int)(ptr)) = int(val)
|
||||
} else {
|
||||
*((*int)(ptr)) = iter.ReadInt()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("uint", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(maxUint) || val < 0 {
|
||||
iter.ReportError("fuzzy decode uint", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*uint)(ptr)) = uint(val)
|
||||
} else {
|
||||
*((*uint)(ptr)) = iter.ReadUint()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("int8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxInt8) || val < float64(math.MinInt8) {
|
||||
iter.ReportError("fuzzy decode int8", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*int8)(ptr)) = int8(val)
|
||||
} else {
|
||||
*((*int8)(ptr)) = iter.ReadInt8()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("uint8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxUint8) || val < 0 {
|
||||
iter.ReportError("fuzzy decode uint8", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*uint8)(ptr)) = uint8(val)
|
||||
} else {
|
||||
*((*uint8)(ptr)) = iter.ReadUint8()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("int16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxInt16) || val < float64(math.MinInt16) {
|
||||
iter.ReportError("fuzzy decode int16", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*int16)(ptr)) = int16(val)
|
||||
} else {
|
||||
*((*int16)(ptr)) = iter.ReadInt16()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("uint16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxUint16) || val < 0 {
|
||||
iter.ReportError("fuzzy decode uint16", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*uint16)(ptr)) = uint16(val)
|
||||
} else {
|
||||
*((*uint16)(ptr)) = iter.ReadUint16()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("int32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxInt32) || val < float64(math.MinInt32) {
|
||||
iter.ReportError("fuzzy decode int32", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*int32)(ptr)) = int32(val)
|
||||
} else {
|
||||
*((*int32)(ptr)) = iter.ReadInt32()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("uint32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxUint32) || val < 0 {
|
||||
iter.ReportError("fuzzy decode uint32", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*uint32)(ptr)) = uint32(val)
|
||||
} else {
|
||||
*((*uint32)(ptr)) = iter.ReadUint32()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("int64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxInt64) || val < float64(math.MinInt64) {
|
||||
iter.ReportError("fuzzy decode int64", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*int64)(ptr)) = int64(val)
|
||||
} else {
|
||||
*((*int64)(ptr)) = iter.ReadInt64()
|
||||
}
|
||||
}})
|
||||
jsoniter.RegisterTypeDecoder("uint64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if isFloat {
|
||||
val := iter.ReadFloat64()
|
||||
if val > float64(math.MaxUint64) || val < 0 {
|
||||
iter.ReportError("fuzzy decode uint64", "exceed range")
|
||||
return
|
||||
}
|
||||
*((*uint64)(ptr)) = uint64(val)
|
||||
} else {
|
||||
*((*uint64)(ptr)) = iter.ReadUint64()
|
||||
}
|
||||
}})
|
||||
}
|
||||
|
||||
type tolerateEmptyArrayExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
|
||||
if typ.Kind() == reflect.Struct || typ.Kind() == reflect.Map {
|
||||
return &tolerateEmptyArrayDecoder{decoder}
|
||||
}
|
||||
return decoder
|
||||
}
|
||||
|
||||
type tolerateEmptyArrayDecoder struct {
|
||||
valDecoder jsoniter.ValDecoder
|
||||
}
|
||||
|
||||
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
if iter.WhatIsNext() == jsoniter.ArrayValue {
|
||||
iter.Skip()
|
||||
newIter := iter.Pool().BorrowIterator([]byte("{}"))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
decoder.valDecoder.Decode(ptr, newIter)
|
||||
} else {
|
||||
decoder.valDecoder.Decode(ptr, iter)
|
||||
}
|
||||
}
|
||||
|
||||
type fuzzyStringDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
switch valueType {
|
||||
case jsoniter.NumberValue:
|
||||
var number json.Number
|
||||
iter.ReadVal(&number)
|
||||
*((*string)(ptr)) = string(number)
|
||||
case jsoniter.StringValue:
|
||||
*((*string)(ptr)) = iter.ReadString()
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*string)(ptr)) = ""
|
||||
default:
|
||||
iter.ReportError("fuzzyStringDecoder", "not number or string")
|
||||
}
|
||||
}
|
||||
|
||||
type fuzzyIntegerDecoder struct {
|
||||
fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator)
|
||||
}
|
||||
|
||||
func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.NumberValue:
|
||||
var number json.Number
|
||||
iter.ReadVal(&number)
|
||||
str = string(number)
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
case jsoniter.BoolValue:
|
||||
if iter.ReadBool() {
|
||||
str = "1"
|
||||
} else {
|
||||
str = "0"
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
str = "0"
|
||||
default:
|
||||
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
||||
}
|
||||
if len(str) == 0 {
|
||||
str = "0"
|
||||
}
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
isFloat := strings.IndexByte(str, '.') != -1
|
||||
decoder.fun(isFloat, ptr, newIter)
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
}
|
||||
|
||||
type fuzzyFloat32Decoder struct {
|
||||
}
|
||||
|
||||
func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.NumberValue:
|
||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
*((*float32)(ptr)) = newIter.ReadFloat32()
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
case jsoniter.BoolValue:
|
||||
// support bool to float32
|
||||
if iter.ReadBool() {
|
||||
*((*float32)(ptr)) = 1
|
||||
} else {
|
||||
*((*float32)(ptr)) = 0
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*float32)(ptr)) = 0
|
||||
default:
|
||||
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
|
||||
}
|
||||
}
|
||||
|
||||
type fuzzyFloat64Decoder struct {
|
||||
}
|
||||
|
||||
func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
var str string
|
||||
switch valueType {
|
||||
case jsoniter.NumberValue:
|
||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||
case jsoniter.StringValue:
|
||||
str = iter.ReadString()
|
||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||
defer iter.Pool().ReturnIterator(newIter)
|
||||
*((*float64)(ptr)) = newIter.ReadFloat64()
|
||||
if newIter.Error != nil && newIter.Error != io.EOF {
|
||||
iter.Error = newIter.Error
|
||||
}
|
||||
case jsoniter.BoolValue:
|
||||
// support bool to float64
|
||||
if iter.ReadBool() {
|
||||
*((*float64)(ptr)) = 1
|
||||
} else {
|
||||
*((*float64)(ptr)) = 0
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.Skip()
|
||||
*((*float64)(ptr)) = 0
|
||||
default:
|
||||
iter.ReportError("fuzzyFloat64Decoder", "not number or string")
|
||||
}
|
||||
}
|
393
extra/fuzzy_decoder_test.go
Normal file
393
extra/fuzzy_decoder_test.go
Normal file
@ -0,0 +1,393 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterFuzzyDecoders()
|
||||
}
|
||||
|
||||
func Test_any_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val string
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal("100", val)
|
||||
should.Nil(jsoniter.UnmarshalFromString("10", &val))
|
||||
should.Equal("10", val)
|
||||
should.Nil(jsoniter.UnmarshalFromString("10.1", &val))
|
||||
should.Equal("10.1", val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal("10.1", val)
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
}
|
||||
func Test_any_to_int64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val int64
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(int64(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(int64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(int64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`""`, &val))
|
||||
should.Equal(int64(0), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int64(1), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(int64(-10), val)
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val int
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(100, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(10, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(10, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(10, val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(0, val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(1, val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_int16(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val int16
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(int16(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(int16(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(int16(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int16(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int16(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int16(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_int32(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val int32
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(int32(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(int32(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(int32(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int32(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_int8(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val int8
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(int8(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(int8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(int8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(int8(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(int8(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(int8(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_uint8(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val uint8
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(uint8(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(uint8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(uint8(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint8(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint8(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint8(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_uint64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val uint64
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(uint64(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(uint64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(uint64(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint64(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint64(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint64(0), val)
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
func Test_any_to_uint32(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val uint32
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(uint32(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(uint32(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(uint32(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint32(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint32(0), val)
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
func Test_any_to_uint16(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val uint16
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(uint16(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(uint16(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(uint16(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint16(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint16(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint16(1), val)
|
||||
|
||||
// TODO fix?
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
|
||||
should.Equal(uint16(0), val)
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
func Test_any_to_uint(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val uint
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(uint(100), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(uint(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(uint(10), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(uint(10), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(uint(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(uint(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
// large float to int
|
||||
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
|
||||
}
|
||||
|
||||
func Test_any_to_float32(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val float32
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(float32(100), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(float32(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(float32(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(float32(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(float32(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(float32(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
}
|
||||
|
||||
func Test_any_to_float64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val float64
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
|
||||
should.Equal(float64(100), val)
|
||||
|
||||
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
|
||||
should.Equal(float64(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
|
||||
should.Equal(float64(10.1), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||
should.Equal(float64(10), val)
|
||||
|
||||
// bool part
|
||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||
should.Equal(float64(0), val)
|
||||
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
|
||||
should.Equal(float64(1), val)
|
||||
|
||||
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
|
||||
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
|
||||
}
|
||||
|
||||
func Test_empty_array_as_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val map[string]interface{}
|
||||
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
|
||||
should.Equal(map[string]interface{}{}, val)
|
||||
}
|
||||
|
||||
func Test_empty_array_as_object(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val struct{}
|
||||
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
|
||||
should.Equal(struct{}{}, val)
|
||||
}
|
||||
|
||||
func Test_bad_case(t *testing.T) {
|
||||
var jsonstr = `
|
||||
{
|
||||
"extra_type": 181760,
|
||||
"combo_type": 0,
|
||||
"trigger_time_ms": 1498800398000,
|
||||
"_create_time": "2017-06-16 11:21:39",
|
||||
"_msg_type": 41000
|
||||
}
|
||||
`
|
||||
|
||||
type OrderEventRequestParams struct {
|
||||
ExtraType uint64 `json:"extra_type"`
|
||||
}
|
||||
|
||||
var a OrderEventRequestParams
|
||||
err := jsoniter.UnmarshalFromString(jsonstr, &a)
|
||||
should := require.New(t)
|
||||
should.Nil(err)
|
||||
}
|
||||
|
||||
func Test_null_to_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message string
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_int(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message int
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_float32(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message float32
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
||||
|
||||
func Test_null_to_float64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
body := []byte(`null`)
|
||||
var message float64
|
||||
err := jsoniter.Unmarshal(body, &message)
|
||||
should.NoError(err)
|
||||
}
|
55
extra/naming_strategy.go
Normal file
55
extra/naming_strategy.go
Normal file
@ -0,0 +1,55 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// SetNamingStrategy rename struct fields uniformly
|
||||
func SetNamingStrategy(translate func(string) string) {
|
||||
jsoniter.RegisterExtension(&namingStrategyExtension{jsoniter.DummyExtension{}, translate})
|
||||
}
|
||||
|
||||
type namingStrategyExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
translate func(string) string
|
||||
}
|
||||
|
||||
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
if unicode.IsLower(rune(binding.Field.Name()[0])) || binding.Field.Name()[0] == '_'{
|
||||
continue
|
||||
}
|
||||
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||
if hastag {
|
||||
tagParts := strings.Split(tag, ",")
|
||||
if tagParts[0] == "-" {
|
||||
continue // hidden field
|
||||
}
|
||||
if tagParts[0] != "" {
|
||||
continue // field explicitly named
|
||||
}
|
||||
}
|
||||
binding.ToNames = []string{extension.translate(binding.Field.Name())}
|
||||
binding.FromNames = []string{extension.translate(binding.Field.Name())}
|
||||
}
|
||||
}
|
||||
|
||||
// LowerCaseWithUnderscores one strategy to SetNamingStrategy for. It will change HelloWorld to hello_world.
|
||||
func LowerCaseWithUnderscores(name string) string {
|
||||
newName := []rune{}
|
||||
for i, c := range name {
|
||||
if i == 0 {
|
||||
newName = append(newName, unicode.ToLower(c))
|
||||
} else {
|
||||
if unicode.IsUpper(c) {
|
||||
newName = append(newName, '_')
|
||||
newName = append(newName, unicode.ToLower(c))
|
||||
} else {
|
||||
newName = append(newName, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(newName)
|
||||
}
|
66
extra/naming_strategy_test.go
Normal file
66
extra/naming_strategy_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_lower_case_with_underscores(t *testing.T) {
|
||||
should := require.New(t)
|
||||
should.Equal("hello_world", LowerCaseWithUnderscores("helloWorld"))
|
||||
should.Equal("hello_world", LowerCaseWithUnderscores("HelloWorld"))
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string
|
||||
FirstLanguage string
|
||||
}{
|
||||
UserName: "taowen",
|
||||
FirstLanguage: "Chinese",
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_overrides(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string `json:"UserName"`
|
||||
FirstLanguage string
|
||||
}{
|
||||
UserName: "taowen",
|
||||
FirstLanguage: "Chinese",
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"UserName":"taowen","first_language":"Chinese"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_omitempty(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string
|
||||
FirstLanguage string `json:",omitempty"`
|
||||
}{
|
||||
UserName: "taowen",
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"taowen"}`, string(output))
|
||||
}
|
||||
|
||||
func Test_set_naming_strategy_with_private_field(t *testing.T) {
|
||||
should := require.New(t)
|
||||
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||
output, err := jsoniter.Marshal(struct {
|
||||
UserName string
|
||||
userId int
|
||||
_UserAge int
|
||||
}{
|
||||
UserName: "allen",
|
||||
userId: 100,
|
||||
_UserAge: 30,
|
||||
})
|
||||
should.Nil(err)
|
||||
should.Equal(`{"user_name":"allen"}`, string(output))
|
||||
}
|
54
extra/privat_fields.go
Normal file
54
extra/privat_fields.go
Normal file
@ -0,0 +1,54 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// SupportPrivateFields include private fields when encoding/decoding
|
||||
func SupportPrivateFields() {
|
||||
jsoniter.RegisterExtension(&privateFieldsExtension{})
|
||||
}
|
||||
|
||||
type privateFieldsExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
for _, binding := range structDescriptor.Fields {
|
||||
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
|
||||
if isPrivate {
|
||||
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||
if !hastag {
|
||||
binding.FromNames = []string{binding.Field.Name()}
|
||||
binding.ToNames = []string{binding.Field.Name()}
|
||||
continue
|
||||
}
|
||||
tagParts := strings.Split(tag, ",")
|
||||
names := calcFieldNames(binding.Field.Name(), tagParts[0], tag)
|
||||
binding.FromNames = names
|
||||
binding.ToNames = names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
|
||||
// ignore?
|
||||
if wholeTag == "-" {
|
||||
return []string{}
|
||||
}
|
||||
// rename?
|
||||
var fieldNames []string
|
||||
if tagProvidedFieldName == "" {
|
||||
fieldNames = []string{originalFieldName}
|
||||
} else {
|
||||
fieldNames = []string{tagProvidedFieldName}
|
||||
}
|
||||
// private?
|
||||
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
|
||||
if isNotExported {
|
||||
fieldNames = []string{}
|
||||
}
|
||||
return fieldNames
|
||||
}
|
18
extra/private_fields_test.go
Normal file
18
extra/private_fields_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_private_fields(t *testing.T) {
|
||||
type TestObject struct {
|
||||
field1 string
|
||||
}
|
||||
SupportPrivateFields()
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
should.Nil(jsoniter.UnmarshalFromString(`{"field1":"Hello"}`, &obj))
|
||||
should.Equal("Hello", obj.field1)
|
||||
}
|
31
extra/time_as_int64_codec.go
Normal file
31
extra/time_as_int64_codec.go
Normal file
@ -0,0 +1,31 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// RegisterTimeAsInt64Codec encode/decode time since number of unit since epoch. the precision is the unit.
|
||||
func RegisterTimeAsInt64Codec(precision time.Duration) {
|
||||
jsoniter.RegisterTypeEncoder("time.Time", &timeAsInt64Codec{precision})
|
||||
jsoniter.RegisterTypeDecoder("time.Time", &timeAsInt64Codec{precision})
|
||||
}
|
||||
|
||||
type timeAsInt64Codec struct {
|
||||
precision time.Duration
|
||||
}
|
||||
|
||||
func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds()
|
||||
*((*time.Time)(ptr)) = time.Unix(0, nanoseconds)
|
||||
}
|
||||
|
||||
func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
ts := *((*time.Time)(ptr))
|
||||
return ts.UnixNano() == 0
|
||||
}
|
||||
func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
ts := *((*time.Time)(ptr))
|
||||
stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds())
|
||||
}
|
31
extra/time_as_int64_codec_test.go
Normal file
31
extra/time_as_int64_codec_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_time_as_int64(t *testing.T) {
|
||||
should := require.New(t)
|
||||
RegisterTimeAsInt64Codec(time.Nanosecond)
|
||||
output, err := jsoniter.Marshal(time.Unix(1497952257, 1002))
|
||||
should.Nil(err)
|
||||
should.Equal("1497952257000001002", string(output))
|
||||
var val time.Time
|
||||
should.Nil(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal(int64(1497952257000001002), val.UnixNano())
|
||||
}
|
||||
|
||||
func Test_time_as_int64_keep_microsecond(t *testing.T) {
|
||||
t.Skip("conflict")
|
||||
should := require.New(t)
|
||||
RegisterTimeAsInt64Codec(time.Microsecond)
|
||||
output, err := jsoniter.Marshal(time.Unix(1, 1002))
|
||||
should.Nil(err)
|
||||
should.Equal("1000001", string(output))
|
||||
var val time.Time
|
||||
should.Nil(jsoniter.Unmarshal(output, &val))
|
||||
should.Equal(int64(1000001000), val.UnixNano())
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Unmarshal adapts to json/encoding APIs
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := ParseBytes(data)
|
||||
iter.ReadVal(v)
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.reportError("Unmarshal", "there are bytes left after unmarshal")
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func UnmarshalAny(data []byte) (Any, error) {
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := ParseBytes(data)
|
||||
any := iter.ReadAny()
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return any, nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.reportError("UnmarshalAny", "there are bytes left after unmarshal")
|
||||
}
|
||||
return any, iter.Error
|
||||
}
|
||||
|
||||
func lastNotSpacePos(data []byte) int {
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
if data[i] != ' ' && data[i] != '\t' && data[i] != '\r' && data[i] != '\n' {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func UnmarshalFromString(str string, v interface{}) error {
|
||||
data := []byte(str)
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := ParseBytes(data)
|
||||
iter.ReadVal(v)
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.reportError("UnmarshalFromString", "there are bytes left after unmarshal")
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
func UnmarshalAnyFromString(str string) (Any, error) {
|
||||
data := []byte(str)
|
||||
data = data[:lastNotSpacePos(data)]
|
||||
iter := ParseBytes(data)
|
||||
any := iter.ReadAny()
|
||||
if iter.head == iter.tail {
|
||||
iter.loadMore()
|
||||
}
|
||||
if iter.Error == io.EOF {
|
||||
return any, nil
|
||||
}
|
||||
if iter.Error == nil {
|
||||
iter.reportError("UnmarshalAnyFromString", "there are bytes left after unmarshal")
|
||||
}
|
||||
return nil, iter.Error
|
||||
}
|
||||
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(buf, 512)
|
||||
stream.WriteVal(v)
|
||||
stream.Flush()
|
||||
if stream.Error != nil {
|
||||
return nil, stream.Error
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func MarshalToString(v interface{}) (string, error) {
|
||||
buf, err := Marshal(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func NewDecoder(reader io.Reader) *AdaptedDecoder {
|
||||
iter := Parse(reader, 512)
|
||||
return &AdaptedDecoder{iter}
|
||||
}
|
||||
|
||||
type AdaptedDecoder struct {
|
||||
iter *Iterator
|
||||
}
|
||||
|
||||
func (adapter *AdaptedDecoder) Decode(obj interface{}) error {
|
||||
adapter.iter.ReadVal(obj)
|
||||
err := adapter.iter.Error
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return adapter.iter.Error
|
||||
}
|
||||
|
||||
func (adapter *AdaptedDecoder) More() bool {
|
||||
return adapter.iter.head != adapter.iter.tail
|
||||
}
|
||||
|
||||
func (adapter *AdaptedDecoder) Buffered() io.Reader {
|
||||
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
|
||||
return bytes.NewReader(remaining)
|
||||
}
|
||||
|
||||
func NewEncoder(writer io.Writer) *AdaptedEncoder {
|
||||
stream := NewStream(writer, 512)
|
||||
return &AdaptedEncoder{stream}
|
||||
}
|
||||
|
||||
type AdaptedEncoder struct {
|
||||
stream *Stream
|
||||
}
|
||||
|
||||
func (adapter *AdaptedEncoder) Encode(val interface{}) error {
|
||||
adapter.stream.WriteVal(val)
|
||||
adapter.stream.Flush()
|
||||
return adapter.stream.Error
|
||||
}
|
||||
|
||||
func (adapter *AdaptedEncoder) SetIndent(prefix, indent string) {
|
||||
// not implemented yet
|
||||
}
|
304
feature_any.go
304
feature_any.go
@ -1,304 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Any interface {
|
||||
LastError() error
|
||||
ValueType() ValueType
|
||||
ToBool() bool
|
||||
ToInt() int
|
||||
ToInt32() int32
|
||||
ToInt64() int64
|
||||
ToUint() uint
|
||||
ToUint32() uint32
|
||||
ToUint64() uint64
|
||||
ToFloat32() float32
|
||||
ToFloat64() float64
|
||||
ToString() string
|
||||
Get(path ...interface{}) Any
|
||||
Size() int
|
||||
Keys() []string
|
||||
IterateObject() (func() (string, Any, bool), bool)
|
||||
IterateArray() (func() (Any, bool), bool)
|
||||
GetArray() []Any
|
||||
SetArray(newList []Any) bool
|
||||
GetObject() map[string]Any
|
||||
SetObject(map[string]Any) bool
|
||||
GetInterface() interface{}
|
||||
WriteTo(stream *Stream)
|
||||
Parse() *Iterator
|
||||
}
|
||||
|
||||
type baseAny struct{}
|
||||
|
||||
func (any *baseAny) Get(path ...interface{}) Any {
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("Get %v from simple value", path)}
|
||||
}
|
||||
|
||||
func (any *baseAny) Size() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *baseAny) Keys() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (any *baseAny) IterateObject() (func() (string, Any, bool), bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (any *baseAny) IterateArray() (func() (Any, bool), bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (any *baseAny) GetArray() []Any {
|
||||
return []Any{}
|
||||
}
|
||||
|
||||
func (any *baseAny) SetArray(newList []Any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (any *baseAny) GetObject() map[string]Any {
|
||||
return map[string]Any{}
|
||||
}
|
||||
|
||||
func (any *baseAny) SetObject(map[string]Any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func WrapInt32(val int32) Any {
|
||||
return &int32Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
func WrapInt64(val int64) Any {
|
||||
return &int64Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
func WrapUint32(val uint32) Any {
|
||||
return &uint32Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
func WrapUint64(val uint64) Any {
|
||||
return &uint64Any{baseAny{}, val}
|
||||
}
|
||||
|
||||
func WrapFloat64(val float64) Any {
|
||||
return &floatAny{baseAny{}, val}
|
||||
}
|
||||
|
||||
func WrapString(val string) Any {
|
||||
return &stringAny{baseAny{}, nil, val}
|
||||
}
|
||||
|
||||
func Wrap(val interface{}) Any {
|
||||
if val == nil {
|
||||
return &nilAny{}
|
||||
}
|
||||
asAny, isAny := val.(Any)
|
||||
if isAny {
|
||||
return asAny
|
||||
}
|
||||
type_ := reflect.TypeOf(val)
|
||||
switch type_.Kind() {
|
||||
case reflect.Slice:
|
||||
return wrapArray(val)
|
||||
case reflect.Struct:
|
||||
return wrapStruct(val)
|
||||
case reflect.Map:
|
||||
return wrapMap(val)
|
||||
case reflect.String:
|
||||
return WrapString(val.(string))
|
||||
case reflect.Int:
|
||||
return WrapInt64(int64(val.(int)))
|
||||
case reflect.Int8:
|
||||
return WrapInt32(int32(val.(int8)))
|
||||
case reflect.Int16:
|
||||
return WrapInt32(int32(val.(int16)))
|
||||
case reflect.Int32:
|
||||
return WrapInt32(val.(int32))
|
||||
case reflect.Int64:
|
||||
return WrapInt64(val.(int64))
|
||||
case reflect.Uint:
|
||||
return WrapUint64(uint64(val.(uint)))
|
||||
case reflect.Uint8:
|
||||
return WrapUint32(uint32(val.(uint8)))
|
||||
case reflect.Uint16:
|
||||
return WrapUint32(uint32(val.(uint16)))
|
||||
case reflect.Uint32:
|
||||
return WrapUint32(uint32(val.(uint32)))
|
||||
case reflect.Uint64:
|
||||
return WrapUint64(val.(uint64))
|
||||
case reflect.Float32:
|
||||
return WrapFloat64(float64(val.(float32)))
|
||||
case reflect.Float64:
|
||||
return WrapFloat64(val.(float64))
|
||||
case reflect.Bool:
|
||||
if val.(bool) == true {
|
||||
return &trueAny{}
|
||||
} else {
|
||||
return &falseAny{}
|
||||
}
|
||||
}
|
||||
return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", type_)}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadAny() Any {
|
||||
return iter.readAny(nil)
|
||||
}
|
||||
|
||||
func (iter *Iterator) readAny(reusableIter *Iterator) Any {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case '"':
|
||||
return iter.readStringAny(reusableIter)
|
||||
case 'n':
|
||||
iter.skipFixedBytes(3) // null
|
||||
return &nilAny{}
|
||||
case 't':
|
||||
iter.skipFixedBytes(3) // true
|
||||
return &trueAny{}
|
||||
case 'f':
|
||||
iter.skipFixedBytes(4) // false
|
||||
return &falseAny{}
|
||||
case '{':
|
||||
return iter.readObjectAny(reusableIter)
|
||||
case '[':
|
||||
return iter.readArrayAny(reusableIter)
|
||||
default:
|
||||
return iter.readNumberAny(reusableIter, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readNumberAny(reusableIter *Iterator, firstByte byte) Any {
|
||||
dotFound := false
|
||||
lazyBuf := make([]byte, 1, 8)
|
||||
lazyBuf[0] = firstByte
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
if c == '.' {
|
||||
dotFound = true
|
||||
continue
|
||||
}
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:i]...)
|
||||
iter.head = i
|
||||
if dotFound {
|
||||
return &float64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
} else {
|
||||
if firstByte == '-' {
|
||||
return &int64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
} else {
|
||||
return &uint64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
|
||||
if !iter.loadMore() {
|
||||
iter.head = iter.tail
|
||||
if dotFound {
|
||||
return &float64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
} else {
|
||||
if firstByte == '-' {
|
||||
return &int64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
} else {
|
||||
return &uint64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readStringAny(reusableIter *Iterator) Any {
|
||||
lazyBuf := make([]byte, 1, 8)
|
||||
lazyBuf[0] = '"'
|
||||
for {
|
||||
end, escaped := iter.findStringEnd()
|
||||
if end == -1 {
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("readStringAny", "incomplete string")
|
||||
return &invalidAny{}
|
||||
}
|
||||
if escaped {
|
||||
iter.head = 1 // skip the first char as last char read is \
|
||||
}
|
||||
} else {
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:end]...)
|
||||
iter.head = end
|
||||
return &stringLazyAny{baseAny{}, lazyBuf, reusableIter, nil, ""}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectAny(reusableIter *Iterator) Any {
|
||||
level := 1
|
||||
lazyBuf := make([]byte, 1, 32)
|
||||
lazyBuf[0] = '{'
|
||||
for {
|
||||
start := iter.head
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '{': // If open symbol, increase level
|
||||
level++
|
||||
case '}': // If close symbol, increase level
|
||||
level--
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
|
||||
return &objectLazyAny{baseAny{}, lazyBuf, reusableIter, nil, nil, lazyBuf}
|
||||
}
|
||||
}
|
||||
}
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipObject", "incomplete object")
|
||||
return &invalidAny{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readArrayAny(reusableIter *Iterator) Any {
|
||||
level := 1
|
||||
lazyBuf := make([]byte, 1, 32)
|
||||
lazyBuf[0] = '['
|
||||
for {
|
||||
start := iter.head
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '[': // If open symbol, increase level
|
||||
level++
|
||||
case ']': // If close symbol, increase level
|
||||
level--
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
|
||||
return &arrayLazyAny{baseAny{}, lazyBuf, reusableIter, nil, nil, lazyBuf}
|
||||
}
|
||||
}
|
||||
}
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipArray", "incomplete array")
|
||||
return &invalidAny{}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,539 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type arrayLazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache []Any
|
||||
remaining []byte
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ValueType() ValueType {
|
||||
return Array
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(any.remaining)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) fillCacheUntil(target int) Any {
|
||||
if any.remaining == nil {
|
||||
if target >= len(any.cache) {
|
||||
return nil
|
||||
}
|
||||
return any.cache[target]
|
||||
}
|
||||
if any.cache == nil {
|
||||
any.cache = make([]Any, 0, 8)
|
||||
}
|
||||
i := len(any.cache)
|
||||
if target < i {
|
||||
return any.cache[target]
|
||||
}
|
||||
iter := any.Parse()
|
||||
if (len(any.remaining) == len(any.buf)) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
element := iter.readAny(iter)
|
||||
any.cache = append(any.cache, element)
|
||||
if target == 0 {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
any.err = iter.Error
|
||||
return element
|
||||
}
|
||||
i = 1
|
||||
} else {
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
element := iter.readAny(iter)
|
||||
any.cache = append(any.cache, element)
|
||||
if i == target {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
any.err = iter.Error
|
||||
return element
|
||||
}
|
||||
i++
|
||||
}
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) fillCache() {
|
||||
if any.remaining == nil {
|
||||
return
|
||||
}
|
||||
if any.cache == nil {
|
||||
any.cache = make([]Any, 0, 8)
|
||||
}
|
||||
iter := any.Parse()
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
any.cache = append(any.cache, iter.readAny(iter))
|
||||
} else {
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
any.cache = append(any.cache, iter.readAny(iter))
|
||||
}
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToBool() bool {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
return len(any.cache) != 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt() int {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt32() int32 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt64() int64 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint() uint {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint32() uint32 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToUint64() uint64 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat32() float32 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat64() float64 {
|
||||
if any.cache == nil {
|
||||
any.IterateArray() // trigger first element read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToString() string {
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
// nothing has been parsed yet
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
} else {
|
||||
any.fillCache()
|
||||
str, err := MarshalToString(any.cache)
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
var element Any
|
||||
switch firstPath := path[0].(type) {
|
||||
case int:
|
||||
element = any.fillCacheUntil(firstPath)
|
||||
if element == nil {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
any.fillCache()
|
||||
arr := make([]Any, 0, len(any.cache))
|
||||
for _, element := range any.cache {
|
||||
found := element.Get(path[1:]...)
|
||||
if found.ValueType() != Invalid {
|
||||
arr = append(arr, found)
|
||||
}
|
||||
}
|
||||
return wrapArray(arr)
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", path[0], any.cache)}
|
||||
}
|
||||
default:
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", path[0], any.cache)}
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return element
|
||||
} else {
|
||||
return element.Get(path[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) IterateArray() (func() (Any, bool), bool) {
|
||||
if any.cache == nil {
|
||||
any.cache = make([]Any, 0, 8)
|
||||
}
|
||||
remaining := any.remaining
|
||||
if len(remaining) == len(any.buf) {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
v := iter.readAny(iter)
|
||||
any.cache = append(any.cache, v)
|
||||
remaining = iter.buf[iter.head:]
|
||||
any.remaining = remaining
|
||||
} else {
|
||||
remaining = nil
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
arr := any.cache
|
||||
nextValue := arr[0]
|
||||
i := 1
|
||||
return func() (Any, bool) {
|
||||
value := nextValue
|
||||
if i < len(arr) {
|
||||
// read from cache
|
||||
nextValue = arr[i]
|
||||
i++
|
||||
return value, true
|
||||
} else {
|
||||
// read from buffer
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(remaining)
|
||||
c := iter.nextToken()
|
||||
if c == ',' {
|
||||
nextValue = iter.readAny(iter)
|
||||
any.cache = append(any.cache, nextValue)
|
||||
remaining = iter.buf[iter.head:]
|
||||
any.remaining = remaining
|
||||
any.err = iter.Error
|
||||
return value, true
|
||||
} else {
|
||||
remaining = nil
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return value, false
|
||||
}
|
||||
}
|
||||
}, true
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) GetArray() []Any {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) SetArray(newList []Any) bool {
|
||||
any.fillCache()
|
||||
any.cache = newList
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) WriteTo(stream *Stream) {
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
// nothing has been parsed yet
|
||||
stream.Write(any.buf)
|
||||
} else {
|
||||
any.fillCache()
|
||||
stream.WriteVal(any.cache)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type arrayAny struct {
|
||||
baseAny
|
||||
err error
|
||||
cache []Any
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapArray(val interface{}) *arrayAny {
|
||||
return &arrayAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *arrayAny) ValueType() ValueType {
|
||||
return Array
|
||||
}
|
||||
|
||||
func (any *arrayAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *arrayAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToBool() bool {
|
||||
return any.val.Len() != 0
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt() int {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt32() int32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToInt64() int64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint() uint {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint32() uint32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToUint64() uint64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToFloat32() float32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToFloat64() float64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *arrayAny) ToString() string {
|
||||
if len(any.cache) == 0 {
|
||||
// nothing has been parsed yet
|
||||
str, err := MarshalToString(any.val.Interface())
|
||||
any.err = err
|
||||
return str
|
||||
} else {
|
||||
any.fillCache()
|
||||
str, err := MarshalToString(any.cache)
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) fillCacheUntil(idx int) Any {
|
||||
if idx < len(any.cache) {
|
||||
return any.cache[idx]
|
||||
} else {
|
||||
for i := len(any.cache); i < any.val.Len(); i++ {
|
||||
element := Wrap(any.val.Index(i).Interface())
|
||||
any.cache = append(any.cache, element)
|
||||
if idx == i {
|
||||
return element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) fillCache() {
|
||||
any.cache = make([]Any, any.val.Len())
|
||||
for i := 0; i < any.val.Len(); i++ {
|
||||
any.cache[i] = Wrap(any.val.Index(i).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
var element Any
|
||||
switch firstPath := path[0].(type) {
|
||||
case int:
|
||||
element = any.fillCacheUntil(firstPath)
|
||||
if element == nil {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
any.fillCache()
|
||||
mappedAll := make([]Any, 0, len(any.cache))
|
||||
for _, element := range any.cache {
|
||||
mapped := element.Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
mappedAll = append(mappedAll, mapped)
|
||||
}
|
||||
}
|
||||
return wrapArray(mappedAll)
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", path[0], any.cache)}
|
||||
}
|
||||
default:
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", path[0], any.cache)}
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return element
|
||||
} else {
|
||||
return element.Get(path[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
||||
|
||||
func (any *arrayAny) IterateArray() (func() (Any, bool), bool) {
|
||||
if any.val.Len() == 0 {
|
||||
return nil, false
|
||||
}
|
||||
i := 0
|
||||
return func() (Any, bool) {
|
||||
if i == any.val.Len() {
|
||||
return nil, false
|
||||
}
|
||||
if i == len(any.cache) {
|
||||
any.cache = append(any.cache, Wrap(any.val.Index(i).Interface()))
|
||||
}
|
||||
val := any.cache[i]
|
||||
i++
|
||||
return val, i != any.val.Len()
|
||||
}, true
|
||||
}
|
||||
|
||||
func (any *arrayAny) GetArray() []Any {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *arrayAny) SetArray(newList []Any) bool {
|
||||
any.fillCache()
|
||||
any.cache = newList
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *arrayAny) WriteTo(stream *Stream) {
|
||||
if len(any.cache) == 0 {
|
||||
// nothing has been parsed yet
|
||||
stream.WriteVal(any.val)
|
||||
} else {
|
||||
any.fillCache()
|
||||
stream.WriteVal(any.cache)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *arrayAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type float64LazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache float64
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
}
|
||||
iter.ResetBytes(any.buf)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) fillCache() {
|
||||
if any.err != nil {
|
||||
return
|
||||
}
|
||||
iter := any.Parse()
|
||||
any.cache = iter.ReadFloat64()
|
||||
if iter.Error != io.EOF {
|
||||
iter.reportError("floatLazyAny", "there are bytes left")
|
||||
}
|
||||
any.err = iter.Error
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToBool() bool {
|
||||
return any.ToFloat64() != 0
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToInt() int {
|
||||
any.fillCache()
|
||||
return int(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToInt32() int32 {
|
||||
any.fillCache()
|
||||
return int32(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToInt64() int64 {
|
||||
any.fillCache()
|
||||
return int64(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToUint() uint {
|
||||
any.fillCache()
|
||||
return uint(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToUint32() uint32 {
|
||||
any.fillCache()
|
||||
return uint32(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToUint64() uint64 {
|
||||
any.fillCache()
|
||||
return uint64(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToFloat32() float32 {
|
||||
any.fillCache()
|
||||
return float32(any.cache)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToFloat64() float64 {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *float64LazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type floatAny struct {
|
||||
baseAny
|
||||
val float64
|
||||
}
|
||||
|
||||
func (any *floatAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *floatAny) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *floatAny) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *floatAny) ToBool() bool {
|
||||
return any.ToFloat64() != 0
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToInt64() int64 {
|
||||
return int64(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint() uint {
|
||||
return uint(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint32() uint32 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToUint64() uint64 {
|
||||
return uint64(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) ToFloat64() float64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *floatAny) ToString() string {
|
||||
return strconv.FormatFloat(any.val, 'E', -1, 64)
|
||||
}
|
||||
|
||||
func (any *floatAny) WriteTo(stream *Stream) {
|
||||
stream.WriteFloat64(any.val)
|
||||
}
|
||||
|
||||
func (any *floatAny) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type int64LazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache int64
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
}
|
||||
iter.ResetBytes(any.buf)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) fillCache() {
|
||||
if any.err != nil {
|
||||
return
|
||||
}
|
||||
iter := any.Parse()
|
||||
any.cache = iter.ReadInt64()
|
||||
if iter.Error != io.EOF {
|
||||
iter.reportError("intLazyAny", "there are bytes left")
|
||||
}
|
||||
any.err = iter.Error
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToBool() bool {
|
||||
return any.ToInt64() != 0
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToInt() int {
|
||||
any.fillCache()
|
||||
return int(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToInt32() int32 {
|
||||
any.fillCache()
|
||||
return int32(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToInt64() int64 {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToUint() uint {
|
||||
any.fillCache()
|
||||
return uint(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToUint32() uint32 {
|
||||
any.fillCache()
|
||||
return uint32(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToUint64() uint64 {
|
||||
any.fillCache()
|
||||
return uint64(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToFloat32() float32 {
|
||||
any.fillCache()
|
||||
return float32(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToFloat64() float64 {
|
||||
any.fillCache()
|
||||
return float64(any.cache)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *int64LazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type int64Any struct {
|
||||
baseAny
|
||||
val int64
|
||||
}
|
||||
|
||||
func (any *int64Any) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *int64Any) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *int64Any) ToBool() bool {
|
||||
return any.val != 0
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToInt64() int64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint() uint {
|
||||
return uint(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint32() uint32 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToUint64() uint64 {
|
||||
return uint64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToFloat64() float64 {
|
||||
return float64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) ToString() string {
|
||||
return strconv.FormatInt(any.val, 10)
|
||||
}
|
||||
|
||||
func (any *int64Any) WriteTo(stream *Stream) {
|
||||
stream.WriteInt64(any.val)
|
||||
}
|
||||
|
||||
func (any *int64Any) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *int64Any) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
@ -1,848 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type objectLazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache map[string]Any
|
||||
remaining []byte
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ValueType() ValueType {
|
||||
return Object
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(any.remaining)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) fillCacheUntil(target string) Any {
|
||||
if any.remaining == nil {
|
||||
return any.cache[target]
|
||||
}
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
val := any.cache[target]
|
||||
if val != nil {
|
||||
return val
|
||||
}
|
||||
iter := any.Parse()
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != '}' {
|
||||
iter.unreadByte()
|
||||
k := string(iter.readObjectFieldAsBytes())
|
||||
v := iter.readAny(iter)
|
||||
any.cache[k] = v
|
||||
if target == k {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
any.err = iter.Error
|
||||
return v
|
||||
}
|
||||
} else {
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
k := string(iter.readObjectFieldAsBytes())
|
||||
v := iter.readAny(iter)
|
||||
any.cache[k] = v
|
||||
if target == k {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
any.err = iter.Error
|
||||
return v
|
||||
}
|
||||
}
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) fillCache() {
|
||||
if any.remaining == nil {
|
||||
return
|
||||
}
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
iter := any.Parse()
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != '}' {
|
||||
iter.unreadByte()
|
||||
k := string(iter.readObjectFieldAsBytes())
|
||||
v := iter.readAny(iter)
|
||||
any.cache[k] = v
|
||||
} else {
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
k := string(iter.readObjectFieldAsBytes())
|
||||
v := iter.readAny(iter)
|
||||
any.cache[k] = v
|
||||
}
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToBool() bool {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
return len(any.cache) != 0
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt() int {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt32() int32 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToInt64() int64 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint() uint {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint32() uint32 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToUint64() uint64 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToFloat32() float32 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToFloat64() float64 {
|
||||
if any.cache == nil {
|
||||
any.IterateObject() // trigger first value read
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) ToString() string {
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
// nothing has been parsed yet
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
} else {
|
||||
any.fillCache()
|
||||
str, err := MarshalToString(any.cache)
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
var element Any
|
||||
|
||||
switch firstPath := path[0].(type) {
|
||||
case string:
|
||||
element = any.fillCacheUntil(firstPath)
|
||||
if element == nil {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
any.fillCache()
|
||||
mappedAll := map[string]Any{}
|
||||
for key, value := range any.cache {
|
||||
mapped := value.Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
mappedAll[key] = mapped
|
||||
}
|
||||
}
|
||||
return wrapMap(mappedAll)
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
default:
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return element
|
||||
} else {
|
||||
return element.Get(path[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Keys() []string {
|
||||
any.fillCache()
|
||||
keys := make([]string, 0, len(any.cache))
|
||||
for key := range any.cache {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) IterateObject() (func() (string, Any, bool), bool) {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
remaining := any.remaining
|
||||
if len(remaining) == len(any.buf) {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != '}' {
|
||||
iter.unreadByte()
|
||||
k := string(iter.readObjectFieldAsBytes())
|
||||
v := iter.readAny(iter)
|
||||
any.cache[k] = v
|
||||
remaining = iter.buf[iter.head:]
|
||||
any.remaining = remaining
|
||||
} else {
|
||||
remaining = nil
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
if len(any.cache) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
keys := make([]string, 0, len(any.cache))
|
||||
values := make([]Any, 0, len(any.cache))
|
||||
for key, value := range any.cache {
|
||||
keys = append(keys, key)
|
||||
values = append(values, value)
|
||||
}
|
||||
nextKey := keys[0]
|
||||
nextValue := values[0]
|
||||
i := 1
|
||||
return func() (string, Any, bool) {
|
||||
key := nextKey
|
||||
value := nextValue
|
||||
if i < len(keys) {
|
||||
// read from cache
|
||||
nextKey = keys[i]
|
||||
nextValue = values[i]
|
||||
i++
|
||||
return key, value, true
|
||||
} else {
|
||||
// read from buffer
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(remaining)
|
||||
c := iter.nextToken()
|
||||
if c == ',' {
|
||||
nextKey = string(iter.readObjectFieldAsBytes())
|
||||
nextValue = iter.readAny(iter)
|
||||
any.cache[nextKey] = nextValue
|
||||
remaining = iter.buf[iter.head:]
|
||||
any.remaining = remaining
|
||||
any.err = iter.Error
|
||||
return key, value, true
|
||||
} else {
|
||||
nextKey = ""
|
||||
remaining = nil
|
||||
any.remaining = nil
|
||||
any.err = iter.Error
|
||||
return key, value, false
|
||||
}
|
||||
}
|
||||
}, true
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) GetObject() map[string]Any {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) SetObject(val map[string]Any) bool {
|
||||
any.fillCache()
|
||||
any.cache = val
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) WriteTo(stream *Stream) {
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
// nothing has been parsed yet
|
||||
stream.Write(any.buf)
|
||||
} else {
|
||||
any.fillCache()
|
||||
stream.WriteVal(any.cache)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectLazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type objectAny struct {
|
||||
baseAny
|
||||
err error
|
||||
cache map[string]Any
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapStruct(val interface{}) *objectAny {
|
||||
return &objectAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *objectAny) ValueType() ValueType {
|
||||
return Object
|
||||
}
|
||||
|
||||
func (any *objectAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *objectAny) fillCacheUntil(target string) Any {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
element, found := any.cache[target]
|
||||
if found {
|
||||
return element
|
||||
}
|
||||
for i := len(any.cache); i < any.val.NumField(); i++ {
|
||||
field := any.val.Field(i)
|
||||
fieldName := any.val.Type().Field(i).Name
|
||||
var element Any
|
||||
if field.CanInterface() {
|
||||
element = Wrap(field.Interface())
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
|
||||
}
|
||||
any.cache[fieldName] = element
|
||||
if fieldName == target {
|
||||
return element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *objectAny) fillCache() {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
if len(any.cache) == any.val.NumField() {
|
||||
return
|
||||
}
|
||||
for i := 0; i < any.val.NumField(); i++ {
|
||||
field := any.val.Field(i)
|
||||
fieldName := any.val.Type().Field(i).Name
|
||||
var element Any
|
||||
if field.CanInterface() {
|
||||
element = Wrap(field.Interface())
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
|
||||
}
|
||||
any.cache[fieldName] = element
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *objectAny) ToBool() bool {
|
||||
return any.val.NumField() != 0
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt() int {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt32() int32 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToInt64() int64 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint() uint {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint32() uint32 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToUint64() uint64 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToFloat32() float32 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToFloat64() float64 {
|
||||
if any.val.NumField() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *objectAny) ToString() string {
|
||||
if len(any.cache) == 0 {
|
||||
str, err := MarshalToString(any.val.Interface())
|
||||
any.err = err
|
||||
return str
|
||||
} else {
|
||||
any.fillCache()
|
||||
str, err := MarshalToString(any.cache)
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
var element Any
|
||||
switch firstPath := path[0].(type) {
|
||||
case string:
|
||||
element = any.fillCacheUntil(firstPath)
|
||||
if element == nil {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
any.fillCache()
|
||||
mappedAll := map[string]Any{}
|
||||
for key, value := range any.cache {
|
||||
mapped := value.Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
mappedAll[key] = mapped
|
||||
}
|
||||
}
|
||||
return wrapMap(mappedAll)
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
default:
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return element
|
||||
} else {
|
||||
return element.Get(path[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectAny) Keys() []string {
|
||||
any.fillCache()
|
||||
keys := make([]string, 0, len(any.cache))
|
||||
for key := range any.cache {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *objectAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
||||
|
||||
func (any *objectAny) IterateObject() (func() (string, Any, bool), bool) {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
if any.val.NumField() == 0 {
|
||||
return nil, false
|
||||
}
|
||||
cacheKeys := make([]string, len(any.cache))
|
||||
i := 0
|
||||
for key := range any.cache {
|
||||
cacheKeys[i] = key
|
||||
i++
|
||||
}
|
||||
i = 0
|
||||
return func() (string, Any, bool) {
|
||||
if i == any.val.NumField() {
|
||||
return "", nil, false
|
||||
}
|
||||
var fieldName string
|
||||
var fieldValueAsAny Any
|
||||
if i == len(cacheKeys) {
|
||||
fieldName = any.val.Type().Field(i).Name
|
||||
cacheKeys = append(cacheKeys, fieldName)
|
||||
fieldValue := any.val.Field(i)
|
||||
if fieldValue.CanInterface() {
|
||||
fieldValueAsAny = Wrap(fieldValue.Interface())
|
||||
any.cache[fieldName] = fieldValueAsAny
|
||||
} else {
|
||||
fieldValueAsAny = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", fieldName, any.cache)}
|
||||
any.cache[fieldName] = fieldValueAsAny
|
||||
}
|
||||
} else {
|
||||
fieldName = cacheKeys[i]
|
||||
fieldValueAsAny = any.cache[fieldName]
|
||||
}
|
||||
i++
|
||||
return fieldName, fieldValueAsAny, i != any.val.NumField()
|
||||
}, true
|
||||
}
|
||||
|
||||
func (any *objectAny) GetObject() map[string]Any {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *objectAny) SetObject(val map[string]Any) bool {
|
||||
any.fillCache()
|
||||
any.cache = val
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *objectAny) WriteTo(stream *Stream) {
|
||||
if len(any.cache) == 0 {
|
||||
// nothing has been parsed yet
|
||||
stream.WriteVal(any.val)
|
||||
} else {
|
||||
any.fillCache()
|
||||
stream.WriteVal(any.cache)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *objectAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type mapAny struct {
|
||||
baseAny
|
||||
err error
|
||||
cache map[string]Any
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func wrapMap(val interface{}) *mapAny {
|
||||
return &mapAny{baseAny{}, nil, nil, reflect.ValueOf(val)}
|
||||
}
|
||||
|
||||
func (any *mapAny) ValueType() ValueType {
|
||||
return Object
|
||||
}
|
||||
|
||||
func (any *mapAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *mapAny) fillCacheUntil(target string) Any {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
element, found := any.cache[target]
|
||||
if found {
|
||||
return element
|
||||
}
|
||||
for _, key := range any.val.MapKeys() {
|
||||
keyAsStr := key.String()
|
||||
_, found := any.cache[keyAsStr]
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
element := Wrap(any.val.MapIndex(key).Interface())
|
||||
any.cache[keyAsStr] = element
|
||||
if keyAsStr == target {
|
||||
return element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *mapAny) fillCache() {
|
||||
if any.cache == nil {
|
||||
any.cache = map[string]Any{}
|
||||
}
|
||||
if len(any.cache) == any.val.Len() {
|
||||
return
|
||||
}
|
||||
for _, key := range any.val.MapKeys() {
|
||||
keyAsStr := key.String()
|
||||
element := Wrap(any.val.MapIndex(key).Interface())
|
||||
any.cache[keyAsStr] = element
|
||||
}
|
||||
}
|
||||
|
||||
func (any *mapAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *mapAny) ToBool() bool {
|
||||
return any.val.Len() != 0
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt() int {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt32() int32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToInt64() int64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint() uint {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint32() uint32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToUint64() uint64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToFloat32() float32 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToFloat64() float64 {
|
||||
if any.val.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (any *mapAny) ToString() string {
|
||||
if len(any.cache) == 0 {
|
||||
str, err := MarshalToString(any.val.Interface())
|
||||
any.err = err
|
||||
return str
|
||||
} else {
|
||||
any.fillCache()
|
||||
str, err := MarshalToString(any.cache)
|
||||
any.err = err
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
func (any *mapAny) Get(path ...interface{}) Any {
|
||||
if len(path) == 0 {
|
||||
return any
|
||||
}
|
||||
var element Any
|
||||
switch firstPath := path[0].(type) {
|
||||
case string:
|
||||
element = any.fillCacheUntil(firstPath)
|
||||
if element == nil {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
case int32:
|
||||
if '*' == firstPath {
|
||||
any.fillCache()
|
||||
mappedAll := map[string]Any{}
|
||||
for key, value := range any.cache {
|
||||
mapped := value.Get(path[1:]...)
|
||||
if mapped.ValueType() != Invalid {
|
||||
mappedAll[key] = mapped
|
||||
}
|
||||
}
|
||||
return wrapMap(mappedAll)
|
||||
} else {
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
default:
|
||||
element = &invalidAny{baseAny{}, fmt.Errorf("%v not found in %v", firstPath, any.cache)}
|
||||
}
|
||||
if len(path) == 1 {
|
||||
return element
|
||||
} else {
|
||||
return element.Get(path[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *mapAny) Keys() []string {
|
||||
any.fillCache()
|
||||
keys := make([]string, 0, len(any.cache))
|
||||
for key := range any.cache {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (any *mapAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
||||
|
||||
func (any *mapAny) IterateObject() (func() (string, Any, bool), bool) {
|
||||
any.fillCache()
|
||||
if len(any.cache) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
keys := make([]string, len(any.cache))
|
||||
values := make([]Any, len(any.cache))
|
||||
i := 0
|
||||
for k, v := range any.cache {
|
||||
keys[i] = k
|
||||
values[i] = v
|
||||
i++
|
||||
}
|
||||
i = 0
|
||||
return func() (string, Any, bool) {
|
||||
if i == len(keys) {
|
||||
return "", nil, false
|
||||
}
|
||||
k := keys[i]
|
||||
v := values[i]
|
||||
i++
|
||||
return k, v, i != len(keys)
|
||||
}, true
|
||||
}
|
||||
|
||||
func (any *mapAny) GetObject() map[string]Any {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *mapAny) SetObject(val map[string]Any) bool {
|
||||
any.fillCache()
|
||||
any.cache = val
|
||||
return true
|
||||
}
|
||||
|
||||
func (any *mapAny) WriteTo(stream *Stream) {
|
||||
if len(any.cache) == 0 {
|
||||
// nothing has been parsed yet
|
||||
stream.WriteVal(any.val)
|
||||
} else {
|
||||
any.fillCache()
|
||||
stream.WriteVal(any.cache)
|
||||
}
|
||||
}
|
||||
|
||||
func (any *mapAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type stringLazyAny struct{
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache string
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ValueType() ValueType {
|
||||
return String
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(any.buf)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) fillCache() {
|
||||
if any.err != nil {
|
||||
return
|
||||
}
|
||||
iter := any.Parse()
|
||||
any.cache = iter.ReadString()
|
||||
if iter.Error != io.EOF {
|
||||
iter.reportError("stringLazyAny", "there are bytes left")
|
||||
}
|
||||
any.err = iter.Error
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToBool() bool {
|
||||
str := any.ToString()
|
||||
if str == "false" {
|
||||
return false
|
||||
}
|
||||
for _, c := range str {
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToInt() int {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadInt()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToInt32() int32 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadInt32()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToInt64() int64 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadInt64()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToUint() uint {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadUint()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToUint32() uint32 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadUint32()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToUint64() uint64 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadUint64()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToFloat32() float32 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadFloat32()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToFloat64() float64 {
|
||||
iter := any.Parse()
|
||||
iter.head++
|
||||
val := iter.ReadFloat64()
|
||||
any.err = iter.Error
|
||||
return val
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) ToString() string {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *stringLazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type stringAny struct{
|
||||
baseAny
|
||||
err error
|
||||
val string
|
||||
}
|
||||
|
||||
func (any *stringAny) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (any *stringAny) ValueType() ValueType {
|
||||
return String
|
||||
}
|
||||
|
||||
func (any *stringAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *stringAny) ToBool() bool {
|
||||
str := any.ToString()
|
||||
if str == "false" {
|
||||
return false
|
||||
}
|
||||
for _, c := range str {
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt() int {
|
||||
parsed, err := strconv.ParseInt(any.val, 10, 64)
|
||||
any.err = err
|
||||
return int(parsed)
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt32() int32 {
|
||||
parsed, err := strconv.ParseInt(any.val, 10, 32)
|
||||
any.err = err
|
||||
return int32(parsed)
|
||||
}
|
||||
|
||||
func (any *stringAny) ToInt64() int64 {
|
||||
parsed, err := strconv.ParseInt(any.val, 10, 64)
|
||||
any.err = err
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint() uint {
|
||||
parsed, err := strconv.ParseUint(any.val, 10, 64)
|
||||
any.err = err
|
||||
return uint(parsed)
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint32() uint32 {
|
||||
parsed, err := strconv.ParseUint(any.val, 10, 32)
|
||||
any.err = err
|
||||
return uint32(parsed)
|
||||
}
|
||||
|
||||
func (any *stringAny) ToUint64() uint64 {
|
||||
parsed, err := strconv.ParseUint(any.val, 10, 64)
|
||||
any.err = err
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToFloat32() float32 {
|
||||
parsed, err := strconv.ParseFloat(any.val, 32)
|
||||
any.err = err
|
||||
return float32(parsed)
|
||||
}
|
||||
|
||||
func (any *stringAny) ToFloat64() float64 {
|
||||
parsed, err := strconv.ParseFloat(any.val, 64)
|
||||
any.err = err
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (any *stringAny) ToString() string {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *stringAny) WriteTo(stream *Stream) {
|
||||
stream.WriteString(any.val)
|
||||
}
|
||||
|
||||
func (any *stringAny) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
type uint64LazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache uint64
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) Parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
}
|
||||
iter.ResetBytes(any.buf)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) fillCache() {
|
||||
if any.err != nil {
|
||||
return
|
||||
}
|
||||
iter := any.Parse()
|
||||
any.cache = iter.ReadUint64()
|
||||
if iter.Error != io.EOF {
|
||||
iter.reportError("intLazyAny", "there are bytes left")
|
||||
}
|
||||
any.err = iter.Error
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) LastError() error {
|
||||
return any.err
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToBool() bool {
|
||||
return any.ToInt64() != 0
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToInt() int {
|
||||
any.fillCache()
|
||||
return int(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToInt32() int32 {
|
||||
any.fillCache()
|
||||
return int32(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToInt64() int64 {
|
||||
any.fillCache()
|
||||
return int64(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToUint() uint {
|
||||
any.fillCache()
|
||||
return uint(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToUint32() uint32 {
|
||||
any.fillCache()
|
||||
return uint32(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToUint64() uint64 {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToFloat32() float32 {
|
||||
any.fillCache()
|
||||
return float32(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToFloat64() float64 {
|
||||
any.fillCache()
|
||||
return float64(any.cache)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) ToString() string {
|
||||
return *(*string)(unsafe.Pointer(&any.buf))
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) WriteTo(stream *Stream) {
|
||||
stream.Write(any.buf)
|
||||
}
|
||||
|
||||
func (any *uint64LazyAny) GetInterface() interface{} {
|
||||
any.fillCache()
|
||||
return any.cache
|
||||
}
|
||||
|
||||
type uint64Any struct {
|
||||
baseAny
|
||||
val uint64
|
||||
}
|
||||
|
||||
func (any *uint64Any) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *uint64Any) ValueType() ValueType {
|
||||
return Number
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToBool() bool {
|
||||
return any.val != 0
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt() int {
|
||||
return int(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt32() int32 {
|
||||
return int32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToInt64() int64 {
|
||||
return int64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint() uint {
|
||||
return uint(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint32() uint32 {
|
||||
return uint32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToUint64() uint64 {
|
||||
return any.val
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToFloat32() float32 {
|
||||
return float32(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToFloat64() float64 {
|
||||
return float64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) ToString() string {
|
||||
return strconv.FormatUint(any.val, 10)
|
||||
}
|
||||
|
||||
func (any *uint64Any) WriteTo(stream *Stream) {
|
||||
stream.WriteUint64(any.val)
|
||||
}
|
||||
|
||||
func (any *uint64Any) Parse() *Iterator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *uint64Any) GetInterface() interface{} {
|
||||
return any.val
|
||||
}
|
279
feature_iter.go
279
feature_iter.go
@ -1,279 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
Invalid ValueType = iota
|
||||
String
|
||||
Number
|
||||
Nil
|
||||
Bool
|
||||
Array
|
||||
Object
|
||||
)
|
||||
|
||||
var hexDigits []byte
|
||||
var valueTypes []ValueType
|
||||
|
||||
func init() {
|
||||
hexDigits = make([]byte, 256)
|
||||
for i := 0; i < len(hexDigits); i++ {
|
||||
hexDigits[i] = 255
|
||||
}
|
||||
for i := '0'; i <= '9'; i++ {
|
||||
hexDigits[i] = byte(i - '0')
|
||||
}
|
||||
for i := 'a'; i <= 'f'; i++ {
|
||||
hexDigits[i] = byte((i - 'a') + 10)
|
||||
}
|
||||
for i := 'A'; i <= 'F'; i++ {
|
||||
hexDigits[i] = byte((i - 'A') + 10)
|
||||
}
|
||||
valueTypes = make([]ValueType, 256)
|
||||
for i := 0; i < len(valueTypes); i++ {
|
||||
valueTypes[i] = Invalid
|
||||
}
|
||||
valueTypes['"'] = String
|
||||
valueTypes['-'] = Number
|
||||
valueTypes['0'] = Number
|
||||
valueTypes['1'] = Number
|
||||
valueTypes['2'] = Number
|
||||
valueTypes['3'] = Number
|
||||
valueTypes['4'] = Number
|
||||
valueTypes['5'] = Number
|
||||
valueTypes['6'] = Number
|
||||
valueTypes['7'] = Number
|
||||
valueTypes['8'] = Number
|
||||
valueTypes['9'] = Number
|
||||
valueTypes['t'] = Bool
|
||||
valueTypes['f'] = Bool
|
||||
valueTypes['n'] = Nil
|
||||
valueTypes['['] = Array
|
||||
valueTypes['{'] = Object
|
||||
}
|
||||
|
||||
// Iterator is a fast and flexible JSON parser
|
||||
type Iterator struct {
|
||||
reader io.Reader
|
||||
buf []byte
|
||||
head int
|
||||
tail int
|
||||
Error error
|
||||
}
|
||||
|
||||
// Create creates an empty Iterator instance
|
||||
func NewIterator() *Iterator {
|
||||
return &Iterator{
|
||||
reader: nil,
|
||||
buf: nil,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses a json buffer in io.Reader into an Iterator instance
|
||||
func Parse(reader io.Reader, bufSize int) *Iterator {
|
||||
return &Iterator{
|
||||
reader: reader,
|
||||
buf: make([]byte, bufSize),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseBytes parses a json byte slice into an Iterator instance
|
||||
func ParseBytes(input []byte) *Iterator {
|
||||
return &Iterator{
|
||||
reader: nil,
|
||||
buf: input,
|
||||
head: 0,
|
||||
tail: len(input),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseString parses a json string into an Iterator instance
|
||||
func ParseString(input string) *Iterator {
|
||||
return ParseBytes([]byte(input))
|
||||
}
|
||||
|
||||
// Reset can reset an Iterator instance for another json buffer in io.Reader
|
||||
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
||||
iter.reader = reader
|
||||
iter.head = 0
|
||||
iter.tail = 0
|
||||
return iter
|
||||
}
|
||||
|
||||
// ResetBytes can reset an Iterator instance for another json byte slice
|
||||
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||
iter.reader = nil
|
||||
iter.Error = nil
|
||||
iter.buf = input
|
||||
iter.head = 0
|
||||
iter.tail = len(input)
|
||||
return iter
|
||||
}
|
||||
|
||||
// WhatIsNext gets ValueType of relatively next json object
|
||||
func (iter *Iterator) WhatIsNext() ValueType {
|
||||
valueType := valueTypes[iter.nextToken()]
|
||||
iter.unreadByte()
|
||||
return valueType
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
}
|
||||
iter.head = i
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (iter *Iterator) nextToken() byte {
|
||||
// a variation of skip whitespaces, returning the next non-whitespace token
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
}
|
||||
iter.head = i + 1
|
||||
return c
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) reportError(operation string, msg string) {
|
||||
if iter.Error != nil {
|
||||
if iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
}
|
||||
peekStart := iter.head - 10
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
iter.Error = fmt.Errorf("%s: %s, parsing %v ...%s... at %s", operation, msg, iter.head,
|
||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
||||
}
|
||||
|
||||
// CurrentBuffer gets current buffer as string
|
||||
func (iter *Iterator) CurrentBuffer() string {
|
||||
peekStart := iter.head - 10
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
return fmt.Sprintf("parsing %v ...|%s|... at %s", iter.head,
|
||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
||||
}
|
||||
|
||||
func (iter *Iterator) readByte() (ret byte) {
|
||||
if iter.head == iter.tail {
|
||||
if iter.loadMore() {
|
||||
ret = iter.buf[iter.head]
|
||||
iter.head++
|
||||
return ret
|
||||
}
|
||||
return 0
|
||||
}
|
||||
ret = iter.buf[iter.head]
|
||||
iter.head++
|
||||
return ret
|
||||
}
|
||||
|
||||
func (iter *Iterator) loadMore() bool {
|
||||
if iter.reader == nil {
|
||||
if iter.Error == nil {
|
||||
iter.Error = io.EOF
|
||||
}
|
||||
return false
|
||||
}
|
||||
for {
|
||||
n, err := iter.reader.Read(iter.buf)
|
||||
if n == 0 {
|
||||
if err != nil {
|
||||
if iter.Error == nil {
|
||||
iter.Error = err
|
||||
}
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
iter.head = 0
|
||||
iter.tail = n
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) unreadByte() {
|
||||
if iter.head == 0 {
|
||||
iter.reportError("unreadByte", "unread too many bytes")
|
||||
return
|
||||
}
|
||||
iter.head--
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) Read() interface{} {
|
||||
valueType := iter.WhatIsNext()
|
||||
switch valueType {
|
||||
case String:
|
||||
return iter.ReadString()
|
||||
case Number:
|
||||
return iter.ReadFloat64()
|
||||
case Nil:
|
||||
iter.skipFixedBytes(4) // null
|
||||
return nil
|
||||
case Bool:
|
||||
return iter.ReadBool()
|
||||
case Array:
|
||||
arr := []interface{}{}
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
arr = append(arr, iter.Read())
|
||||
return true
|
||||
})
|
||||
return arr
|
||||
case Object:
|
||||
obj := map[string]interface{}{}
|
||||
iter.ReadObjectCB(func(Iter *Iterator, field string) bool {
|
||||
obj[field] = iter.Read()
|
||||
return true
|
||||
})
|
||||
return obj
|
||||
default:
|
||||
iter.reportError("Read", fmt.Sprintf("unexpected value type: %v", valueType))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBase64 reads a json object as Base64 in byte slice
|
||||
func (iter *Iterator) ReadBase64() (ret []byte) {
|
||||
src := iter.ReadStringAsSlice()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
b64 := base64.StdEncoding
|
||||
ret = make([]byte, b64.DecodedLen(len(src)))
|
||||
n, err := b64.Decode(ret, src)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
return ret[:n]
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
func (iter *Iterator) ReadArray() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipFixedBytes(3)
|
||||
return false // null
|
||||
case '[':
|
||||
c = iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case ']':
|
||||
return false
|
||||
case ',':
|
||||
return true
|
||||
default:
|
||||
iter.reportError("ReadArray", "expect [ or , or ] or n, but found: " + string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == '[' {
|
||||
c = iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
if !callback(iter) {
|
||||
return false
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
if !callback(iter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipFixedBytes(3)
|
||||
return true // null
|
||||
}
|
||||
iter.reportError("ReadArrayCB", "expect [ or n, but found: " + string([]byte{c}))
|
||||
return false
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var intDigits []int8
|
||||
|
||||
const uint32SafeToMultiply10 = uint32(0xffffffff) / 10 - 1
|
||||
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff) / 10 - 1
|
||||
const int64Max = uint64(0x7fffffffffffffff)
|
||||
const int32Max = uint32(0x7fffffff)
|
||||
const int16Max = uint32(0x7fff)
|
||||
const uint16Max = uint32(0xffff)
|
||||
const int8Max = uint32(0x7fff)
|
||||
const uint8Max = uint32(0xffff)
|
||||
|
||||
func init() {
|
||||
intDigits = make([]int8, 256)
|
||||
for i := 0; i < len(floatDigits); i++ {
|
||||
intDigits[i] = invalidCharForNumber
|
||||
}
|
||||
for i := int8('0'); i <= int8('9'); i++ {
|
||||
intDigits[i] = i - int8('0')
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint() uint {
|
||||
return uint(iter.ReadUint64())
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt() int {
|
||||
return int(iter.ReadInt64())
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt8() (ret int8) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > int8Max + 1 {
|
||||
iter.reportError("ReadInt8", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int8(val)
|
||||
} else {
|
||||
val := iter.readUint32(c)
|
||||
if val > int8Max {
|
||||
iter.reportError("ReadInt8", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int8(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint8() (ret uint8) {
|
||||
val := iter.readUint32(iter.nextToken())
|
||||
if val > uint8Max {
|
||||
iter.reportError("ReadUint8", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return uint8(val)
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt16() (ret int16) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > int16Max + 1 {
|
||||
iter.reportError("ReadInt16", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int16(val)
|
||||
} else {
|
||||
val := iter.readUint32(c)
|
||||
if val > int16Max {
|
||||
iter.reportError("ReadInt16", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int16(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint16() (ret uint16) {
|
||||
val := iter.readUint32(iter.nextToken())
|
||||
if val > uint16Max {
|
||||
iter.reportError("ReadUint16", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return uint16(val)
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt32() (ret int32) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > int32Max + 1 {
|
||||
iter.reportError("ReadInt32", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int32(val)
|
||||
} else {
|
||||
val := iter.readUint32(c)
|
||||
if val > int32Max {
|
||||
iter.reportError("ReadInt32", "overflow: " + strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int32(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint32() (ret uint32) {
|
||||
return iter.readUint32(iter.nextToken())
|
||||
}
|
||||
|
||||
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
iter.reportError("readUint32", "unexpected character: " + string([]byte{byte(ind)}))
|
||||
return
|
||||
}
|
||||
value := uint32(ind)
|
||||
if iter.tail - iter.head > 10 {
|
||||
i := iter.head
|
||||
ind2 := intDigits[iter.buf[i]]
|
||||
if ind2 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value
|
||||
}
|
||||
i++
|
||||
ind3 := intDigits[iter.buf[i]]
|
||||
if ind3 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value * 10 + uint32(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
|
||||
return value * 100 + uint32(ind2) * 10 + uint32(ind3)
|
||||
}
|
||||
i++
|
||||
ind5 := intDigits[iter.buf[i]]
|
||||
if ind5 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value * 1000 + uint32(ind2) * 100 + uint32(ind3) * 10 + uint32(ind4)
|
||||
}
|
||||
i++
|
||||
ind6 := intDigits[iter.buf[i]]
|
||||
if ind6 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value * 10000 + uint32(ind2) * 1000 + uint32(ind3) * 100 + uint32(ind4) * 10 + uint32(ind5)
|
||||
}
|
||||
i++
|
||||
ind7 := intDigits[iter.buf[i]]
|
||||
if ind7 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value * 100000 + uint32(ind2) * 10000 + uint32(ind3) * 1000 + uint32(ind4) * 100 + uint32(ind5) * 10 + uint32(ind6)
|
||||
}
|
||||
i++
|
||||
ind8 := intDigits[iter.buf[i]]
|
||||
if ind8 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value * 1000000 + uint32(ind2) * 100000 + uint32(ind3) * 10000 + uint32(ind4) * 1000 + uint32(ind5) * 100 + uint32(ind6) * 10 + uint32(ind7)
|
||||
}
|
||||
i++
|
||||
ind9 := intDigits[iter.buf[i]]
|
||||
value = value * 10000000 + uint32(ind2) * 1000000 + uint32(ind3) * 100000 + uint32(ind4) * 10000 + uint32(ind5) * 1000 + uint32(ind6) * 100 + uint32(ind7) * 10 + uint32(ind8)
|
||||
iter.head = i
|
||||
if ind9 == invalidCharForNumber {
|
||||
return value
|
||||
}
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value
|
||||
}
|
||||
if value > uint32SafeToMultiply10 {
|
||||
value2 := (value << 3) + (value << 1) + uint32(ind)
|
||||
if value2 < value {
|
||||
iter.reportError("readUint32", "overflow")
|
||||
return
|
||||
} else {
|
||||
value = value2
|
||||
continue
|
||||
}
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint32(ind)
|
||||
}
|
||||
if (!iter.loadMore()) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt64() (ret int64) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint64(iter.readByte())
|
||||
if val > int64Max + 1 {
|
||||
iter.reportError("ReadInt64", "overflow: " + strconv.FormatUint(uint64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int64(val)
|
||||
} else {
|
||||
val := iter.readUint64(c)
|
||||
if val > int64Max {
|
||||
iter.reportError("ReadInt64", "overflow: " + strconv.FormatUint(uint64(val), 10))
|
||||
return
|
||||
}
|
||||
return int64(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint64() uint64 {
|
||||
return iter.readUint64(iter.nextToken())
|
||||
}
|
||||
|
||||
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
iter.reportError("readUint64", "unexpected character: " + string([]byte{byte(ind)}))
|
||||
return
|
||||
}
|
||||
value := uint64(ind)
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
return value
|
||||
}
|
||||
if value > uint64SafeToMultiple10 {
|
||||
value2 := (value << 3) + (value << 1) + uint64(ind)
|
||||
if value2 < value {
|
||||
iter.reportError("readUint64", "overflow")
|
||||
return
|
||||
} else {
|
||||
value = value2
|
||||
continue
|
||||
}
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
}
|
||||
if (!iter.loadMore()) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipFixedBytes(3)
|
||||
return "" // null
|
||||
case '{':
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
return string(iter.readObjectFieldAsBytes())
|
||||
}
|
||||
if c == '}' {
|
||||
return "" // end of object
|
||||
}
|
||||
iter.reportError("ReadObject", `expect " after {`)
|
||||
return
|
||||
case ',':
|
||||
return string(iter.readObjectFieldAsBytes())
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.reportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readFieldHash() int32 {
|
||||
hash := int64(0x811c9dc5)
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
b := iter.buf[i]
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
if b == '"' {
|
||||
iter.head = i+1
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.reportError("readFieldHash", `expect :, but found ` + string([]byte{c}))
|
||||
}
|
||||
return int32(hash)
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("readFieldHash", `incomplete field name`)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.reportError("readFieldHash", `expect ", but found ` + string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
|
||||
func calcHash(str string) int32 {
|
||||
hash := int64(0x811c9dc5)
|
||||
for _, b := range str {
|
||||
hash ^= int64(unicode.ToLower(b))
|
||||
hash *= 0x1000193
|
||||
}
|
||||
return int32(hash)
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := string(iter.readObjectFieldAsBytes())
|
||||
if !callback(iter, field) {
|
||||
return false
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
field := string(iter.readObjectFieldAsBytes())
|
||||
if !callback(iter, field) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if c == '}' {
|
||||
return true
|
||||
}
|
||||
iter.reportError("ReadObjectCB", `expect " after }`)
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipFixedBytes(3)
|
||||
return true // null
|
||||
}
|
||||
iter.reportError("ReadObjectCB", `expect { or n`)
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectStart() bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '}' {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return true
|
||||
} else if c == 'n' {
|
||||
iter.skipFixedBytes(3)
|
||||
return false
|
||||
}
|
||||
iter.reportError("readObjectStart", "expect { or n")
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
|
||||
str := iter.ReadStringAsSlice()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.reportError("readObjectFieldAsBytes", "expect : after object field")
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == nil {
|
||||
return str
|
||||
}
|
||||
return ret
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
func (iter *Iterator) ReadString() (ret string) {
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for i := iter.head ; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
if c == '"' {
|
||||
ret = string(iter.buf[iter.head:i])
|
||||
iter.head = i + 1
|
||||
return ret
|
||||
} else if c == '\\' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iter.readStringSlowPath()
|
||||
} else if c == 'n' {
|
||||
iter.skipFixedBytes(3)
|
||||
return ""
|
||||
}
|
||||
iter.reportError("ReadString", `expects " or n`)
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readStringSlowPath() (ret string) {
|
||||
var str []byte
|
||||
var c byte
|
||||
for iter.Error == nil {
|
||||
c = iter.readByte()
|
||||
if c == '"' {
|
||||
return string(str)
|
||||
}
|
||||
if c == '\\' {
|
||||
c = iter.readByte()
|
||||
switch c {
|
||||
case 'u', 'U':
|
||||
r := iter.readU4()
|
||||
if utf16.IsSurrogate(r) {
|
||||
c = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
if c != '\\' {
|
||||
iter.reportError("ReadString",
|
||||
`expects \u after utf16 surrogate, but \ not found`)
|
||||
return
|
||||
}
|
||||
c = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
if c != 'u' && c != 'U' {
|
||||
iter.reportError("ReadString",
|
||||
`expects \u after utf16 surrogate, but \u not found`)
|
||||
return
|
||||
}
|
||||
r2 := iter.readU4()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
combined := utf16.DecodeRune(r, r2)
|
||||
str = appendRune(str, combined)
|
||||
} else {
|
||||
str = appendRune(str, r)
|
||||
}
|
||||
case '"':
|
||||
str = append(str, '"')
|
||||
case '\\':
|
||||
str = append(str, '\\')
|
||||
case '/':
|
||||
str = append(str, '/')
|
||||
case 'b':
|
||||
str = append(str, '\b')
|
||||
case 'f':
|
||||
str = append(str, '\f')
|
||||
case 'n':
|
||||
str = append(str, '\n')
|
||||
case 'r':
|
||||
str = append(str, '\r')
|
||||
case 't':
|
||||
str = append(str, '\t')
|
||||
default:
|
||||
iter.reportError("ReadString",
|
||||
`invalid escape char after \`)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
str = append(str, c)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
// for: field name, base64, number
|
||||
if iter.buf[i] == '"' {
|
||||
// fast path: reuse the underlying buffer
|
||||
ret = iter.buf[iter.head : i]
|
||||
iter.head = i + 1
|
||||
return ret
|
||||
}
|
||||
}
|
||||
readLen := iter.tail - iter.head
|
||||
copied := make([]byte, readLen, readLen * 2)
|
||||
copy(copied, iter.buf[iter.head:iter.tail])
|
||||
iter.head = iter.tail
|
||||
for iter.Error == nil {
|
||||
c := iter.readByte()
|
||||
if c == '"' {
|
||||
return copied
|
||||
}
|
||||
copied = append(copied, c)
|
||||
}
|
||||
return copied
|
||||
}
|
||||
iter.reportError("ReadString", `expects " or n`)
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readU4() (ret rune) {
|
||||
for i := 0; i < 4; i++ {
|
||||
c := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
if c >= '0' && c <= '9' {
|
||||
ret = ret * 16 + rune(c - '0')
|
||||
} else if c >= 'a' && c <= 'f' {
|
||||
ret = ret * 16 + rune(c - 'a' + 10)
|
||||
} else if c >= 'A' && c <= 'F' {
|
||||
ret = ret * 16 + rune(c - 'A' + 10)
|
||||
} else {
|
||||
iter.reportError("readU4", "expects 0~9 or a~f")
|
||||
return
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
const (
|
||||
t1 = 0x00 // 0000 0000
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
|
||||
maskx = 0x3F // 0011 1111
|
||||
mask2 = 0x1F // 0001 1111
|
||||
mask3 = 0x0F // 0000 1111
|
||||
mask4 = 0x07 // 0000 0111
|
||||
|
||||
rune1Max = 1 << 7 - 1
|
||||
rune2Max = 1 << 11 - 1
|
||||
rune3Max = 1 << 16 - 1
|
||||
|
||||
surrogateMin = 0xD800
|
||||
surrogateMax = 0xDFFF
|
||||
|
||||
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
|
||||
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
|
||||
)
|
||||
|
||||
func appendRune(p []byte, r rune) []byte {
|
||||
// Negative values are erroneous. Making it unsigned addresses the problem.
|
||||
switch i := uint32(r); {
|
||||
case i <= rune1Max:
|
||||
p = append(p, byte(r))
|
||||
return p
|
||||
case i <= rune2Max:
|
||||
p = append(p, t2 | byte(r >> 6))
|
||||
p = append(p, tx | byte(r) & maskx)
|
||||
return p
|
||||
case i > maxRune, surrogateMin <= i && i <= surrogateMax:
|
||||
r = runeError
|
||||
fallthrough
|
||||
case i <= rune3Max:
|
||||
p = append(p, t3 | byte(r >> 12))
|
||||
p = append(p, tx | byte(r >> 6) & maskx)
|
||||
p = append(p, tx | byte(r) & maskx)
|
||||
return p
|
||||
default:
|
||||
p = append(p, t4 | byte(r >> 18))
|
||||
p = append(p, tx | byte(r >> 12) & maskx)
|
||||
p = append(p, tx | byte(r >> 6) & maskx)
|
||||
p = append(p, tx | byte(r) & maskx)
|
||||
return p
|
||||
}
|
||||
}
|
@ -1,532 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
/*
|
||||
Reflection on type to create decoders, which is then cached
|
||||
Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions
|
||||
1. create instance of new value, for example *int will need a int to be allocated
|
||||
2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New
|
||||
3. assignment to map, both key and value will be reflect.Value
|
||||
For a simple struct binding, it will be reflect.Value free and allocation free
|
||||
*/
|
||||
|
||||
type Decoder interface {
|
||||
decode(ptr unsafe.Pointer, iter *Iterator)
|
||||
}
|
||||
|
||||
type Encoder interface {
|
||||
isEmpty(ptr unsafe.Pointer) bool
|
||||
encode(ptr unsafe.Pointer, stream *Stream)
|
||||
encodeInterface(val interface{}, stream *Stream)
|
||||
}
|
||||
|
||||
func writeToStream(val interface{}, stream *Stream, encoder Encoder) {
|
||||
e := (*emptyInterface)(unsafe.Pointer(&val))
|
||||
if reflect.TypeOf(val).Kind() == reflect.Ptr {
|
||||
encoder.encode(unsafe.Pointer(&e.word), stream)
|
||||
} else {
|
||||
encoder.encode(e.word, stream)
|
||||
}
|
||||
}
|
||||
|
||||
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
|
||||
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
|
||||
type ExtensionFunc func(typ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc)
|
||||
|
||||
type funcDecoder struct {
|
||||
fun DecoderFunc
|
||||
}
|
||||
|
||||
func (decoder *funcDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.fun(ptr, iter)
|
||||
}
|
||||
|
||||
type funcEncoder struct {
|
||||
fun EncoderFunc
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
encoder.fun(ptr, stream)
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *funcEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var DECODERS unsafe.Pointer
|
||||
var ENCODERS unsafe.Pointer
|
||||
|
||||
var typeDecoders map[string]Decoder
|
||||
var fieldDecoders map[string]Decoder
|
||||
var typeEncoders map[string]Encoder
|
||||
var fieldEncoders map[string]Encoder
|
||||
var extensions []ExtensionFunc
|
||||
var jsonNumberType reflect.Type
|
||||
var jsonRawMessageType reflect.Type
|
||||
var anyType reflect.Type
|
||||
var marshalerType reflect.Type
|
||||
var unmarshalerType reflect.Type
|
||||
|
||||
func init() {
|
||||
typeDecoders = map[string]Decoder{}
|
||||
fieldDecoders = map[string]Decoder{}
|
||||
typeEncoders = map[string]Encoder{}
|
||||
fieldEncoders = map[string]Encoder{}
|
||||
extensions = []ExtensionFunc{}
|
||||
atomic.StorePointer(&DECODERS, unsafe.Pointer(&map[string]Decoder{}))
|
||||
atomic.StorePointer(&ENCODERS, unsafe.Pointer(&map[string]Encoder{}))
|
||||
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
|
||||
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
|
||||
anyType = reflect.TypeOf((*Any)(nil)).Elem()
|
||||
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
}
|
||||
|
||||
func addDecoderToCache(cacheKey reflect.Type, decoder Decoder) {
|
||||
done := false
|
||||
for !done {
|
||||
ptr := atomic.LoadPointer(&DECODERS)
|
||||
cache := *(*map[reflect.Type]Decoder)(ptr)
|
||||
copied := map[reflect.Type]Decoder{}
|
||||
for k, v := range cache {
|
||||
copied[k] = v
|
||||
}
|
||||
copied[cacheKey] = decoder
|
||||
done = atomic.CompareAndSwapPointer(&DECODERS, ptr, unsafe.Pointer(&copied))
|
||||
}
|
||||
}
|
||||
|
||||
func addEncoderToCache(cacheKey reflect.Type, encoder Encoder) {
|
||||
done := false
|
||||
for !done {
|
||||
ptr := atomic.LoadPointer(&ENCODERS)
|
||||
cache := *(*map[reflect.Type]Encoder)(ptr)
|
||||
copied := map[reflect.Type]Encoder{}
|
||||
for k, v := range cache {
|
||||
copied[k] = v
|
||||
}
|
||||
copied[cacheKey] = encoder
|
||||
done = atomic.CompareAndSwapPointer(&ENCODERS, ptr, unsafe.Pointer(&copied))
|
||||
}
|
||||
}
|
||||
|
||||
func getDecoderFromCache(cacheKey reflect.Type) Decoder {
|
||||
ptr := atomic.LoadPointer(&DECODERS)
|
||||
cache := *(*map[reflect.Type]Decoder)(ptr)
|
||||
return cache[cacheKey]
|
||||
}
|
||||
|
||||
func getEncoderFromCache(cacheKey reflect.Type) Encoder {
|
||||
ptr := atomic.LoadPointer(&ENCODERS)
|
||||
cache := *(*map[reflect.Type]Encoder)(ptr)
|
||||
return cache[cacheKey]
|
||||
}
|
||||
|
||||
// RegisterTypeDecoder can register a type for json object
|
||||
func RegisterTypeDecoder(typ string, fun DecoderFunc) {
|
||||
typeDecoders[typ] = &funcDecoder{fun}
|
||||
}
|
||||
|
||||
// RegisterFieldDecoder can register a type for json field
|
||||
func RegisterFieldDecoder(typ string, field string, fun DecoderFunc) {
|
||||
fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = &funcDecoder{fun}
|
||||
}
|
||||
|
||||
func RegisterTypeEncoder(typ string, fun EncoderFunc) {
|
||||
typeEncoders[typ] = &funcEncoder{fun}
|
||||
}
|
||||
|
||||
func RegisterFieldEncoder(typ string, field string, fun EncoderFunc) {
|
||||
fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = &funcEncoder{fun}
|
||||
}
|
||||
|
||||
// RegisterExtension can register a custom extension
|
||||
func RegisterExtension(extension ExtensionFunc) {
|
||||
extensions = append(extensions, extension)
|
||||
}
|
||||
|
||||
// CleanDecoders cleans decoders registered
|
||||
func CleanDecoders() {
|
||||
typeDecoders = map[string]Decoder{}
|
||||
fieldDecoders = map[string]Decoder{}
|
||||
}
|
||||
|
||||
func CleanEncoders() {
|
||||
typeEncoders = map[string]Encoder{}
|
||||
fieldEncoders = map[string]Encoder{}
|
||||
}
|
||||
|
||||
type optionalDecoder struct {
|
||||
valueType reflect.Type
|
||||
valueDecoder Decoder
|
||||
}
|
||||
|
||||
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)
|
||||
decoder.valueDecoder.decode(unsafe.Pointer(value.Pointer()), iter)
|
||||
*((*uintptr)(ptr)) = value.Pointer()
|
||||
} else {
|
||||
// reuse existing instance
|
||||
decoder.valueDecoder.decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type optionalEncoder struct {
|
||||
valueEncoder Encoder
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
stream.WriteNil()
|
||||
} else {
|
||||
encoder.valueEncoder.encode(*((*unsafe.Pointer)(ptr)), stream)
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *optionalEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
return true
|
||||
} else {
|
||||
return encoder.valueEncoder.isEmpty(*((*unsafe.Pointer)(ptr)))
|
||||
}
|
||||
}
|
||||
|
||||
type placeholderEncoder struct {
|
||||
valueEncoder Encoder
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
encoder.valueEncoder.encode(ptr, stream)
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *placeholderEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return encoder.valueEncoder.isEmpty(ptr)
|
||||
}
|
||||
|
||||
type placeholderDecoder struct {
|
||||
valueDecoder Decoder
|
||||
}
|
||||
|
||||
func (decoder *placeholderDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.valueDecoder.decode(ptr, iter)
|
||||
}
|
||||
|
||||
// emptyInterface is the header for an interface{} value.
|
||||
type emptyInterface struct {
|
||||
typ unsafe.Pointer
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
// emptyInterface is the header for an interface with method (not interface{})
|
||||
type nonEmptyInterface struct {
|
||||
// see ../runtime/iface.go:/Itab
|
||||
itab *struct {
|
||||
ityp unsafe.Pointer // static interface type
|
||||
typ unsafe.Pointer // dynamic concrete type
|
||||
link unsafe.Pointer
|
||||
bad int32
|
||||
unused int32
|
||||
fun [100000]unsafe.Pointer // method table
|
||||
}
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
// Read converts an Iterator instance into go interface, same as json.Unmarshal
|
||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
||||
typ := reflect.TypeOf(obj)
|
||||
cacheKey := typ.Elem()
|
||||
cachedDecoder := getDecoderFromCache(cacheKey)
|
||||
if cachedDecoder == nil {
|
||||
decoder, err := decoderOfType(cacheKey)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
cachedDecoder = decoder
|
||||
addDecoderToCache(cacheKey, decoder)
|
||||
}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&obj))
|
||||
cachedDecoder.decode(e.word, iter)
|
||||
}
|
||||
|
||||
|
||||
func (stream *Stream) WriteVal(val interface{}) {
|
||||
if nil == val {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
typ := reflect.TypeOf(val)
|
||||
cacheKey := typ
|
||||
cachedEncoder := getEncoderFromCache(cacheKey)
|
||||
if cachedEncoder == nil {
|
||||
encoder, err := encoderOfType(cacheKey)
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
return
|
||||
}
|
||||
cachedEncoder = encoder
|
||||
addEncoderToCache(cacheKey, encoder)
|
||||
}
|
||||
cachedEncoder.encodeInterface(val, stream)
|
||||
}
|
||||
|
||||
type prefix string
|
||||
|
||||
func (p prefix) addToDecoder(decoder Decoder, err error) (Decoder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", p, err.Error())
|
||||
}
|
||||
return decoder, err
|
||||
}
|
||||
|
||||
func (p prefix) addToEncoder(encoder Encoder, err error) (Encoder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", p, err.Error())
|
||||
}
|
||||
return encoder, err
|
||||
}
|
||||
|
||||
func decoderOfType(typ reflect.Type) (Decoder, error) {
|
||||
typeName := typ.String()
|
||||
typeDecoder := typeDecoders[typeName]
|
||||
if typeDecoder != nil {
|
||||
return typeDecoder, nil
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typeDecoder := typeDecoders[typ.Elem().String()]
|
||||
if typeDecoder != nil {
|
||||
return &optionalDecoder{typ.Elem(),typeDecoder}, nil
|
||||
}
|
||||
}
|
||||
cacheKey := typ
|
||||
cachedDecoder := getDecoderFromCache(cacheKey)
|
||||
if cachedDecoder != nil {
|
||||
return cachedDecoder, nil
|
||||
}
|
||||
placeholder := &placeholderDecoder{}
|
||||
addDecoderToCache(cacheKey, placeholder)
|
||||
newDecoder, err := createDecoderOfType(typ)
|
||||
placeholder.valueDecoder = newDecoder
|
||||
addDecoderToCache(cacheKey, newDecoder)
|
||||
return newDecoder, err
|
||||
}
|
||||
|
||||
func createDecoderOfType(typ reflect.Type) (Decoder, error) {
|
||||
if typ.AssignableTo(jsonRawMessageType) {
|
||||
return &jsonRawMessageCodec{}, nil
|
||||
}
|
||||
if typ.AssignableTo(jsonNumberType) {
|
||||
return &jsonNumberCodec{}, nil
|
||||
}
|
||||
if typ.ConvertibleTo(unmarshalerType) {
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
return &optionalDecoder{typ, &unmarshalerDecoder{extractInterface(templateInterface)}}, nil
|
||||
}
|
||||
if typ.ConvertibleTo(anyType) {
|
||||
return &anyCodec{}, nil
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
return &stringCodec{}, nil
|
||||
case reflect.Int:
|
||||
return &intCodec{}, nil
|
||||
case reflect.Int8:
|
||||
return &int8Codec{}, nil
|
||||
case reflect.Int16:
|
||||
return &int16Codec{}, nil
|
||||
case reflect.Int32:
|
||||
return &int32Codec{}, nil
|
||||
case reflect.Int64:
|
||||
return &int64Codec{}, nil
|
||||
case reflect.Uint:
|
||||
return &uintCodec{}, nil
|
||||
case reflect.Uint8:
|
||||
return &uint8Codec{}, nil
|
||||
case reflect.Uint16:
|
||||
return &uint16Codec{}, nil
|
||||
case reflect.Uint32:
|
||||
return &uint32Codec{}, nil
|
||||
case reflect.Uint64:
|
||||
return &uint64Codec{}, nil
|
||||
case reflect.Float32:
|
||||
return &float32Codec{}, nil
|
||||
case reflect.Float64:
|
||||
return &float64Codec{}, nil
|
||||
case reflect.Bool:
|
||||
return &boolCodec{}, nil
|
||||
case reflect.Interface:
|
||||
if typ.NumMethod() == 0 {
|
||||
return &emptyInterfaceCodec{}, nil
|
||||
} else {
|
||||
return &nonEmptyInterfaceCodec{}, nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ))
|
||||
case reflect.Slice:
|
||||
return prefix("[slice]").addToDecoder(decoderOfSlice(typ))
|
||||
case reflect.Map:
|
||||
return prefix("[map]").addToDecoder(decoderOfMap(typ))
|
||||
case reflect.Ptr:
|
||||
return prefix("[optional]").addToDecoder(decoderOfOptional(typ))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func encoderOfType(typ reflect.Type) (Encoder, error) {
|
||||
typeName := typ.String()
|
||||
typeEncoder := typeEncoders[typeName]
|
||||
if typeEncoder != nil {
|
||||
return typeEncoder, nil
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typeEncoder := typeEncoders[typ.Elem().String()]
|
||||
if typeEncoder != nil {
|
||||
return &optionalEncoder{typeEncoder}, nil
|
||||
}
|
||||
}
|
||||
cacheKey := typ
|
||||
cachedEncoder := getEncoderFromCache(cacheKey)
|
||||
if cachedEncoder != nil {
|
||||
return cachedEncoder, nil
|
||||
}
|
||||
placeholder := &placeholderEncoder{}
|
||||
addEncoderToCache(cacheKey, placeholder)
|
||||
newEncoder, err := createEncoderOfType(typ)
|
||||
placeholder.valueEncoder = newEncoder
|
||||
addEncoderToCache(cacheKey, newEncoder)
|
||||
return newEncoder, err
|
||||
}
|
||||
|
||||
func createEncoderOfType(typ reflect.Type) (Encoder, error) {
|
||||
if typ.AssignableTo(jsonRawMessageType) {
|
||||
return &jsonRawMessageCodec{}, nil
|
||||
}
|
||||
if typ.AssignableTo(jsonNumberType) {
|
||||
return &jsonNumberCodec{}, nil
|
||||
}
|
||||
if typ.ConvertibleTo(marshalerType) {
|
||||
templateInterface := reflect.New(typ).Elem().Interface()
|
||||
return &marshalerEncoder{extractInterface(templateInterface)}, nil
|
||||
}
|
||||
if typ.ConvertibleTo(anyType) {
|
||||
return &anyCodec{}, nil
|
||||
}
|
||||
kind := typ.Kind()
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return &stringCodec{}, nil
|
||||
case reflect.Int:
|
||||
return &intCodec{}, nil
|
||||
case reflect.Int8:
|
||||
return &int8Codec{}, nil
|
||||
case reflect.Int16:
|
||||
return &int16Codec{}, nil
|
||||
case reflect.Int32:
|
||||
return &int32Codec{}, nil
|
||||
case reflect.Int64:
|
||||
return &int64Codec{}, nil
|
||||
case reflect.Uint:
|
||||
return &uintCodec{}, nil
|
||||
case reflect.Uint8:
|
||||
return &uint8Codec{}, nil
|
||||
case reflect.Uint16:
|
||||
return &uint16Codec{}, nil
|
||||
case reflect.Uint32:
|
||||
return &uint32Codec{}, nil
|
||||
case reflect.Uint64:
|
||||
return &uint64Codec{}, nil
|
||||
case reflect.Float32:
|
||||
return &float32Codec{}, nil
|
||||
case reflect.Float64:
|
||||
return &float64Codec{}, nil
|
||||
case reflect.Bool:
|
||||
return &boolCodec{}, nil
|
||||
case reflect.Interface:
|
||||
if typ.NumMethod() == 0 {
|
||||
return &emptyInterfaceCodec{}, nil
|
||||
} else {
|
||||
return &nonEmptyInterfaceCodec{}, nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ))
|
||||
case reflect.Slice:
|
||||
return prefix("[slice]").addToEncoder(encoderOfSlice(typ))
|
||||
case reflect.Map:
|
||||
return prefix("[map]").addToEncoder(encoderOfMap(typ))
|
||||
case reflect.Ptr:
|
||||
return prefix("[optional]").addToEncoder(encoderOfOptional(typ))
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func decoderOfOptional(typ reflect.Type) (Decoder, error) {
|
||||
elemType := typ.Elem()
|
||||
decoder, err := decoderOfType(elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &optionalDecoder{elemType, decoder}, nil
|
||||
}
|
||||
|
||||
func encoderOfOptional(typ reflect.Type) (Encoder, error) {
|
||||
elemType := typ.Elem()
|
||||
decoder, err := encoderOfType(elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &optionalEncoder{ decoder}, nil
|
||||
}
|
||||
|
||||
func decoderOfMap(typ reflect.Type) (Decoder, error) {
|
||||
decoder, err := decoderOfType(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapInterface := reflect.New(typ).Interface()
|
||||
return &mapDecoder{typ, typ.Elem(), decoder, extractInterface(mapInterface)}, nil
|
||||
}
|
||||
|
||||
func extractInterface(val interface{}) emptyInterface {
|
||||
return *((*emptyInterface)(unsafe.Pointer(&val)))
|
||||
}
|
||||
|
||||
func encoderOfMap(typ reflect.Type) (Encoder, error) {
|
||||
elemType := typ.Elem()
|
||||
encoder, err := encoderOfType(elemType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapInterface := reflect.New(typ).Elem().Interface()
|
||||
if elemType.Kind() == reflect.Interface && elemType.NumMethod() == 0 {
|
||||
return &mapInterfaceEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||
} else {
|
||||
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"reflect"
|
||||
"io"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func decoderOfSlice(typ reflect.Type) (Decoder, error) {
|
||||
decoder, err := decoderOfType(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sliceDecoder{typ, typ.Elem(), decoder}, nil
|
||||
}
|
||||
|
||||
func encoderOfSlice(typ reflect.Type) (Encoder, error) {
|
||||
encoder, err := encoderOfType(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ.Elem().Kind() == reflect.Map {
|
||||
encoder = &optionalEncoder{ encoder}
|
||||
}
|
||||
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
|
||||
}
|
||||
|
||||
type sliceEncoder struct {
|
||||
sliceType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder Encoder
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
if slice.Len == 0 {
|
||||
stream.WriteEmptyArray()
|
||||
return
|
||||
}
|
||||
stream.WriteArrayStart()
|
||||
elemPtr := uintptr(slice.Data)
|
||||
encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream)
|
||||
for i := 1; i < slice.Len; i++ {
|
||||
stream.WriteMore()
|
||||
elemPtr += encoder.elemType.Size()
|
||||
encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
if stream.Error != nil && stream.Error != io.EOF {
|
||||
stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
return slice.Len == 0
|
||||
}
|
||||
|
||||
type sliceDecoder struct {
|
||||
sliceType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemDecoder Decoder
|
||||
}
|
||||
|
||||
// sliceHeader is a safe version of SliceHeader used within this package.
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
func (decoder *sliceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
decoder.doDecode(ptr, iter)
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
slice := (*sliceHeader)(ptr)
|
||||
reuseSlice(slice, decoder.sliceType, 4)
|
||||
if !iter.ReadArray() {
|
||||
return
|
||||
}
|
||||
offset := uintptr(0)
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
|
||||
if !iter.ReadArray() {
|
||||
slice.Len = 1
|
||||
return
|
||||
}
|
||||
offset += decoder.elemType.Size()
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
|
||||
if !iter.ReadArray() {
|
||||
slice.Len = 2
|
||||
return
|
||||
}
|
||||
offset += decoder.elemType.Size()
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
|
||||
if !iter.ReadArray() {
|
||||
slice.Len = 3
|
||||
return
|
||||
}
|
||||
offset += decoder.elemType.Size()
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
|
||||
slice.Len = 4
|
||||
for iter.ReadArray() {
|
||||
growOne(slice, decoder.sliceType, decoder.elemType)
|
||||
offset += decoder.elemType.Size()
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
|
||||
}
|
||||
}
|
||||
|
||||
// grow grows the slice s so that it can hold extra more values, allocating
|
||||
// more capacity if needed. It also returns the old and new slice lengths.
|
||||
func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Type) {
|
||||
newLen := slice.Len + 1
|
||||
if newLen <= slice.Cap {
|
||||
slice.Len = newLen
|
||||
return
|
||||
}
|
||||
newCap := slice.Cap
|
||||
if newCap == 0 {
|
||||
newCap = 1
|
||||
} else {
|
||||
for newCap < newLen {
|
||||
if slice.Len < 1024 {
|
||||
newCap += newCap
|
||||
} else {
|
||||
newCap += newCap / 4
|
||||
}
|
||||
}
|
||||
}
|
||||
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer())
|
||||
// copy old array into new array
|
||||
originalBytesCount := uintptr(slice.Len) * elementType.Size()
|
||||
srcPtr := (*[1 << 30]byte)(slice.Data)
|
||||
dstPtr := (*[1 << 30]byte)(dst)
|
||||
for i := uintptr(0); i < originalBytesCount; i++ {
|
||||
dstPtr[i] = srcPtr[i]
|
||||
}
|
||||
slice.Len = newLen
|
||||
slice.Cap = newCap
|
||||
slice.Data = dst
|
||||
}
|
||||
|
||||
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
|
||||
if expectedCap <= slice.Cap {
|
||||
return
|
||||
}
|
||||
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer())
|
||||
slice.Cap = expectedCap
|
||||
slice.Data = dst
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mapDecoder struct {
|
||||
mapType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemDecoder Decoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
|
||||
mapInterface := decoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface).Elem()
|
||||
if realVal.IsNil() {
|
||||
realVal.Set(reflect.MakeMap(realVal.Type()))
|
||||
}
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
elem := reflect.New(decoder.elemType)
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
|
||||
// to put into map, we have to use reflection
|
||||
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
type mapEncoder struct {
|
||||
mapType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder Encoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
|
||||
stream.WriteObjectStart()
|
||||
for i, key := range realVal.MapKeys() {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(key.String())
|
||||
val := realVal.MapIndex(key).Interface()
|
||||
encoder.elemEncoder.encodeInterface(val, stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *mapEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
return realVal.Len() == 0
|
||||
}
|
||||
|
||||
type mapInterfaceEncoder struct {
|
||||
mapType reflect.Type
|
||||
elemType reflect.Type
|
||||
elemEncoder Encoder
|
||||
mapInterface emptyInterface
|
||||
}
|
||||
|
||||
func (encoder *mapInterfaceEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
|
||||
stream.WriteObjectStart()
|
||||
for i, key := range realVal.MapKeys() {
|
||||
if i != 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(key.String())
|
||||
val := realVal.MapIndex(key).Interface()
|
||||
encoder.elemEncoder.encode(unsafe.Pointer(&val), stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
func (encoder *mapInterfaceEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *mapInterfaceEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
mapInterface := encoder.mapInterface
|
||||
mapInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
|
||||
realVal := reflect.ValueOf(*realInterface)
|
||||
|
||||
return realVal.Len() == 0
|
||||
}
|
@ -1,450 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type stringCodec struct {
|
||||
}
|
||||
|
||||
func (codec *stringCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = iter.ReadString()
|
||||
}
|
||||
|
||||
func (codec *stringCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
stream.WriteString(str)
|
||||
}
|
||||
|
||||
func (encoder *stringCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *stringCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*string)(ptr)) == ""
|
||||
}
|
||||
|
||||
type intCodec struct {
|
||||
}
|
||||
|
||||
func (codec *intCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int)(ptr)) = iter.ReadInt()
|
||||
}
|
||||
|
||||
func (codec *intCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt(*((*int)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *intCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *intCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int8Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int8Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int8)(ptr)) = iter.ReadInt8()
|
||||
}
|
||||
|
||||
func (codec *int8Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt8(*((*int8)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *int8Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *int8Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int8)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int16Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int16Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int16)(ptr)) = iter.ReadInt16()
|
||||
}
|
||||
|
||||
func (codec *int16Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt16(*((*int16)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *int16Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *int16Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int16)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int32Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int32)(ptr)) = iter.ReadInt32()
|
||||
}
|
||||
|
||||
func (codec *int32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt32(*((*int32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *int32Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *int32Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type int64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *int64Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*int64)(ptr)) = iter.ReadInt64()
|
||||
}
|
||||
|
||||
func (codec *int64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteInt64(*((*int64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *int64Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *int64Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*int64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uintCodec struct {
|
||||
}
|
||||
|
||||
func (codec *uintCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint)(ptr)) = iter.ReadUint()
|
||||
}
|
||||
|
||||
func (codec *uintCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint(*((*uint)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *uintCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *uintCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint8Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint8)(ptr)) = iter.ReadUint8()
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint8(*((*uint8)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *uint8Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *uint8Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint8)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint16Codec struct {
|
||||
}
|
||||
|
||||
func (decoder *uint16Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint16)(ptr)) = iter.ReadUint16()
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint16(*((*uint16)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *uint16Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *uint16Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint16)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint32)(ptr)) = iter.ReadUint32()
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint32(*((*uint32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *uint32Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *uint32Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type uint64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*uint64)(ptr)) = iter.ReadUint64()
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteUint64(*((*uint64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *uint64Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *uint64Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*uint64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type float32Codec struct {
|
||||
}
|
||||
|
||||
func (codec *float32Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
||||
}
|
||||
|
||||
func (codec *float32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat32(*((*float32)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *float32Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *float32Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float32)(ptr)) == 0
|
||||
}
|
||||
|
||||
type float64Codec struct {
|
||||
}
|
||||
|
||||
func (codec *float64Codec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||
}
|
||||
|
||||
func (codec *float64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteFloat64(*((*float64)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *float64Codec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *float64Codec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*float64)(ptr)) == 0
|
||||
}
|
||||
|
||||
type boolCodec struct {
|
||||
}
|
||||
|
||||
func (codec *boolCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*bool)(ptr)) = iter.ReadBool()
|
||||
}
|
||||
|
||||
func (codec *boolCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteBool(*((*bool)(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *boolCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return !(*((*bool)(ptr)))
|
||||
}
|
||||
|
||||
type emptyInterfaceCodec struct {
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*interface{})(ptr)) = iter.Read()
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteVal(*((*interface{})(ptr)))
|
||||
}
|
||||
|
||||
func (encoder *emptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
func (codec *emptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return ptr == nil
|
||||
}
|
||||
|
||||
type nonEmptyInterfaceCodec struct {
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
var i interface{}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||
e.typ = nonEmptyInterface.itab.typ
|
||||
e.word = nonEmptyInterface.word
|
||||
iter.ReadVal(&i)
|
||||
nonEmptyInterface.word = e.word
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
var i interface{}
|
||||
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||
e.typ = nonEmptyInterface.itab.typ
|
||||
e.word = nonEmptyInterface.word
|
||||
stream.WriteVal(i)
|
||||
}
|
||||
|
||||
func (encoder *nonEmptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
func (codec *nonEmptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||
return nonEmptyInterface.word == nil
|
||||
}
|
||||
|
||||
type anyCodec struct {
|
||||
}
|
||||
|
||||
func (codec *anyCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*Any)(ptr)) = iter.ReadAny()
|
||||
}
|
||||
|
||||
func (codec *anyCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
(*((*Any)(ptr))).WriteTo(stream)
|
||||
}
|
||||
|
||||
func (encoder *anyCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
(val.(Any)).WriteTo(stream)
|
||||
}
|
||||
|
||||
func (encoder *anyCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return (*((*Any)(ptr))).Size() == 0
|
||||
}
|
||||
|
||||
type jsonNumberCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
|
||||
}
|
||||
|
||||
func (codec *jsonNumberCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteRaw(string(*((*json.Number)(ptr))))
|
||||
}
|
||||
|
||||
func (encoder *jsonNumberCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteRaw(string(val.(json.Number)))
|
||||
}
|
||||
|
||||
func (encoder *jsonNumberCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.Number)(ptr))) == 0
|
||||
}
|
||||
|
||||
type jsonRawMessageCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes())
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
|
||||
}
|
||||
|
||||
func (encoder *jsonRawMessageCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||
stream.WriteRaw(string(val.(json.RawMessage)))
|
||||
}
|
||||
|
||||
func (encoder *jsonRawMessageCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||
return len(*((*json.RawMessage)(ptr))) == 0
|
||||
}
|
||||
|
||||
type stringNumberDecoder struct {
|
||||
elemDecoder Decoder
|
||||
}
|
||||
|
||||
func (decoder *stringNumberDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.reportError("stringNumberDecoder", `expect "`)
|
||||
return
|
||||
}
|
||||
decoder.elemDecoder.decode(ptr, iter)
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
c = iter.readByte()
|
||||
if c != '"' {
|
||||
iter.reportError("stringNumberDecoder", `expect "`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type marshalerEncoder struct {
|
||||
templateInterface emptyInterface
|
||||
}
|
||||
|
||||
func (encoder *marshalerEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
templateInterface := encoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
marshaler := (*realInterface).(json.Marshaler)
|
||||
bytes, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
stream.Error = err
|
||||
} else {
|
||||
stream.Write(bytes)
|
||||
}
|
||||
}
|
||||
func (encoder *marshalerEncoder) encodeInterface(val interface{}, stream *Stream) {
|
||||
writeToStream(val, stream, encoder)
|
||||
}
|
||||
|
||||
func (encoder *marshalerEncoder) isEmpty(ptr unsafe.Pointer) bool {
|
||||
templateInterface := encoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
marshaler := (*realInterface).(json.Marshaler)
|
||||
bytes, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return true
|
||||
} else {
|
||||
return len(bytes) > 0
|
||||
}
|
||||
}
|
||||
|
||||
type unmarshalerDecoder struct {
|
||||
templateInterface emptyInterface
|
||||
}
|
||||
|
||||
func (decoder *unmarshalerDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
templateInterface := decoder.templateInterface
|
||||
templateInterface.word = ptr
|
||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
||||
unmarshaler := (*realInterface).(json.Unmarshaler)
|
||||
bytes := iter.SkipAndReturnBytes()
|
||||
err := unmarshaler.UnmarshalJSON(bytes)
|
||||
if err != nil {
|
||||
iter.reportError("unmarshaler", err.Error())
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,313 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type Stream struct {
|
||||
out io.Writer
|
||||
buf []byte
|
||||
n int
|
||||
Error error
|
||||
indention int
|
||||
IndentionStep int
|
||||
}
|
||||
|
||||
func NewStream(out io.Writer, bufSize int) *Stream {
|
||||
return &Stream{out, make([]byte, bufSize), 0, nil, 0, 0}
|
||||
}
|
||||
|
||||
func (b *Stream) Reset(out io.Writer) {
|
||||
b.out = out
|
||||
b.n = 0
|
||||
}
|
||||
|
||||
// Available returns how many bytes are unused in the buffer.
|
||||
func (b *Stream) Available() int {
|
||||
return len(b.buf) - b.n
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes that have been written into the current buffer.
|
||||
func (b *Stream) Buffered() int {
|
||||
return b.n
|
||||
}
|
||||
|
||||
// Write writes the contents of p into the buffer.
|
||||
// It returns the number of bytes written.
|
||||
// If nn < len(p), it also returns an error explaining
|
||||
// why the write is short.
|
||||
func (b *Stream) Write(p []byte) (nn int, err error) {
|
||||
for len(p) > b.Available() && b.Error == nil {
|
||||
var n int
|
||||
if b.Buffered() == 0 {
|
||||
// Large write, empty buffer.
|
||||
// Write directly from p to avoid copy.
|
||||
n, b.Error = b.out.Write(p)
|
||||
} else {
|
||||
n = copy(b.buf[b.n:], p)
|
||||
b.n += n
|
||||
b.Flush()
|
||||
}
|
||||
nn += n
|
||||
p = p[n:]
|
||||
}
|
||||
if b.Error != nil {
|
||||
return nn, b.Error
|
||||
}
|
||||
n := copy(b.buf[b.n:], p)
|
||||
b.n += n
|
||||
nn += n
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
|
||||
// WriteByte writes a single byte.
|
||||
func (b *Stream) writeByte(c byte) {
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
if b.Available() <= 0 && b.Flush() != nil {
|
||||
return
|
||||
}
|
||||
b.buf[b.n] = c
|
||||
b.n++
|
||||
}
|
||||
|
||||
func (b *Stream) writeTwoBytes(c1 byte, c2 byte) {
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
if b.Available() <= 1 && b.Flush() != nil {
|
||||
return
|
||||
}
|
||||
b.buf[b.n] = c1
|
||||
b.buf[b.n + 1] = c2
|
||||
b.n += 2
|
||||
}
|
||||
|
||||
func (b *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) {
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
if b.Available() <= 2 && b.Flush() != nil {
|
||||
return
|
||||
}
|
||||
b.buf[b.n] = c1
|
||||
b.buf[b.n + 1] = c2
|
||||
b.buf[b.n + 2] = c3
|
||||
b.n += 3
|
||||
}
|
||||
|
||||
func (b *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) {
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
if b.Available() <= 3 && b.Flush() != nil {
|
||||
return
|
||||
}
|
||||
b.buf[b.n] = c1
|
||||
b.buf[b.n + 1] = c2
|
||||
b.buf[b.n + 2] = c3
|
||||
b.buf[b.n + 3] = c4
|
||||
b.n += 4
|
||||
}
|
||||
|
||||
func (b *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) {
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
if b.Available() <= 3 && b.Flush() != nil {
|
||||
return
|
||||
}
|
||||
b.buf[b.n] = c1
|
||||
b.buf[b.n + 1] = c2
|
||||
b.buf[b.n + 2] = c3
|
||||
b.buf[b.n + 3] = c4
|
||||
b.buf[b.n + 4] = c5
|
||||
b.n += 5
|
||||
}
|
||||
|
||||
// Flush writes any buffered data to the underlying io.Writer.
|
||||
func (b *Stream) Flush() error {
|
||||
if b.Error != nil {
|
||||
return b.Error
|
||||
}
|
||||
if b.n == 0 {
|
||||
return nil
|
||||
}
|
||||
n, err := b.out.Write(b.buf[0:b.n])
|
||||
if n < b.n && err == nil {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err != nil {
|
||||
if n > 0 && n < b.n {
|
||||
copy(b.buf[0:b.n - n], b.buf[n:b.n])
|
||||
}
|
||||
b.n -= n
|
||||
b.Error = err
|
||||
return err
|
||||
}
|
||||
b.n = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Stream) WriteRaw(s string) {
|
||||
for len(s) > b.Available() && b.Error == nil {
|
||||
n := copy(b.buf[b.n:], s)
|
||||
b.n += n
|
||||
s = s[n:]
|
||||
b.Flush()
|
||||
}
|
||||
if b.Error != nil {
|
||||
return
|
||||
}
|
||||
n := copy(b.buf[b.n:], s)
|
||||
b.n += n
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteString(s string) {
|
||||
valLen := len(s)
|
||||
toWriteLen := valLen
|
||||
bufLengthMinusTwo := len(stream.buf) - 2 // make room for the quotes
|
||||
if stream.n + toWriteLen > bufLengthMinusTwo {
|
||||
toWriteLen = bufLengthMinusTwo - stream.n
|
||||
}
|
||||
if toWriteLen < 0 {
|
||||
stream.Flush()
|
||||
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
|
||||
i := 0
|
||||
for ; i < toWriteLen; i++ {
|
||||
c := s[i]
|
||||
if c > 31 && c != '"' && c != '\\' {
|
||||
stream.buf[n] = c
|
||||
n++
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if i == valLen {
|
||||
stream.buf[n] = '"'
|
||||
n++
|
||||
stream.n = n
|
||||
return
|
||||
}
|
||||
stream.n = n
|
||||
// for the remaining parts, we process them char by char
|
||||
stream.writeStringSlowPath(s, i, valLen);
|
||||
stream.writeByte('"')
|
||||
}
|
||||
|
||||
func (stream *Stream) writeStringSlowPath(s string, i int, valLen int) {
|
||||
for ; i < valLen; i++ {
|
||||
c := s[i]
|
||||
switch (c) {
|
||||
case '"':
|
||||
stream.writeTwoBytes('\\', '"')
|
||||
case '\\':
|
||||
stream.writeTwoBytes('\\', '\\')
|
||||
case '\b':
|
||||
stream.writeTwoBytes('\\', 'b')
|
||||
case '\f':
|
||||
stream.writeTwoBytes('\\', 'f')
|
||||
case '\n':
|
||||
stream.writeTwoBytes('\\', 'n')
|
||||
case '\r':
|
||||
stream.writeTwoBytes('\\', 'r')
|
||||
case '\t':
|
||||
stream.writeTwoBytes('\\', 't')
|
||||
default:
|
||||
stream.writeByte(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteNil() {
|
||||
stream.writeFourBytes('n', 'u', 'l', 'l')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteTrue() {
|
||||
stream.writeFourBytes('t', 'r', 'u', 'e')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteFalse() {
|
||||
stream.writeFiveBytes('f', 'a', 'l', 's', 'e')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteBool(val bool) {
|
||||
if val {
|
||||
stream.WriteTrue()
|
||||
} else {
|
||||
stream.WriteFalse()
|
||||
}
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteObjectStart() {
|
||||
stream.indention += stream.IndentionStep
|
||||
stream.writeByte('{')
|
||||
stream.writeIndention(0)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteObjectField(field string) {
|
||||
stream.WriteString(field)
|
||||
stream.writeByte(':')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteObjectEnd() {
|
||||
stream.writeIndention(stream.IndentionStep)
|
||||
stream.indention -= stream.IndentionStep
|
||||
stream.writeByte('}')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteEmptyObject() {
|
||||
stream.writeByte('{')
|
||||
stream.writeByte('}')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteMore() {
|
||||
stream.writeByte(',')
|
||||
stream.writeIndention(0)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteArrayStart() {
|
||||
stream.indention += stream.IndentionStep
|
||||
stream.writeByte('[')
|
||||
stream.writeIndention(0)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteEmptyArray() {
|
||||
stream.writeByte('[')
|
||||
stream.writeByte(']')
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteArrayEnd() {
|
||||
stream.writeIndention(stream.IndentionStep)
|
||||
stream.indention -= stream.IndentionStep
|
||||
stream.writeByte(']')
|
||||
}
|
||||
|
||||
func (stream *Stream) writeIndention(delta int) {
|
||||
if (stream.indention == 0) {
|
||||
return
|
||||
}
|
||||
stream.writeByte('\n')
|
||||
toWrite := stream.indention - delta
|
||||
i := 0
|
||||
for {
|
||||
for ; i < toWrite && stream.n < len(stream.buf); i++ {
|
||||
stream.buf[stream.n] = ' '
|
||||
stream.n ++
|
||||
}
|
||||
if i == toWrite {
|
||||
break;
|
||||
} else {
|
||||
stream.Flush()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var POW10 []uint64
|
||||
|
||||
func init() {
|
||||
POW10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000}
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteFloat32(val float32) {
|
||||
stream.WriteRaw(strconv.FormatFloat(float64(val), 'f', -1, 32))
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteFloat32Lossy(val float32) {
|
||||
if val < 0 {
|
||||
stream.writeByte('-')
|
||||
val = -val
|
||||
}
|
||||
if val > 0x4ffffff {
|
||||
stream.WriteRaw(strconv.FormatFloat(float64(val), 'f', -1, 32));
|
||||
return
|
||||
}
|
||||
precision := 6
|
||||
exp := uint64(1000000) // 6
|
||||
lval := uint64(float64(val) * float64(exp) + 0.5)
|
||||
stream.WriteUint64(lval / exp)
|
||||
fval := lval % exp
|
||||
if fval == 0 {
|
||||
return
|
||||
}
|
||||
stream.writeByte('.')
|
||||
if stream.Available() < 10 {
|
||||
stream.Flush()
|
||||
}
|
||||
for p := precision - 1; p > 0 && fval < POW10[p]; p-- {
|
||||
stream.writeByte('0')
|
||||
}
|
||||
stream.WriteUint64(fval)
|
||||
for stream.buf[stream.n - 1] == '0' {
|
||||
stream.n--
|
||||
}
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteFloat64(val float64) {
|
||||
stream.WriteRaw(strconv.FormatFloat(float64(val), 'f', -1, 64))
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteFloat64Lossy(val float64) {
|
||||
if val < 0 {
|
||||
stream.writeByte('-')
|
||||
val = -val
|
||||
}
|
||||
if val > 0x4ffffff {
|
||||
stream.WriteRaw(strconv.FormatFloat(val, 'f', -1, 64));
|
||||
return
|
||||
}
|
||||
precision := 6
|
||||
exp := uint64(1000000) // 6
|
||||
lval := uint64(val * float64(exp) + 0.5)
|
||||
stream.WriteUint64(lval / exp)
|
||||
fval := lval % exp
|
||||
if fval == 0 {
|
||||
return
|
||||
}
|
||||
stream.writeByte('.')
|
||||
if stream.Available() < 10 {
|
||||
stream.Flush()
|
||||
}
|
||||
for p := precision - 1; p > 0 && fval < POW10[p]; p-- {
|
||||
stream.writeByte('0')
|
||||
}
|
||||
stream.WriteUint64(fval)
|
||||
for stream.buf[stream.n - 1] == '0' {
|
||||
stream.n--
|
||||
}
|
||||
}
|
||||
|
||||
func EnableLossyFloatMarshalling() {
|
||||
// for better performance
|
||||
RegisterTypeEncoder("float32", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
val := *((*float32)(ptr))
|
||||
stream.WriteFloat32Lossy(val)
|
||||
})
|
||||
RegisterTypeEncoder("float64", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
val := *((*float64)(ptr))
|
||||
stream.WriteFloat64Lossy(val)
|
||||
})
|
||||
}
|
@ -1,361 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
var digits []uint8
|
||||
var digitTens []uint8
|
||||
var digitOnes []uint8
|
||||
var DIGITS []uint32
|
||||
|
||||
func init() {
|
||||
digits = []uint8{
|
||||
'0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b',
|
||||
'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z',
|
||||
}
|
||||
digitTens = []uint8{
|
||||
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
|
||||
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
|
||||
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
|
||||
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
|
||||
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
|
||||
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
|
||||
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
|
||||
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
|
||||
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
|
||||
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
|
||||
}
|
||||
digitOnes = []uint8{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
}
|
||||
DIGITS = make([]uint32, 1000)
|
||||
for i := uint32(0); i < 1000; i++ {
|
||||
DIGITS[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i % 10 + '0';
|
||||
if i < 10 {
|
||||
DIGITS[i] += 2 << 24
|
||||
} else if i < 100 {
|
||||
DIGITS[i] += 1 << 24
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFirstBuf(buf []byte, v uint32, n int) int {
|
||||
start := v >> 24
|
||||
if start == 0 {
|
||||
buf[n] = byte(v >> 16)
|
||||
n++
|
||||
buf[n] = byte(v >> 8)
|
||||
n++
|
||||
} else if start == 1 {
|
||||
buf[n] = byte(v >> 8)
|
||||
n++
|
||||
}
|
||||
buf[n] = byte(v)
|
||||
n++
|
||||
return n
|
||||
}
|
||||
|
||||
func writeBuf(buf []byte, v uint32, n int) {
|
||||
buf[n] = byte(v >> 16)
|
||||
buf[n + 1] = byte(v >> 8)
|
||||
buf[n + 2] = byte(v)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteUint8(val uint8) {
|
||||
if stream.Available() < 3 {
|
||||
stream.Flush()
|
||||
}
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], stream.n)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteInt8(nval int8) {
|
||||
if stream.Available() < 4 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
var val uint8
|
||||
if (nval < 0) {
|
||||
val = uint8(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint8(nval)
|
||||
}
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteUint16(val uint16) {
|
||||
if stream.Available() < 5 {
|
||||
stream.Flush()
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], stream.n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
n := writeFirstBuf(stream.buf, DIGITS[q1], stream.n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteInt16(nval int16) {
|
||||
if stream.Available() < 6 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
var val uint16
|
||||
if (nval < 0) {
|
||||
val = uint16(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint16(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q1], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteUint32(val uint32) {
|
||||
if stream.Available() < 10 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, DIGITS[q1], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2 * 1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q2], n)
|
||||
} else {
|
||||
r3 := q2 - q3 * 1000
|
||||
stream.buf[n] = byte(q3 + '0')
|
||||
n++
|
||||
writeBuf(stream.buf, DIGITS[r3], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, DIGITS[r2], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 3)
|
||||
stream.n = n + 6
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteInt32(nval int32) {
|
||||
if stream.Available() < 11 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
var val uint32
|
||||
if (nval < 0) {
|
||||
val = uint32(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint32(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, DIGITS[q1], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2 * 1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q2], n)
|
||||
} else {
|
||||
r3 := q2 - q3 * 1000
|
||||
stream.buf[n] = byte(q3 + '0')
|
||||
n++
|
||||
writeBuf(stream.buf, DIGITS[r3], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, DIGITS[r2], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 3)
|
||||
stream.n = n + 6
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteUint64(val uint64) {
|
||||
if stream.Available() < 20 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, DIGITS[q1], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2 * 1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q2], n)
|
||||
writeBuf(stream.buf, DIGITS[r2], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 3)
|
||||
stream.n = n + 6
|
||||
return
|
||||
}
|
||||
r3 := q2 - q3 * 1000
|
||||
q4 := q3 / 1000
|
||||
if q4 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q3], n)
|
||||
writeBuf(stream.buf, DIGITS[r3], n)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 6)
|
||||
stream.n = n + 9
|
||||
return
|
||||
}
|
||||
r4 := q3 - q4 * 1000
|
||||
q5 := q4 / 1000
|
||||
if q5 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q4], n)
|
||||
writeBuf(stream.buf, DIGITS[r4], n)
|
||||
writeBuf(stream.buf, DIGITS[r3], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 6)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 9)
|
||||
stream.n = n + 12
|
||||
return
|
||||
}
|
||||
r5 := q4 - q5 * 1000
|
||||
q6 := q5 / 1000
|
||||
if q6 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q5], n)
|
||||
} else {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q6], n)
|
||||
r6 := q5 - q6 * 1000
|
||||
writeBuf(stream.buf, DIGITS[r6], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, DIGITS[r5], n)
|
||||
writeBuf(stream.buf, DIGITS[r4], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r3], n + 6)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 9)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 12)
|
||||
stream.n = n + 15
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteInt64(nval int64) {
|
||||
if stream.Available() < 20 {
|
||||
stream.Flush()
|
||||
}
|
||||
n := stream.n
|
||||
var val uint64
|
||||
if (nval < 0) {
|
||||
val = uint64(-nval)
|
||||
stream.buf[n] = '-'
|
||||
n++
|
||||
} else {
|
||||
val = uint64(nval)
|
||||
}
|
||||
q1 := val / 1000
|
||||
if q1 == 0 {
|
||||
stream.n = writeFirstBuf(stream.buf, DIGITS[val], n)
|
||||
return
|
||||
}
|
||||
r1 := val - q1 * 1000;
|
||||
q2 := q1 / 1000
|
||||
if q2 == 0 {
|
||||
n := writeFirstBuf(stream.buf, DIGITS[q1], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n)
|
||||
stream.n = n + 3
|
||||
return
|
||||
}
|
||||
r2 := q1 - q2 * 1000
|
||||
q3 := q2 / 1000
|
||||
if q3 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q2], n)
|
||||
writeBuf(stream.buf, DIGITS[r2], n)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 3)
|
||||
stream.n = n + 6
|
||||
return
|
||||
}
|
||||
r3 := q2 - q3 * 1000
|
||||
q4 := q3 / 1000
|
||||
if q4 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q3], n)
|
||||
writeBuf(stream.buf, DIGITS[r3], n)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 6)
|
||||
stream.n = n + 9
|
||||
return
|
||||
}
|
||||
r4 := q3 - q4 * 1000
|
||||
q5 := q4 / 1000
|
||||
if q5 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q4], n)
|
||||
writeBuf(stream.buf, DIGITS[r4], n)
|
||||
writeBuf(stream.buf, DIGITS[r3], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 6)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 9)
|
||||
stream.n = n + 12
|
||||
return
|
||||
}
|
||||
r5 := q4 - q5 * 1000
|
||||
q6 := q5 / 1000
|
||||
if q6 == 0 {
|
||||
n = writeFirstBuf(stream.buf, DIGITS[q5], n)
|
||||
} else {
|
||||
stream.buf[n] = byte(q6 + '0')
|
||||
n++
|
||||
r6 := q5 - q6 * 1000
|
||||
writeBuf(stream.buf, DIGITS[r6], n)
|
||||
n += 3
|
||||
}
|
||||
writeBuf(stream.buf, DIGITS[r5], n)
|
||||
writeBuf(stream.buf, DIGITS[r4], n + 3)
|
||||
writeBuf(stream.buf, DIGITS[r3], n + 6)
|
||||
writeBuf(stream.buf, DIGITS[r2], n + 9)
|
||||
writeBuf(stream.buf, DIGITS[r1], n + 12)
|
||||
stream.n = n + 15
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteInt(val int) {
|
||||
stream.WriteInt64(int64(val))
|
||||
}
|
||||
|
||||
func (stream *Stream) WriteUint(val uint) {
|
||||
stream.WriteUint64(uint64(val))
|
||||
}
|
7
fuzzy_mode_convert_table.md
Normal file
7
fuzzy_mode_convert_table.md
Normal file
@ -0,0 +1,7 @@
|
||||
| json type \ dest type | bool | int | uint | float |string|
|
||||
| --- | --- | --- | --- |--|--|
|
||||
| number | positive => true <br/> negative => true <br/> zero => false| 23.2 => 23 <br/> -32.1 => -32| 12.1 => 12 <br/> -12.1 => 0|as normal|same as origin|
|
||||
| string | empty string => false <br/> string "0" => false <br/> other strings => true | "123.32" => 123 <br/> "-123.4" => -123 <br/> "123.23xxxw" => 123 <br/> "abcde12" => 0 <br/> "-32.1" => -32| 13.2 => 13 <br/> -1.1 => 0 |12.1 => 12.1 <br/> -12.3 => -12.3<br/> 12.4xxa => 12.4 <br/> +1.1e2 =>110 |same as origin|
|
||||
| bool | true => true <br/> false => false| true => 1 <br/> false => 0 | true => 1 <br/> false => 0 |true => 1 <br/>false => 0|true => "true" <br/> false => "false"|
|
||||
| object | true | 0 | 0 |0|originnal json|
|
||||
| array | empty array => false <br/> nonempty array => true| [] => 0 <br/> [1,2] => 1 | [] => 0 <br/> [1,2] => 1 |[] => 0<br/>[1,2] => 1|original json|
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module github.com/json-iterator/go
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/google/gofuzz v1.0.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
|
||||
github.com/modern-go/reflect2 v1.0.2
|
||||
github.com/stretchr/testify v1.8.0
|
||||
)
|
21
go.sum
Normal file
21
go.sum
Normal file
@ -0,0 +1,21 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
349
iter.go
Normal file
349
iter.go
Normal file
@ -0,0 +1,349 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ValueType the type for JSON element
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
// InvalidValue invalid JSON element
|
||||
InvalidValue ValueType = iota
|
||||
// StringValue JSON element "string"
|
||||
StringValue
|
||||
// NumberValue JSON element 100 or 0.10
|
||||
NumberValue
|
||||
// NilValue JSON element null
|
||||
NilValue
|
||||
// BoolValue JSON element true or false
|
||||
BoolValue
|
||||
// ArrayValue JSON element []
|
||||
ArrayValue
|
||||
// ObjectValue JSON element {}
|
||||
ObjectValue
|
||||
)
|
||||
|
||||
var hexDigits []byte
|
||||
var valueTypes []ValueType
|
||||
|
||||
func init() {
|
||||
hexDigits = make([]byte, 256)
|
||||
for i := 0; i < len(hexDigits); i++ {
|
||||
hexDigits[i] = 255
|
||||
}
|
||||
for i := '0'; i <= '9'; i++ {
|
||||
hexDigits[i] = byte(i - '0')
|
||||
}
|
||||
for i := 'a'; i <= 'f'; i++ {
|
||||
hexDigits[i] = byte((i - 'a') + 10)
|
||||
}
|
||||
for i := 'A'; i <= 'F'; i++ {
|
||||
hexDigits[i] = byte((i - 'A') + 10)
|
||||
}
|
||||
valueTypes = make([]ValueType, 256)
|
||||
for i := 0; i < len(valueTypes); i++ {
|
||||
valueTypes[i] = InvalidValue
|
||||
}
|
||||
valueTypes['"'] = StringValue
|
||||
valueTypes['-'] = NumberValue
|
||||
valueTypes['0'] = NumberValue
|
||||
valueTypes['1'] = NumberValue
|
||||
valueTypes['2'] = NumberValue
|
||||
valueTypes['3'] = NumberValue
|
||||
valueTypes['4'] = NumberValue
|
||||
valueTypes['5'] = NumberValue
|
||||
valueTypes['6'] = NumberValue
|
||||
valueTypes['7'] = NumberValue
|
||||
valueTypes['8'] = NumberValue
|
||||
valueTypes['9'] = NumberValue
|
||||
valueTypes['t'] = BoolValue
|
||||
valueTypes['f'] = BoolValue
|
||||
valueTypes['n'] = NilValue
|
||||
valueTypes['['] = ArrayValue
|
||||
valueTypes['{'] = ObjectValue
|
||||
}
|
||||
|
||||
// Iterator is a io.Reader like object, with JSON specific read functions.
|
||||
// Error is not returned as return value, but stored as Error member on this iterator instance.
|
||||
type Iterator struct {
|
||||
cfg *frozenConfig
|
||||
reader io.Reader
|
||||
buf []byte
|
||||
head int
|
||||
tail int
|
||||
depth int
|
||||
captureStartedAt int
|
||||
captured []byte
|
||||
Error error
|
||||
Attachment interface{} // open for customized decoder
|
||||
}
|
||||
|
||||
// NewIterator creates an empty Iterator instance
|
||||
func NewIterator(cfg API) *Iterator {
|
||||
return &Iterator{
|
||||
cfg: cfg.(*frozenConfig),
|
||||
reader: nil,
|
||||
buf: nil,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse creates an Iterator instance from io.Reader
|
||||
func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
|
||||
return &Iterator{
|
||||
cfg: cfg.(*frozenConfig),
|
||||
reader: reader,
|
||||
buf: make([]byte, bufSize),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseBytes creates an Iterator instance from byte array
|
||||
func ParseBytes(cfg API, input []byte) *Iterator {
|
||||
return &Iterator{
|
||||
cfg: cfg.(*frozenConfig),
|
||||
reader: nil,
|
||||
buf: input,
|
||||
head: 0,
|
||||
tail: len(input),
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseString creates an Iterator instance from string
|
||||
func ParseString(cfg API, input string) *Iterator {
|
||||
return ParseBytes(cfg, []byte(input))
|
||||
}
|
||||
|
||||
// Pool returns a pool can provide more iterator with same configuration
|
||||
func (iter *Iterator) Pool() IteratorPool {
|
||||
return iter.cfg
|
||||
}
|
||||
|
||||
// Reset reuse iterator instance by specifying another reader
|
||||
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
||||
iter.reader = reader
|
||||
iter.head = 0
|
||||
iter.tail = 0
|
||||
iter.depth = 0
|
||||
return iter
|
||||
}
|
||||
|
||||
// ResetBytes reuse iterator instance by specifying another byte array as input
|
||||
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||
iter.reader = nil
|
||||
iter.buf = input
|
||||
iter.head = 0
|
||||
iter.tail = len(input)
|
||||
iter.depth = 0
|
||||
return iter
|
||||
}
|
||||
|
||||
// WhatIsNext gets ValueType of relatively next json element
|
||||
func (iter *Iterator) WhatIsNext() ValueType {
|
||||
valueType := valueTypes[iter.nextToken()]
|
||||
iter.unreadByte()
|
||||
return valueType
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
}
|
||||
iter.head = i
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (iter *Iterator) isObjectEnd() bool {
|
||||
c := iter.nextToken()
|
||||
if c == ',' {
|
||||
return false
|
||||
}
|
||||
if c == '}' {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
|
||||
return true
|
||||
}
|
||||
|
||||
func (iter *Iterator) nextToken() byte {
|
||||
// a variation of skip whitespaces, returning the next non-whitespace token
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
}
|
||||
iter.head = i + 1
|
||||
return c
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReportError record a error in iterator instance with current position.
|
||||
func (iter *Iterator) ReportError(operation string, msg string) {
|
||||
if iter.Error != nil {
|
||||
if iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
}
|
||||
peekStart := iter.head - 10
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
peekEnd := iter.head + 10
|
||||
if peekEnd > iter.tail {
|
||||
peekEnd = iter.tail
|
||||
}
|
||||
parsing := string(iter.buf[peekStart:peekEnd])
|
||||
contextStart := iter.head - 50
|
||||
if contextStart < 0 {
|
||||
contextStart = 0
|
||||
}
|
||||
contextEnd := iter.head + 50
|
||||
if contextEnd > iter.tail {
|
||||
contextEnd = iter.tail
|
||||
}
|
||||
context := string(iter.buf[contextStart:contextEnd])
|
||||
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...",
|
||||
operation, msg, iter.head-peekStart, parsing, context)
|
||||
}
|
||||
|
||||
// CurrentBuffer gets current buffer as string for debugging purpose
|
||||
func (iter *Iterator) CurrentBuffer() string {
|
||||
peekStart := iter.head - 10
|
||||
if peekStart < 0 {
|
||||
peekStart = 0
|
||||
}
|
||||
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]))
|
||||
}
|
||||
|
||||
func (iter *Iterator) readByte() (ret byte) {
|
||||
if iter.head == iter.tail {
|
||||
if iter.loadMore() {
|
||||
ret = iter.buf[iter.head]
|
||||
iter.head++
|
||||
return ret
|
||||
}
|
||||
return 0
|
||||
}
|
||||
ret = iter.buf[iter.head]
|
||||
iter.head++
|
||||
return ret
|
||||
}
|
||||
|
||||
func (iter *Iterator) loadMore() bool {
|
||||
if iter.reader == nil {
|
||||
if iter.Error == nil {
|
||||
iter.head = iter.tail
|
||||
iter.Error = io.EOF
|
||||
}
|
||||
return false
|
||||
}
|
||||
if iter.captured != nil {
|
||||
iter.captured = append(iter.captured,
|
||||
iter.buf[iter.captureStartedAt:iter.tail]...)
|
||||
iter.captureStartedAt = 0
|
||||
}
|
||||
for {
|
||||
n, err := iter.reader.Read(iter.buf)
|
||||
if n == 0 {
|
||||
if err != nil {
|
||||
if iter.Error == nil {
|
||||
iter.Error = err
|
||||
}
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
iter.head = 0
|
||||
iter.tail = n
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) unreadByte() {
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
iter.head--
|
||||
return
|
||||
}
|
||||
|
||||
// Read read the next JSON element as generic interface{}.
|
||||
func (iter *Iterator) Read() interface{} {
|
||||
valueType := iter.WhatIsNext()
|
||||
switch valueType {
|
||||
case StringValue:
|
||||
return iter.ReadString()
|
||||
case NumberValue:
|
||||
if iter.cfg.configBeforeFrozen.UseNumber {
|
||||
return json.Number(iter.readNumberAsString())
|
||||
}
|
||||
return iter.ReadFloat64()
|
||||
case NilValue:
|
||||
iter.skipFourBytes('n', 'u', 'l', 'l')
|
||||
return nil
|
||||
case BoolValue:
|
||||
return iter.ReadBool()
|
||||
case ArrayValue:
|
||||
arr := []interface{}{}
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
var elem interface{}
|
||||
iter.ReadVal(&elem)
|
||||
arr = append(arr, elem)
|
||||
return true
|
||||
})
|
||||
return arr
|
||||
case ObjectValue:
|
||||
obj := map[string]interface{}{}
|
||||
iter.ReadMapCB(func(Iter *Iterator, field string) bool {
|
||||
var elem interface{}
|
||||
iter.ReadVal(&elem)
|
||||
obj[field] = elem
|
||||
return true
|
||||
})
|
||||
return obj
|
||||
default:
|
||||
iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
|
||||
const maxDepth = 10000
|
||||
|
||||
func (iter *Iterator) incrementDepth() (success bool) {
|
||||
iter.depth++
|
||||
if iter.depth <= maxDepth {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("incrementDepth", "exceeded max depth")
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) decrementDepth() (success bool) {
|
||||
iter.depth--
|
||||
if iter.depth >= 0 {
|
||||
return true
|
||||
}
|
||||
iter.ReportError("decrementDepth", "unexpected negative nesting")
|
||||
return false
|
||||
}
|
64
iter_array.go
Normal file
64
iter_array.go
Normal file
@ -0,0 +1,64 @@
|
||||
package jsoniter
|
||||
|
||||
// ReadArray read array element, tells if the array has more element to read.
|
||||
func (iter *Iterator) ReadArray() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return false // null
|
||||
case '[':
|
||||
c = iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case ']':
|
||||
return false
|
||||
case ',':
|
||||
return true
|
||||
default:
|
||||
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ReadArrayCB read array with callback
|
||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == '[' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
if !callback(iter) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
if !callback(iter) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != ']' {
|
||||
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c}))
|
||||
return false
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var floatDigits []int8
|
||||
|
||||
const invalidCharForNumber = int8(-1)
|
||||
const endOfNumber = int8(-2)
|
||||
const dotInNumber = int8(-3)
|
||||
@ -29,6 +32,7 @@ func init() {
|
||||
floatDigits['.'] = dotInNumber
|
||||
}
|
||||
|
||||
// ReadBigFloat read big.Float
|
||||
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
||||
str := iter.readNumberAsString()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
@ -46,6 +50,7 @@ func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
||||
return val
|
||||
}
|
||||
|
||||
// ReadBigInt read big.Int
|
||||
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
||||
str := iter.readNumberAsString()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
@ -55,27 +60,54 @@ func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
||||
var success bool
|
||||
ret, success = ret.SetString(str, 10)
|
||||
if !success {
|
||||
iter.reportError("ReadBigInt", "invalid big int")
|
||||
iter.ReportError("ReadBigInt", "invalid big int")
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
//ReadFloat32 read float32
|
||||
func (iter *Iterator) ReadFloat32() (ret float32) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
return -iter.readPositiveFloat32()
|
||||
} else {
|
||||
iter.unreadByte()
|
||||
return iter.readPositiveFloat32()
|
||||
}
|
||||
iter.unreadByte()
|
||||
return iter.readPositiveFloat32()
|
||||
}
|
||||
|
||||
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||
value := uint64(0)
|
||||
c := byte(' ')
|
||||
i := iter.head
|
||||
non_decimal_loop:
|
||||
// first char
|
||||
if i == iter.tail {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
c := iter.buf[i]
|
||||
i++
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
case invalidCharForNumber:
|
||||
return iter.readFloat32SlowPath()
|
||||
case endOfNumber:
|
||||
iter.ReportError("readFloat32", "empty number")
|
||||
return
|
||||
case dotInNumber:
|
||||
iter.ReportError("readFloat32", "leading dot is invalid")
|
||||
return
|
||||
case 0:
|
||||
if i == iter.tail {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
c = iter.buf[i]
|
||||
switch c {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
iter.ReportError("readFloat32", "leading zero is invalid")
|
||||
return
|
||||
}
|
||||
}
|
||||
value := uint64(ind)
|
||||
// chars before dot
|
||||
non_decimal_loop:
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c]
|
||||
@ -91,25 +123,27 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||
if value > uint64SafeToMultiple10 {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind); // value = value * 10 + ind;
|
||||
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
|
||||
}
|
||||
// chars after dot
|
||||
if c == '.' {
|
||||
i++
|
||||
decimalPlaces := 0;
|
||||
decimalPlaces := 0
|
||||
if i == iter.tail {
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c];
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
case endOfNumber:
|
||||
if decimalPlaces > 0 && decimalPlaces < len(POW10) {
|
||||
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
|
||||
iter.head = i
|
||||
return float32(float64(value) / float64(POW10[decimalPlaces]))
|
||||
return float32(float64(value) / float64(pow10[decimalPlaces]))
|
||||
}
|
||||
// too many decimal places
|
||||
return iter.readFloat32SlowPath()
|
||||
case invalidCharForNumber:
|
||||
fallthrough
|
||||
case dotInNumber:
|
||||
return iter.readFloat32SlowPath()
|
||||
case invalidCharForNumber, dotInNumber:
|
||||
return iter.readFloat32SlowPath()
|
||||
}
|
||||
decimalPlaces++
|
||||
@ -125,12 +159,12 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||
func (iter *Iterator) readNumberAsString() (ret string) {
|
||||
strBuf := [16]byte{}
|
||||
str := strBuf[0:0]
|
||||
load_loop:
|
||||
load_loop:
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
str = append(str, c)
|
||||
continue
|
||||
default:
|
||||
@ -146,7 +180,7 @@ func (iter *Iterator) readNumberAsString() (ret string) {
|
||||
return
|
||||
}
|
||||
if len(str) == 0 {
|
||||
iter.reportError("readNumberAsString", "invalid number")
|
||||
iter.ReportError("readNumberAsString", "invalid number")
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&str))
|
||||
}
|
||||
@ -156,6 +190,11 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
errMsg := validateFloat(str)
|
||||
if errMsg != "" {
|
||||
iter.ReportError("readFloat32SlowPath", errMsg)
|
||||
return
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 32)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
@ -164,21 +203,48 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
|
||||
return float32(val)
|
||||
}
|
||||
|
||||
// ReadFloat64 read float64
|
||||
func (iter *Iterator) ReadFloat64() (ret float64) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
return -iter.readPositiveFloat64()
|
||||
} else {
|
||||
iter.unreadByte()
|
||||
return iter.readPositiveFloat64()
|
||||
}
|
||||
iter.unreadByte()
|
||||
return iter.readPositiveFloat64()
|
||||
}
|
||||
|
||||
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||
value := uint64(0)
|
||||
c := byte(' ')
|
||||
i := iter.head
|
||||
non_decimal_loop:
|
||||
// first char
|
||||
if i == iter.tail {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
c := iter.buf[i]
|
||||
i++
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
case invalidCharForNumber:
|
||||
return iter.readFloat64SlowPath()
|
||||
case endOfNumber:
|
||||
iter.ReportError("readFloat64", "empty number")
|
||||
return
|
||||
case dotInNumber:
|
||||
iter.ReportError("readFloat64", "leading dot is invalid")
|
||||
return
|
||||
case 0:
|
||||
if i == iter.tail {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
c = iter.buf[i]
|
||||
switch c {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
iter.ReportError("readFloat64", "leading zero is invalid")
|
||||
return
|
||||
}
|
||||
}
|
||||
value := uint64(ind)
|
||||
// chars before dot
|
||||
non_decimal_loop:
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c]
|
||||
@ -194,25 +260,27 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||
if value > uint64SafeToMultiple10 {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind); // value = value * 10 + ind;
|
||||
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
|
||||
}
|
||||
// chars after dot
|
||||
if c == '.' {
|
||||
i++
|
||||
decimalPlaces := 0;
|
||||
decimalPlaces := 0
|
||||
if i == iter.tail {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
for ; i < iter.tail; i++ {
|
||||
c = iter.buf[i]
|
||||
ind := floatDigits[c];
|
||||
ind := floatDigits[c]
|
||||
switch ind {
|
||||
case endOfNumber:
|
||||
if decimalPlaces > 0 && decimalPlaces < len(POW10) {
|
||||
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
|
||||
iter.head = i
|
||||
return float64(value) / float64(POW10[decimalPlaces])
|
||||
return float64(value) / float64(pow10[decimalPlaces])
|
||||
}
|
||||
// too many decimal places
|
||||
return iter.readFloat64SlowPath()
|
||||
case invalidCharForNumber:
|
||||
fallthrough
|
||||
case dotInNumber:
|
||||
case invalidCharForNumber, dotInNumber:
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
decimalPlaces++
|
||||
@ -220,6 +288,9 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
if value > maxFloat64 {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
return iter.readFloat64SlowPath()
|
||||
@ -230,6 +301,11 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
errMsg := validateFloat(str)
|
||||
if errMsg != "" {
|
||||
iter.ReportError("readFloat64SlowPath", errMsg)
|
||||
return
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
@ -237,3 +313,30 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func validateFloat(str string) string {
|
||||
// strconv.ParseFloat is not validating `1.` or `1.e1`
|
||||
if len(str) == 0 {
|
||||
return "empty number"
|
||||
}
|
||||
if str[0] == '-' {
|
||||
return "-- is not valid"
|
||||
}
|
||||
dotPos := strings.IndexByte(str, '.')
|
||||
if dotPos != -1 {
|
||||
if dotPos == len(str)-1 {
|
||||
return "dot can not be last character"
|
||||
}
|
||||
switch str[dotPos+1] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
return "missing digit after dot"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ReadNumber read json.Number
|
||||
func (iter *Iterator) ReadNumber() (ret json.Number) {
|
||||
return json.Number(iter.readNumberAsString())
|
||||
}
|
346
iter_int.go
Normal file
346
iter_int.go
Normal file
@ -0,0 +1,346 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var intDigits []int8
|
||||
|
||||
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
|
||||
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1
|
||||
const maxFloat64 = 1<<53 - 1
|
||||
|
||||
func init() {
|
||||
intDigits = make([]int8, 256)
|
||||
for i := 0; i < len(intDigits); i++ {
|
||||
intDigits[i] = invalidCharForNumber
|
||||
}
|
||||
for i := int8('0'); i <= int8('9'); i++ {
|
||||
intDigits[i] = i - int8('0')
|
||||
}
|
||||
}
|
||||
|
||||
// ReadUint read uint
|
||||
func (iter *Iterator) ReadUint() uint {
|
||||
if strconv.IntSize == 32 {
|
||||
return uint(iter.ReadUint32())
|
||||
}
|
||||
return uint(iter.ReadUint64())
|
||||
}
|
||||
|
||||
// ReadInt read int
|
||||
func (iter *Iterator) ReadInt() int {
|
||||
if strconv.IntSize == 32 {
|
||||
return int(iter.ReadInt32())
|
||||
}
|
||||
return int(iter.ReadInt64())
|
||||
}
|
||||
|
||||
// ReadInt8 read int8
|
||||
func (iter *Iterator) ReadInt8() (ret int8) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > math.MaxInt8+1 {
|
||||
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int8(val)
|
||||
}
|
||||
val := iter.readUint32(c)
|
||||
if val > math.MaxInt8 {
|
||||
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int8(val)
|
||||
}
|
||||
|
||||
// ReadUint8 read uint8
|
||||
func (iter *Iterator) ReadUint8() (ret uint8) {
|
||||
val := iter.readUint32(iter.nextToken())
|
||||
if val > math.MaxUint8 {
|
||||
iter.ReportError("ReadUint8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return uint8(val)
|
||||
}
|
||||
|
||||
// ReadInt16 read int16
|
||||
func (iter *Iterator) ReadInt16() (ret int16) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > math.MaxInt16+1 {
|
||||
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int16(val)
|
||||
}
|
||||
val := iter.readUint32(c)
|
||||
if val > math.MaxInt16 {
|
||||
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int16(val)
|
||||
}
|
||||
|
||||
// ReadUint16 read uint16
|
||||
func (iter *Iterator) ReadUint16() (ret uint16) {
|
||||
val := iter.readUint32(iter.nextToken())
|
||||
if val > math.MaxUint16 {
|
||||
iter.ReportError("ReadUint16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return uint16(val)
|
||||
}
|
||||
|
||||
// ReadInt32 read int32
|
||||
func (iter *Iterator) ReadInt32() (ret int32) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint32(iter.readByte())
|
||||
if val > math.MaxInt32+1 {
|
||||
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int32(val)
|
||||
}
|
||||
val := iter.readUint32(c)
|
||||
if val > math.MaxInt32 {
|
||||
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||
return
|
||||
}
|
||||
return int32(val)
|
||||
}
|
||||
|
||||
// ReadUint32 read uint32
|
||||
func (iter *Iterator) ReadUint32() (ret uint32) {
|
||||
return iter.readUint32(iter.nextToken())
|
||||
}
|
||||
|
||||
func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
iter.assertInteger()
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
iter.ReportError("readUint32", "unexpected character: "+string([]byte{byte(ind)}))
|
||||
return
|
||||
}
|
||||
value := uint32(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 + uint32(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 + uint32(ind2)*10 + uint32(ind3)
|
||||
}
|
||||
i++
|
||||
ind5 := intDigits[iter.buf[i]]
|
||||
if ind5 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4)
|
||||
}
|
||||
i++
|
||||
ind6 := intDigits[iter.buf[i]]
|
||||
if ind6 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5)
|
||||
}
|
||||
i++
|
||||
ind7 := intDigits[iter.buf[i]]
|
||||
if ind7 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6)
|
||||
}
|
||||
i++
|
||||
ind8 := intDigits[iter.buf[i]]
|
||||
if ind8 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7)
|
||||
}
|
||||
i++
|
||||
ind9 := intDigits[iter.buf[i]]
|
||||
value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8)
|
||||
iter.head = i
|
||||
if ind9 == invalidCharForNumber {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
if value > uint32SafeToMultiply10 {
|
||||
value2 := (value << 3) + (value << 1) + uint32(ind)
|
||||
if value2 < value {
|
||||
iter.ReportError("readUint32", "overflow")
|
||||
return
|
||||
}
|
||||
value = value2
|
||||
continue
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint32(ind)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadInt64 read int64
|
||||
func (iter *Iterator) ReadInt64() (ret int64) {
|
||||
c := iter.nextToken()
|
||||
if c == '-' {
|
||||
val := iter.readUint64(iter.readByte())
|
||||
if val > math.MaxInt64+1 {
|
||||
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
||||
return
|
||||
}
|
||||
return -int64(val)
|
||||
}
|
||||
val := iter.readUint64(c)
|
||||
if val > math.MaxInt64 {
|
||||
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
||||
return
|
||||
}
|
||||
return int64(val)
|
||||
}
|
||||
|
||||
// ReadUint64 read uint64
|
||||
func (iter *Iterator) ReadUint64() uint64 {
|
||||
return iter.readUint64(iter.nextToken())
|
||||
}
|
||||
|
||||
func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
ind := intDigits[c]
|
||||
if ind == 0 {
|
||||
iter.assertInteger()
|
||||
return 0 // single zero
|
||||
}
|
||||
if ind == invalidCharForNumber {
|
||||
iter.ReportError("readUint64", "unexpected character: "+string([]byte{byte(ind)}))
|
||||
return
|
||||
}
|
||||
value := uint64(ind)
|
||||
if iter.tail-iter.head > 10 {
|
||||
i := iter.head
|
||||
ind2 := intDigits[iter.buf[i]]
|
||||
if ind2 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
i++
|
||||
ind3 := intDigits[iter.buf[i]]
|
||||
if ind3 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10 + uint64(ind2)
|
||||
}
|
||||
//iter.head = i + 1
|
||||
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
|
||||
i++
|
||||
ind4 := intDigits[iter.buf[i]]
|
||||
if ind4 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100 + uint64(ind2)*10 + uint64(ind3)
|
||||
}
|
||||
i++
|
||||
ind5 := intDigits[iter.buf[i]]
|
||||
if ind5 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4)
|
||||
}
|
||||
i++
|
||||
ind6 := intDigits[iter.buf[i]]
|
||||
if ind6 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5)
|
||||
}
|
||||
i++
|
||||
ind7 := intDigits[iter.buf[i]]
|
||||
if ind7 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6)
|
||||
}
|
||||
i++
|
||||
ind8 := intDigits[iter.buf[i]]
|
||||
if ind8 == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7)
|
||||
}
|
||||
i++
|
||||
ind9 := intDigits[iter.buf[i]]
|
||||
value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8)
|
||||
iter.head = i
|
||||
if ind9 == invalidCharForNumber {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
ind = intDigits[iter.buf[i]]
|
||||
if ind == invalidCharForNumber {
|
||||
iter.head = i
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
if value > uint64SafeToMultiple10 {
|
||||
value2 := (value << 3) + (value << 1) + uint64(ind)
|
||||
if value2 < value {
|
||||
iter.ReportError("readUint64", "overflow")
|
||||
return
|
||||
}
|
||||
value = value2
|
||||
continue
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.assertInteger()
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) assertInteger() {
|
||||
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
|
||||
iter.ReportError("assertInteger", "can not decode float as int")
|
||||
}
|
||||
}
|
267
iter_object.go
Normal file
267
iter_object.go
Normal file
@ -0,0 +1,267 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadObject read one field from object.
|
||||
// If object ended, returns empty string.
|
||||
// Otherwise, returns the field name.
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return "" // null
|
||||
case '{':
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
return field
|
||||
}
|
||||
if c == '}' {
|
||||
return "" // end of object
|
||||
}
|
||||
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
|
||||
return
|
||||
case ',':
|
||||
field := iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
return field
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CaseInsensitive
|
||||
func (iter *Iterator) readFieldHash() int64 {
|
||||
hash := int64(0x811c9dc5)
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
b := iter.buf[i]
|
||||
if b == '\\' {
|
||||
iter.head = i
|
||||
for _, b := range iter.readStringSlowPath() {
|
||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
if b == '"' {
|
||||
iter.head = i + 1
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
|
||||
return 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||
b += 'a' - 'A'
|
||||
}
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.ReportError("readFieldHash", `incomplete field name`)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func calcHash(str string, caseSensitive bool) int64 {
|
||||
if !caseSensitive {
|
||||
str = strings.ToLower(str)
|
||||
}
|
||||
hash := int64(0x811c9dc5)
|
||||
for _, b := range []byte(str) {
|
||||
hash ^= int64(b)
|
||||
hash *= 0x1000193
|
||||
}
|
||||
return int64(hash)
|
||||
}
|
||||
|
||||
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
|
||||
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
var field string
|
||||
if c == '{' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field = iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.ReadString()
|
||||
c = iter.nextToken()
|
||||
if c != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == '}' {
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadMapCB read map with callback, the key can be any string
|
||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
if !iter.incrementDepth() {
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
if c == '"' {
|
||||
iter.unreadByte()
|
||||
field := iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
for c == ',' {
|
||||
field = iter.ReadString()
|
||||
if iter.nextToken() != ':' {
|
||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if !callback(iter, field) {
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
c = iter.nextToken()
|
||||
}
|
||||
if c != '}' {
|
||||
iter.ReportError("ReadMapCB", `object not ended with }`)
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
if c == '}' {
|
||||
return iter.decrementDepth()
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c}))
|
||||
iter.decrementDepth()
|
||||
return false
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return true // null
|
||||
}
|
||||
iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectStart() bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '}' {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return true
|
||||
} else if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return false
|
||||
}
|
||||
iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
|
||||
str := iter.ReadStringAsSlice()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == nil {
|
||||
ret = make([]byte, len(str))
|
||||
copy(ret, str)
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == nil {
|
||||
return str
|
||||
}
|
||||
return ret
|
||||
}
|
130
iter_skip.go
Normal file
130
iter_skip.go
Normal file
@ -0,0 +1,130 @@
|
||||
package jsoniter
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ReadNil reads a json object as nil and
|
||||
// returns whether it's a nil or not
|
||||
func (iter *Iterator) ReadNil() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
||||
return true
|
||||
}
|
||||
iter.unreadByte()
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadBool reads a json object as BoolValue
|
||||
func (iter *Iterator) ReadBool() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == 't' {
|
||||
iter.skipThreeBytes('r', 'u', 'e')
|
||||
return true
|
||||
}
|
||||
if c == 'f' {
|
||||
iter.skipFourBytes('a', 'l', 's', 'e')
|
||||
return false
|
||||
}
|
||||
iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
// SkipAndReturnBytes skip next JSON element, and return its content as []byte.
|
||||
// The []byte can be kept, it is a copy of data.
|
||||
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
||||
iter.startCapture(iter.head)
|
||||
iter.Skip()
|
||||
return iter.stopCapture()
|
||||
}
|
||||
|
||||
// SkipAndAppendBytes skips next JSON element and appends its content to
|
||||
// buffer, returning the result.
|
||||
func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte {
|
||||
iter.startCaptureTo(buf, iter.head)
|
||||
iter.Skip()
|
||||
return iter.stopCapture()
|
||||
}
|
||||
|
||||
func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) {
|
||||
if iter.captured != nil {
|
||||
panic("already in capture mode")
|
||||
}
|
||||
iter.captureStartedAt = captureStartedAt
|
||||
iter.captured = buf
|
||||
}
|
||||
|
||||
func (iter *Iterator) startCapture(captureStartedAt int) {
|
||||
iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt)
|
||||
}
|
||||
|
||||
func (iter *Iterator) stopCapture() []byte {
|
||||
if iter.captured == nil {
|
||||
panic("not in capture mode")
|
||||
}
|
||||
captured := iter.captured
|
||||
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
||||
iter.captureStartedAt = -1
|
||||
iter.captured = nil
|
||||
return append(captured, remaining...)
|
||||
}
|
||||
|
||||
// Skip skips a json object and positions to relatively the next json object
|
||||
func (iter *Iterator) Skip() {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case '"':
|
||||
iter.skipString()
|
||||
case 'n':
|
||||
iter.skipThreeBytes('u', 'l', 'l') // null
|
||||
case 't':
|
||||
iter.skipThreeBytes('r', 'u', 'e') // true
|
||||
case 'f':
|
||||
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
||||
case '0':
|
||||
iter.unreadByte()
|
||||
iter.ReadFloat32()
|
||||
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
iter.skipNumber()
|
||||
case '[':
|
||||
iter.skipArray()
|
||||
case '{':
|
||||
iter.skipObject()
|
||||
default:
|
||||
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) {
|
||||
if iter.readByte() != b1 {
|
||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
||||
return
|
||||
}
|
||||
if iter.readByte() != b2 {
|
||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
||||
return
|
||||
}
|
||||
if iter.readByte() != b3 {
|
||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
||||
return
|
||||
}
|
||||
if iter.readByte() != b4 {
|
||||
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) {
|
||||
if iter.readByte() != b1 {
|
||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
||||
return
|
||||
}
|
||||
if iter.readByte() != b2 {
|
||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
||||
return
|
||||
}
|
||||
if iter.readByte() != b3 {
|
||||
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
|
||||
return
|
||||
}
|
||||
}
|
@ -1,66 +1,98 @@
|
||||
//+build jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
||||
import "fmt"
|
||||
// sloppy but faster implementation, do not validate the input json
|
||||
|
||||
// ReadNil reads a json object as nil and
|
||||
// returns whether it's a nil or not
|
||||
func (iter *Iterator) ReadNil() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == 'n' {
|
||||
iter.skipFixedBytes(3) // null
|
||||
return true
|
||||
func (iter *Iterator) skipNumber() {
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
||||
iter.head = i
|
||||
return
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
iter.unreadByte()
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadBool reads a json object as Bool
|
||||
func (iter *Iterator) ReadBool() (ret bool) {
|
||||
c := iter.nextToken()
|
||||
if c == 't' {
|
||||
iter.skipFixedBytes(3)
|
||||
return true
|
||||
}
|
||||
if c == 'f' {
|
||||
iter.skipFixedBytes(4)
|
||||
return false
|
||||
}
|
||||
iter.reportError("ReadBool", "expect t or f")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
||||
if iter.reader != nil {
|
||||
panic("reader input does not support this api")
|
||||
}
|
||||
before := iter.head
|
||||
iter.Skip()
|
||||
after := iter.head
|
||||
return iter.buf[before:after]
|
||||
}
|
||||
|
||||
|
||||
// Skip skips a json object and positions to relatively the next json object
|
||||
func (iter *Iterator) Skip() {
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case '"':
|
||||
iter.skipString()
|
||||
case 'n', 't':
|
||||
iter.skipFixedBytes(3) // null or true
|
||||
case 'f':
|
||||
iter.skipFixedBytes(4) // false
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
iter.skipUntilBreak()
|
||||
case '[':
|
||||
iter.skipArray()
|
||||
case '{':
|
||||
iter.skipObject()
|
||||
default:
|
||||
iter.reportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
||||
func (iter *Iterator) skipArray() {
|
||||
level := 1
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '[': // If open symbol, increase level
|
||||
level++
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
case ']': // If close symbol, increase level
|
||||
level--
|
||||
if !iter.decrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.ReportError("skipObject", "incomplete array")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipObject() {
|
||||
level := 1
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '{': // If open symbol, increase level
|
||||
level++
|
||||
if !iter.incrementDepth() {
|
||||
return
|
||||
}
|
||||
case '}': // If close symbol, increase level
|
||||
level--
|
||||
if !iter.decrementDepth() {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.ReportError("skipObject", "incomplete object")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipString() {
|
||||
@ -68,7 +100,7 @@ func (iter *Iterator) skipString() {
|
||||
end, escaped := iter.findStringEnd()
|
||||
if end == -1 {
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipString", "incomplete string")
|
||||
iter.ReportError("skipString", "incomplete string")
|
||||
return
|
||||
}
|
||||
if escaped {
|
||||
@ -129,90 +161,3 @@ func (iter *Iterator) findStringEnd() (int, bool) {
|
||||
}
|
||||
return -1, true // end with \
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipArray() {
|
||||
level := 1
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '[': // If open symbol, increase level
|
||||
level++
|
||||
case ']': // If close symbol, increase level
|
||||
level--
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipObject", "incomplete array")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipObject() {
|
||||
level := 1
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '{': // If open symbol, increase level
|
||||
level++
|
||||
case '}': // If close symbol, increase level
|
||||
level--
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipObject", "incomplete object")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipUntilBreak() {
|
||||
// true, false, null, number
|
||||
for {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
||||
iter.head = i
|
||||
return
|
||||
}
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipFixedBytes(n int) {
|
||||
iter.head += n;
|
||||
if (iter.head >= iter.tail) {
|
||||
more := iter.head - iter.tail;
|
||||
if !iter.loadMore() {
|
||||
if more > 0 {
|
||||
iter.reportError("skipFixedBytes", "unexpected end");
|
||||
}
|
||||
return
|
||||
}
|
||||
iter.head += more;
|
||||
}
|
||||
}
|
@ -1,62 +1,64 @@
|
||||
//+build jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
)
|
||||
|
||||
func Test_string_end(t *testing.T) {
|
||||
end, escaped := ParseString(`abc"`).findStringEnd()
|
||||
end, escaped := ParseString(ConfigDefault, `abc"`).findStringEnd()
|
||||
if end != 4 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != false {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`abc\\"`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `abc\\"`).findStringEnd()
|
||||
if end != 6 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != true {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`abc\\\\"`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `abc\\\\"`).findStringEnd()
|
||||
if end != 8 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != true {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`abc\"`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `abc\"`).findStringEnd()
|
||||
if end != -1 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != false {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`abc\`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `abc\`).findStringEnd()
|
||||
if end != -1 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != true {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`abc\\`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `abc\\`).findStringEnd()
|
||||
if end != -1 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != false {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`\\`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `\\`).findStringEnd()
|
||||
if end != -1 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
if escaped != false {
|
||||
t.Fatal(escaped)
|
||||
}
|
||||
end, escaped = ParseString(`\`).findStringEnd()
|
||||
end, escaped = ParseString(ConfigDefault, `\`).findStringEnd()
|
||||
if end != -1 {
|
||||
t.Fatal(end)
|
||||
}
|
||||
@ -91,54 +93,54 @@ func (reader *StagedReader) Read(p []byte) (n int, err error) {
|
||||
|
||||
func Test_skip_string(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(`"abc`)
|
||||
iter := ParseString(ConfigDefault, `"abc`)
|
||||
iter.skipString()
|
||||
should.Equal(1, iter.head)
|
||||
iter = ParseString(`\""abc`)
|
||||
iter = ParseString(ConfigDefault, `\""abc`)
|
||||
iter.skipString()
|
||||
should.Equal(3, iter.head)
|
||||
reader := &StagedReader{
|
||||
r1: `abc`,
|
||||
r2: `"`,
|
||||
}
|
||||
iter = Parse(reader, 4096)
|
||||
iter = Parse(ConfigDefault, reader, 4096)
|
||||
iter.skipString()
|
||||
should.Equal(1, iter.head)
|
||||
reader = &StagedReader{
|
||||
r1: `abc`,
|
||||
r2: `1"`,
|
||||
}
|
||||
iter = Parse(reader, 4096)
|
||||
iter = Parse(ConfigDefault, reader, 4096)
|
||||
iter.skipString()
|
||||
should.Equal(2, iter.head)
|
||||
reader = &StagedReader{
|
||||
r1: `abc\`,
|
||||
r2: `"`,
|
||||
}
|
||||
iter = Parse(reader, 4096)
|
||||
iter = Parse(ConfigDefault, reader, 4096)
|
||||
iter.skipString()
|
||||
should.NotNil(iter.Error)
|
||||
reader = &StagedReader{
|
||||
r1: `abc\`,
|
||||
r2: `""`,
|
||||
}
|
||||
iter = Parse(reader, 4096)
|
||||
iter = Parse(ConfigDefault, reader, 4096)
|
||||
iter.skipString()
|
||||
should.Equal(2, iter.head)
|
||||
}
|
||||
|
||||
func Test_skip_object(t *testing.T) {
|
||||
iter := ParseString(`}`)
|
||||
iter := ParseString(ConfigDefault, `}`)
|
||||
iter.skipObject()
|
||||
if iter.head != 1 {
|
||||
t.Fatal(iter.head)
|
||||
}
|
||||
iter = ParseString(`a}`)
|
||||
iter = ParseString(ConfigDefault, `a}`)
|
||||
iter.skipObject()
|
||||
if iter.head != 2 {
|
||||
t.Fatal(iter.head)
|
||||
}
|
||||
iter = ParseString(`{}}a`)
|
||||
iter = ParseString(ConfigDefault, `{}}a`)
|
||||
iter.skipObject()
|
||||
if iter.head != 3 {
|
||||
t.Fatal(iter.head)
|
||||
@ -147,12 +149,12 @@ func Test_skip_object(t *testing.T) {
|
||||
r1: `{`,
|
||||
r2: `}}a`,
|
||||
}
|
||||
iter = Parse(reader, 4096)
|
||||
iter = Parse(ConfigDefault, reader, 4096)
|
||||
iter.skipObject()
|
||||
if iter.head != 2 {
|
||||
t.Fatal(iter.head)
|
||||
}
|
||||
iter = ParseString(`"}"}a`)
|
||||
iter = ParseString(ConfigDefault, `"}"}a`)
|
||||
iter.skipObject()
|
||||
if iter.head != 4 {
|
||||
t.Fatal(iter.head)
|
99
iter_skip_strict.go
Normal file
99
iter_skip_strict.go
Normal file
@ -0,0 +1,99 @@
|
||||
//+build !jsoniter_sloppy
|
||||
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func (iter *Iterator) skipNumber() {
|
||||
if !iter.trySkipNumber() {
|
||||
iter.unreadByte()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return
|
||||
}
|
||||
iter.ReadFloat64()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
iter.Error = nil
|
||||
iter.ReadBigFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) trySkipNumber() bool {
|
||||
dotFound := false
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
switch c {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
case '.':
|
||||
if dotFound {
|
||||
iter.ReportError("validateNumber", `more than one dot found in number`)
|
||||
return true // already failed
|
||||
}
|
||||
if i+1 == iter.tail {
|
||||
return false
|
||||
}
|
||||
c = iter.buf[i+1]
|
||||
switch c {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
iter.ReportError("validateNumber", `missing digit after dot`)
|
||||
return true // already failed
|
||||
}
|
||||
dotFound = true
|
||||
default:
|
||||
switch c {
|
||||
case ',', ']', '}', ' ', '\t', '\n', '\r':
|
||||
if iter.head == i {
|
||||
return false // if - without following digits
|
||||
}
|
||||
iter.head = i
|
||||
return true // must be valid
|
||||
}
|
||||
return false // may be invalid
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipString() {
|
||||
if !iter.trySkipString() {
|
||||
iter.unreadByte()
|
||||
iter.ReadString()
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) trySkipString() bool {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
if c == '"' {
|
||||
iter.head = i + 1
|
||||
return true // valid
|
||||
} else if c == '\\' {
|
||||
return false
|
||||
} else if c < ' ' {
|
||||
iter.ReportError("trySkipString",
|
||||
fmt.Sprintf(`invalid control character found: %d`, c))
|
||||
return true // already failed
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipObject() {
|
||||
iter.unreadByte()
|
||||
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
||||
iter.Skip()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (iter *Iterator) skipArray() {
|
||||
iter.unreadByte()
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
iter.Skip()
|
||||
return true
|
||||
})
|
||||
}
|
215
iter_str.go
Normal file
215
iter_str.go
Normal file
@ -0,0 +1,215 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// ReadString read string from iterator
|
||||
func (iter *Iterator) ReadString() (ret string) {
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
c := iter.buf[i]
|
||||
if c == '"' {
|
||||
ret = string(iter.buf[iter.head:i])
|
||||
iter.head = i + 1
|
||||
return ret
|
||||
} else if c == '\\' {
|
||||
break
|
||||
} else if c < ' ' {
|
||||
iter.ReportError("ReadString",
|
||||
fmt.Sprintf(`invalid control character found: %d`, c))
|
||||
return
|
||||
}
|
||||
}
|
||||
return iter.readStringSlowPath()
|
||||
} else if c == 'n' {
|
||||
iter.skipThreeBytes('u', 'l', 'l')
|
||||
return ""
|
||||
}
|
||||
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readStringSlowPath() (ret string) {
|
||||
var str []byte
|
||||
var c byte
|
||||
for iter.Error == nil {
|
||||
c = iter.readByte()
|
||||
if c == '"' {
|
||||
return string(str)
|
||||
}
|
||||
if c == '\\' {
|
||||
c = iter.readByte()
|
||||
str = iter.readEscapedChar(c, str)
|
||||
} else {
|
||||
str = append(str, c)
|
||||
}
|
||||
}
|
||||
iter.ReportError("readStringSlowPath", "unexpected end of input")
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
|
||||
switch c {
|
||||
case 'u':
|
||||
r := iter.readU4()
|
||||
if utf16.IsSurrogate(r) {
|
||||
c = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return nil
|
||||
}
|
||||
if c != '\\' {
|
||||
iter.unreadByte()
|
||||
str = appendRune(str, r)
|
||||
return str
|
||||
}
|
||||
c = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return nil
|
||||
}
|
||||
if c != 'u' {
|
||||
str = appendRune(str, r)
|
||||
return iter.readEscapedChar(c, str)
|
||||
}
|
||||
r2 := iter.readU4()
|
||||
if iter.Error != nil {
|
||||
return nil
|
||||
}
|
||||
combined := utf16.DecodeRune(r, r2)
|
||||
if combined == '\uFFFD' {
|
||||
str = appendRune(str, r)
|
||||
str = appendRune(str, r2)
|
||||
} else {
|
||||
str = appendRune(str, combined)
|
||||
}
|
||||
} else {
|
||||
str = appendRune(str, r)
|
||||
}
|
||||
case '"':
|
||||
str = append(str, '"')
|
||||
case '\\':
|
||||
str = append(str, '\\')
|
||||
case '/':
|
||||
str = append(str, '/')
|
||||
case 'b':
|
||||
str = append(str, '\b')
|
||||
case 'f':
|
||||
str = append(str, '\f')
|
||||
case 'n':
|
||||
str = append(str, '\n')
|
||||
case 'r':
|
||||
str = append(str, '\r')
|
||||
case 't':
|
||||
str = append(str, '\t')
|
||||
default:
|
||||
iter.ReportError("readEscapedChar",
|
||||
`invalid escape char after \`)
|
||||
return nil
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// ReadStringAsSlice read string from iterator without copying into string form.
|
||||
// The []byte can not be kept, as it will change after next iterator call.
|
||||
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
// require ascii string and no escape
|
||||
// for: field name, base64, number
|
||||
if iter.buf[i] == '"' {
|
||||
// fast path: reuse the underlying buffer
|
||||
ret = iter.buf[iter.head:i]
|
||||
iter.head = i + 1
|
||||
return ret
|
||||
}
|
||||
}
|
||||
readLen := iter.tail - iter.head
|
||||
copied := make([]byte, readLen, readLen*2)
|
||||
copy(copied, iter.buf[iter.head:iter.tail])
|
||||
iter.head = iter.tail
|
||||
for iter.Error == nil {
|
||||
c := iter.readByte()
|
||||
if c == '"' {
|
||||
return copied
|
||||
}
|
||||
copied = append(copied, c)
|
||||
}
|
||||
return copied
|
||||
}
|
||||
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readU4() (ret rune) {
|
||||
for i := 0; i < 4; i++ {
|
||||
c := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
if c >= '0' && c <= '9' {
|
||||
ret = ret*16 + rune(c-'0')
|
||||
} else if c >= 'a' && c <= 'f' {
|
||||
ret = ret*16 + rune(c-'a'+10)
|
||||
} else if c >= 'A' && c <= 'F' {
|
||||
ret = ret*16 + rune(c-'A'+10)
|
||||
} else {
|
||||
iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
const (
|
||||
t1 = 0x00 // 0000 0000
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
|
||||
maskx = 0x3F // 0011 1111
|
||||
mask2 = 0x1F // 0001 1111
|
||||
mask3 = 0x0F // 0000 1111
|
||||
mask4 = 0x07 // 0000 0111
|
||||
|
||||
rune1Max = 1<<7 - 1
|
||||
rune2Max = 1<<11 - 1
|
||||
rune3Max = 1<<16 - 1
|
||||
|
||||
surrogateMin = 0xD800
|
||||
surrogateMax = 0xDFFF
|
||||
|
||||
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
|
||||
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
|
||||
)
|
||||
|
||||
func appendRune(p []byte, r rune) []byte {
|
||||
// Negative values are erroneous. Making it unsigned addresses the problem.
|
||||
switch i := uint32(r); {
|
||||
case i <= rune1Max:
|
||||
p = append(p, byte(r))
|
||||
return p
|
||||
case i <= rune2Max:
|
||||
p = append(p, t2|byte(r>>6))
|
||||
p = append(p, tx|byte(r)&maskx)
|
||||
return p
|
||||
case i > maxRune, surrogateMin <= i && i <= surrogateMax:
|
||||
r = runeError
|
||||
fallthrough
|
||||
case i <= rune3Max:
|
||||
p = append(p, t3|byte(r>>12))
|
||||
p = append(p, tx|byte(r>>6)&maskx)
|
||||
p = append(p, tx|byte(r)&maskx)
|
||||
return p
|
||||
default:
|
||||
p = append(p, t4|byte(r>>18))
|
||||
p = append(p, tx|byte(r>>12)&maskx)
|
||||
p = append(p, tx|byte(r>>6)&maskx)
|
||||
p = append(p, tx|byte(r)&maskx)
|
||||
return p
|
||||
}
|
||||
}
|
18
jsoniter.go
Normal file
18
jsoniter.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Package jsoniter implements encoding and decoding of JSON as defined in
|
||||
// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json.
|
||||
// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter
|
||||
// and variable type declarations (if any).
|
||||
// jsoniter interfaces gives 100% compatibility with code using standard lib.
|
||||
//
|
||||
// "JSON and Go"
|
||||
// (https://golang.org/doc/articles/json_and_go.html)
|
||||
// gives a description of how Marshal/Unmarshal operate
|
||||
// between arbitrary or predefined json objects and bytes,
|
||||
// and it applies to jsoniter.Marshal/Unmarshal as well.
|
||||
//
|
||||
// Besides, jsoniter.Iterator provides a different set of interfaces
|
||||
// iterating given bytes/string/reader
|
||||
// and yielding parsed elements one by one.
|
||||
// This set of interfaces reads input as required and gives
|
||||
// better performance.
|
||||
package jsoniter
|
@ -1,46 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
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_new_encoder(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf1 := &bytes.Buffer{}
|
||||
encoder1 := json.NewEncoder(buf1)
|
||||
encoder1.Encode([]int{1})
|
||||
should.Equal("[1]\n", buf1.String())
|
||||
buf2 := &bytes.Buffer{}
|
||||
encoder2 := NewEncoder(buf2)
|
||||
encoder2.Encode([]int{1})
|
||||
should.Equal("[1]", buf2.String())
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Test_empty_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(`[]`)
|
||||
cont := iter.ReadArray()
|
||||
should.False(cont)
|
||||
iter = ParseString(`[]`)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
should.FailNow("should not call")
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func Test_one_element(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(`[1]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(1, iter.ReadInt())
|
||||
should.False(iter.ReadArray())
|
||||
iter = ParseString(`[1]`)
|
||||
iter.ReadArrayCB(func(iter *Iterator) bool {
|
||||
should.Equal(1, iter.ReadInt())
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func Test_two_elements(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(`[1,2]`)
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(int64(1), iter.ReadInt64())
|
||||
should.True(iter.ReadArray())
|
||||
should.Equal(int64(2), iter.ReadInt64())
|
||||
should.False(iter.ReadArray())
|
||||
iter = ParseString(`[1,2]`)
|
||||
should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
|
||||
}
|
||||
|
||||
func Test_read_empty_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[]")
|
||||
should.Nil(err)
|
||||
should.Equal(0, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_one_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1]")
|
||||
should.Nil(err)
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_two_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1,2]")
|
||||
should.Nil(err)
|
||||
should.Equal(1, any.Get(0).ToInt())
|
||||
should.Equal(2, any.Size())
|
||||
should.True(any.ToBool())
|
||||
should.Equal(1, any.ToInt())
|
||||
}
|
||||
|
||||
func Test_read_array_with_any_iterator(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1,2]")
|
||||
should.Nil(err)
|
||||
var element Any
|
||||
var elements []int
|
||||
for next, hasNext := any.IterateArray(); hasNext; {
|
||||
element, hasNext = next()
|
||||
elements = append(elements, element.ToInt())
|
||||
}
|
||||
should.Equal([]int{1, 2}, elements)
|
||||
}
|
||||
|
||||
func Test_wrap_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap([]int{1,2,3})
|
||||
should.Equal("[1,2,3]", any.ToString())
|
||||
var element Any
|
||||
var elements []int
|
||||
for next, hasNext := any.IterateArray(); hasNext; {
|
||||
element, hasNext = next()
|
||||
elements = append(elements, element.ToInt())
|
||||
}
|
||||
should.Equal([]int{1, 2, 3}, elements)
|
||||
any = Wrap([]int{1,2,3})
|
||||
should.Equal(3, any.Size())
|
||||
any = Wrap([]int{1,2,3})
|
||||
should.Equal(2, any.Get(1).ToInt())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1,[2,3],4]")
|
||||
should.Nil(err)
|
||||
should.Equal(3, any.Get(1,1).ToInt())
|
||||
should.Equal("[1,[2,3],4]", any.ToString())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[[1],[2],[3,4]]")
|
||||
should.Nil(err)
|
||||
should.Equal("[1,2,3]", any.Get('*',0).ToString())
|
||||
}
|
||||
|
||||
func Test_array_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := wrapArray([][]int{
|
||||
[]int{1, 2},
|
||||
[]int{3, 4},
|
||||
[]int{5, 6},
|
||||
})
|
||||
should.Equal("[1,3,5]", any.Get('*',0).ToString())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_get_invalid(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[]")
|
||||
should.Nil(err)
|
||||
should.Equal(Invalid, any.Get(1,1).ValueType())
|
||||
should.NotNil(any.Get(1,1).LastError())
|
||||
should.Equal(Invalid, any.Get("1").ValueType())
|
||||
should.NotNil(any.Get("1").LastError())
|
||||
}
|
||||
|
||||
func Test_array_lazy_any_set(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1,[2,3],4]")
|
||||
should.Nil(err)
|
||||
any.GetArray()[0] = WrapInt64(2)
|
||||
str, err := MarshalToString(any)
|
||||
should.Nil(err)
|
||||
should.Equal("[2,[2,3],4]", str)
|
||||
}
|
||||
|
||||
func Test_invalid_array(t *testing.T) {
|
||||
_, err := UnmarshalAnyFromString("[")
|
||||
if err == nil || err == io.EOF {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_whitespace_in_head(t *testing.T) {
|
||||
iter := ParseString(` [1]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
}
|
||||
if iter.ReadUint64() != 1 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_whitespace_after_array_start(t *testing.T) {
|
||||
iter := ParseString(`[ 1]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
}
|
||||
if iter.ReadUint64() != 1 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_whitespace_before_array_end(t *testing.T) {
|
||||
iter := ParseString(`[1 ]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
}
|
||||
if iter.ReadUint64() != 1 {
|
||||
t.FailNow()
|
||||
}
|
||||
cont = iter.ReadArray()
|
||||
if cont != false {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_whitespace_before_comma(t *testing.T) {
|
||||
iter := ParseString(`[1 ,2]`)
|
||||
cont := iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
}
|
||||
if iter.ReadUint64() != 1 {
|
||||
t.FailNow()
|
||||
}
|
||||
cont = iter.ReadArray()
|
||||
if cont != true {
|
||||
t.FailNow()
|
||||
}
|
||||
if iter.ReadUint64() != 2 {
|
||||
t.FailNow()
|
||||
}
|
||||
cont = iter.ReadArray()
|
||||
if cont != false {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_write_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
buf := &bytes.Buffer{}
|
||||
stream := NewStream(buf, 4096)
|
||||
stream.IndentionStep = 2
|
||||
stream.WriteArrayStart()
|
||||
stream.WriteInt(1)
|
||||
stream.WriteMore()
|
||||
stream.WriteInt(2)
|
||||
stream.WriteArrayEnd()
|
||||
stream.Flush()
|
||||
should.Nil(stream.Error)
|
||||
should.Equal("[\n 1,\n 2\n]", buf.String())
|
||||
}
|
||||
|
||||
func Test_write_val_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []int{1, 2, 3}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal("[1,2,3]", str)
|
||||
}
|
||||
|
||||
func Test_write_val_empty_array(t *testing.T) {
|
||||
should := require.New(t)
|
||||
val := []int{}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Equal("[]", str)
|
||||
}
|
||||
|
||||
func Test_write_array_of_interface_in_struct(t *testing.T) {
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
Field []interface{}
|
||||
Field2 string
|
||||
}
|
||||
val := TestObject{[]interface{}{1, 2}, ""}
|
||||
str, err := MarshalToString(val)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"Field":[1,2]`)
|
||||
should.Contains(str, `"Field2":""`)
|
||||
}
|
||||
|
||||
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 Benchmark_jsoniter_array(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
input := []byte(`[1,2,3,4,5,6,7,8,9]`)
|
||||
iter := ParseBytes(input)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter.ResetBytes(input)
|
||||
for iter.ReadArray() {
|
||||
iter.ReadUint64()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_array(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := []interface{}{}
|
||||
json.Unmarshal([]byte(`[1,2,3]`), &result)
|
||||
}
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
"github.com/json-iterator/go/require"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func Test_customize_type_decoder(t *testing.T) {
|
||||
RegisterTypeDecoder("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 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)
|
||||
RegisterTypeEncoder("time.Time", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
t := *((*time.Time)(ptr))
|
||||
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
|
||||
})
|
||||
defer 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) {
|
||||
should := require.New(t)
|
||||
RegisterTypeEncoder("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
t := *((*[]byte)(ptr))
|
||||
stream.WriteString(string(t))
|
||||
})
|
||||
defer 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)
|
||||
EnableLossyFloatMarshalling()
|
||||
defer CleanEncoders()
|
||||
str, err := 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) {
|
||||
RegisterFieldDecoder("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
})
|
||||
defer CleanDecoders()
|
||||
tom := Tom{}
|
||||
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TestObject1 struct {
|
||||
field1 string
|
||||
}
|
||||
|
||||
func Test_customize_field_by_extension(t *testing.T) {
|
||||
should := require.New(t)
|
||||
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) {
|
||||
if type_.String() == "jsoniter.TestObject1" && field.Name == "field1" {
|
||||
encode := func(ptr unsafe.Pointer, stream *Stream) {
|
||||
str := *((*string)(ptr))
|
||||
val, _ := strconv.Atoi(str)
|
||||
stream.WriteInt(val)
|
||||
}
|
||||
decode := func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
}
|
||||
return []string{"field-1"}, encode, decode
|
||||
}
|
||||
return nil, nil, nil
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
func Test_unexported_fields(t *testing.T) {
|
||||
EnableUnexportedStructFieldsSupport()
|
||||
should := require.New(t)
|
||||
type TestObject struct {
|
||||
field1 string
|
||||
field2 string `json:"field-2"`
|
||||
}
|
||||
obj := TestObject{}
|
||||
obj.field1 = "hello"
|
||||
should.Nil(UnmarshalFromString(`{}`, &obj))
|
||||
should.Equal("hello", obj.field1)
|
||||
should.Nil(UnmarshalFromString(`{"field1": "world", "field-2": "abc"}`, &obj))
|
||||
should.Equal("world", obj.field1)
|
||||
should.Equal("abc", obj.field2)
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Contains(str, `"field-2":"abc"`)
|
||||
}
|
||||
|
||||
type ObjectImplementedMarshaler int
|
||||
|
||||
func (obj *ObjectImplementedMarshaler) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"hello"`), nil
|
||||
}
|
||||
|
||||
func Test_marshaler(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *ObjectImplementedMarshaler
|
||||
}
|
||||
should := require.New(t)
|
||||
val := ObjectImplementedMarshaler(100)
|
||||
obj := TestObject{&val}
|
||||
bytes, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello"}`, string(bytes))
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello"}`, str)
|
||||
}
|
||||
|
||||
func Test_marshaler_and_encoder(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *ObjectImplementedMarshaler
|
||||
}
|
||||
should := require.New(t)
|
||||
RegisterTypeEncoder("jsoniter.ObjectImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
|
||||
stream.WriteString("hello from encoder")
|
||||
})
|
||||
val := ObjectImplementedMarshaler(100)
|
||||
obj := TestObject{&val}
|
||||
bytes, err := json.Marshal(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello"}`, string(bytes))
|
||||
str, err := MarshalToString(obj)
|
||||
should.Nil(err)
|
||||
should.Equal(`{"Field":"hello from encoder"}`, str)
|
||||
}
|
||||
|
||||
type ObjectImplementedUnmarshaler int
|
||||
|
||||
func (obj *ObjectImplementedUnmarshaler) UnmarshalJSON([]byte) error {
|
||||
*obj = 100
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_unmarshaler(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *ObjectImplementedUnmarshaler
|
||||
Field2 string
|
||||
}
|
||||
should := require.New(t)
|
||||
obj := TestObject{}
|
||||
val := ObjectImplementedUnmarshaler(0)
|
||||
obj.Field = &val
|
||||
err := json.Unmarshal([]byte(`{"Field":"hello"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(*obj.Field))
|
||||
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(*obj.Field))
|
||||
}
|
||||
|
||||
func Test_unmarshaler_and_decoder(t *testing.T) {
|
||||
type TestObject struct {
|
||||
Field *ObjectImplementedUnmarshaler
|
||||
Field2 string
|
||||
}
|
||||
should := require.New(t)
|
||||
RegisterTypeDecoder("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":"hello"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(100, int(*obj.Field))
|
||||
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
|
||||
should.Nil(err)
|
||||
should.Equal(10, int(*obj.Field))
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"github.com/json-iterator/go/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) {
|
||||
iter := ParseString(`[0,1,2,3]`)
|
||||
total := 0
|
||||
for iter.ReadArray() {
|
||||
total += iter.ReadInt()
|
||||
}
|
||||
fmt.Println(total)
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
"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)
|
||||
}
|
||||
|
||||
func Test_read_custom_interface(t *testing.T) {
|
||||
should := require.New(t)
|
||||
var val MyInterface
|
||||
RegisterTypeDecoder("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)
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_read_by_one(t *testing.T) {
|
||||
iter := Parse(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)
|
||||
}
|
||||
iter.unreadByte()
|
||||
if iter.Error == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
iter.Error = nil
|
||||
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) {
|
||||
iter := Parse(bytes.NewBufferString("abc"), 2)
|
||||
b := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'a' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
b = iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'b' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
iter.unreadByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
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_until_eof(t *testing.T) {
|
||||
iter := Parse(bytes.NewBufferString("abc"), 2)
|
||||
iter.readByte()
|
||||
iter.readByte()
|
||||
b := iter.readByte()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if b != 'c' {
|
||||
t.Fatal(b)
|
||||
}
|
||||
iter.readByte()
|
||||
if iter.Error != io.EOF {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func Test_large_file(t *testing.T) {
|
||||
// file, err := os.Open("/tmp/large-file.json")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// iter := Parse(file, 4096)
|
||||
// count := 0
|
||||
// for iter.ReadArray() {
|
||||
// iter.Skip()
|
||||
// count++
|
||||
// }
|
||||
// if count != 11351 {
|
||||
// t.Fatal(count)
|
||||
// }
|
||||
//}
|
||||
|
||||
func Benchmark_jsoniter_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
iter := Parse(file, 4096)
|
||||
count := 0
|
||||
for iter.ReadArray() {
|
||||
iter.Skip()
|
||||
count++
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_large_file(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
file, _ := os.Open("/tmp/large-file.json")
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
file.Close()
|
||||
result := []struct{}{}
|
||||
json.Unmarshal(bytes, &result)
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
)
|
||||
|
||||
func Test_read_map(t *testing.T) {
|
||||
should := require.New(t)
|
||||
iter := ParseString(`{"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(`{"hello": "world"}`)
|
||||
m := map[string]interface{}{"1": "2"}
|
||||
iter.ReadVal(&m)
|
||||
should.Equal(map[string]interface{}{"1": "2", "hello": "world"}, m)
|
||||
iter = ParseString(`{"hello": "world"}`)
|
||||
should.Equal(map[string]interface{}{"hello": "world"}, iter.Read())
|
||||
}
|
||||
|
||||
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())
|
||||
any = Wrap(map[string]string{"Field1": "hello"})
|
||||
vals := map[string]string{}
|
||||
var k string
|
||||
var v Any
|
||||
for next, hasNext := any.IterateObject(); hasNext; {
|
||||
k, v, hasNext = next()
|
||||
if v.ValueType() == String {
|
||||
vals[k] = v.ToString()
|
||||
}
|
||||
}
|
||||
should.Equal(map[string]string{"Field1":"hello"}, vals)
|
||||
}
|
||||
|
||||
func Test_map_wrapper_any_get_all(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any := Wrap(map[string][]int{"Field1": []int{1, 2}})
|
||||
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
|
||||
}
|
||||
|
||||
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"])
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package jsoniter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Level1 struct {
|
||||
Hello []Level2
|
||||
}
|
||||
|
||||
type Level2 struct {
|
||||
World string
|
||||
}
|
||||
|
||||
func Test_nested(t *testing.T) {
|
||||
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
||||
l1 := Level1{}
|
||||
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
|
||||
switch l1Field {
|
||||
case "hello":
|
||||
l2Array := []Level2{}
|
||||
for iter.ReadArray() {
|
||||
l2 := Level2{}
|
||||
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
|
||||
switch l2Field {
|
||||
case "world":
|
||||
l2.World = iter.ReadString()
|
||||
default:
|
||||
iter.reportError("bind l2", "unexpected field: "+l2Field)
|
||||
}
|
||||
}
|
||||
l2Array = append(l2Array, l2)
|
||||
}
|
||||
l1.Hello = l2Array
|
||||
default:
|
||||
iter.reportError("bind l1", "unexpected field: "+l1Field)
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(l1, Level1{
|
||||
Hello: []Level2{
|
||||
{World: "value1"},
|
||||
{World: "value2"},
|
||||
},
|
||||
}) {
|
||||
t.Fatal(l1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_nested(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
||||
l1 := Level1{}
|
||||
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
|
||||
switch l1Field {
|
||||
case "hello":
|
||||
l1.Hello = readLevel1Hello(iter)
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readLevel1Hello(iter *Iterator) []Level2 {
|
||||
l2Array := make([]Level2, 0, 2)
|
||||
for iter.ReadArray() {
|
||||
l2 := Level2{}
|
||||
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
|
||||
switch l2Field {
|
||||
case "world":
|
||||
l2.World = iter.ReadString()
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
}
|
||||
l2Array = append(l2Array, l2)
|
||||
}
|
||||
return l2Array
|
||||
}
|
||||
|
||||
func Benchmark_json_nested(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
l1 := Level1{}
|
||||
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user