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

457 Commits

Author SHA1 Message Date
36b14963da #153 fix invalid utf8 using same implementation as the standard library 2017-08-29 23:58:51 +08:00
f706335302 #153 fix critical bug: infinite loop when write string is invalid utf8 2017-08-29 23:39:43 +08:00
2dc0031b26 #152 gofmt 2017-08-25 12:53:23 +08:00
cdbd2ed810 #145 interface {} customizatoin is recursive 2017-08-22 10:39:01 +08:00
39e9d67807 Merge branch 'master' of https://github.com/json-iterator/go 2017-08-22 00:12:18 +08:00
2066b01acb #146 support config TagKey 2017-08-22 00:12:09 +08:00
ac3b3cd160 test []interface{} 2017-08-21 22:43:51 +08:00
887789156a Merge pull request #147 from thockin/output_tests
Add tests for int64
2017-08-11 12:55:48 +08:00
7df5a67d0d Add tests for int64 2017-08-10 20:58:49 -07:00
9c358632dc #144 make []byte support Unmarshaler&Marshaler 2017-08-09 13:59:40 +08:00
1cfa233923 #143 make jsoniter.Number same meaning as json.Number, however UseNumber still returns json.Number. 1.9 alias support should be added later 2017-08-05 07:22:53 +08:00
d249b05a85 rename ValueType, to avoid collision with json.Number 2017-08-05 07:10:15 +08:00
abbd16da6c #140 blind fix 2017-08-02 09:20:43 +08:00
b67201557a avoid gc issue 2017-08-01 08:34:38 +08:00
5124683f24 #140 try fix: maybe memory collected before assigned to existing object graph 2017-07-31 23:24:58 +08:00
4892de725b add ad 2017-07-31 21:49:02 +08:00
34a2174be3 #142 decode struct field should be case insensitiveyet another fix 2017-07-31 21:48:22 +08:00
24ecaff2a1 #142 decode struct field should be case insensitive, the bug only happen for struct with more than 10 fields 2017-07-31 20:50:07 +08:00
c15b4d116c #139 unmarshal non base64 into []byte 2017-07-19 12:04:22 +08:00
12cd299fa8 add benchmark for Skip() 2017-07-19 00:22:41 +08:00
60ba332980 acknowledge @mattn for #138 #137 #136 #135 2017-07-19 00:09:50 +08:00
f705934fbf #138 fix - without following digits; fix 1.e1 2017-07-18 23:48:40 +08:00
17a26a6e20 remove debug print 2017-07-18 23:24:21 +08:00
156284b028 #137 fix unicode surrogate incompatibility 2017-07-18 23:17:52 +08:00
6b6938829d #136 strconv.ParseFloat can not validate 1. , added extra validation for this special case 2017-07-18 22:19:52 +08:00
e066e54964 #135 verify 1e1 and 1.0e1 is handled same as std 2017-07-18 11:28:19 +08:00
18d6ae2668 #135 fix leading zero 2017-07-18 11:23:29 +08:00
c966eaa031 #135 fix double negative 2017-07-18 11:05:39 +08:00
f6da8e62c3 #133 validate json when Skip() 2017-07-18 09:45:25 +08:00
5eded4f6ae implement skip number and string strictly 2017-07-18 09:01:43 +08:00
9b3ec40fd9 #133 fix empty struct skip; fix ] as empty array 2017-07-17 09:09:00 +08:00
0d604da7d7 Merge pull request #134 from thockin/output_tests
Add a fuzz test for non-JSON input
2017-07-17 08:47:36 +08:00
b6ace7d51b Add a fuzz test for non-JSON input 2017-07-16 17:24:46 -07:00
6a4fbb9892 ensure buffer flushed to io.Writer 2017-07-16 10:47:24 +08:00
4ae426c4b7 Merge branch 'master' of https://github.com/json-iterator/go 2017-07-15 18:09:14 +08:00
b46d0a2324 make test faster 2017-07-15 18:09:06 +08:00
8b03604184 Merge pull request #131 from cch123/feature-increase-coverage
update conversion table, add string escape test
2017-07-13 19:01:45 +08:00
93ce14316d increase coverage 2017-07-13 15:32:26 +08:00
779c3e2164 update conversion table 2017-07-13 00:13:04 +08:00
4b33139ad0 #130 loadMore should use iter.captured 2017-07-12 17:56:51 +08:00
dc388588a3 Merge branch 'master' of https://github.com/json-iterator/go 2017-07-12 16:40:14 +08:00
bd4364ab7c #129 fix read map with reader, should use ReadMapCB instead of ReadObjectCB 2017-07-12 16:40:05 +08:00
b9dc3ebda7 Merge pull request #128 from carlcarl/fix-json-use-number
Fix #123, `UseNumber` not works with iterator
2017-07-12 07:04:30 +08:00
90137b4a60 Use readNumberAsString 2017-07-12 00:23:49 +08:00
be9d4ded4f Use json.Number as the return 2017-07-12 00:11:50 +08:00
7b1fd129cf Add test for iterator UseNumber 2017-07-11 23:39:09 +08:00
b91b7ac682 Fix #123, make iterator read int if using number 2017-07-11 22:07:08 +08:00
845d8438db #126 fix space in case map key is sorted 2017-07-11 01:07:18 +08:00
d37197e176 #126 add space between map key and value when MarshalIndent 2017-07-10 22:14:11 +08:00
45c22b130b Merge branch 'master' of https://github.com/json-iterator/go 2017-07-10 15:24:04 +08:00
4a84b0b30e Merge branch 'liggitt-malformed-string-test' 2017-07-10 15:23:52 +08:00
0187038bad check null/true/false 2017-07-10 15:23:35 +08:00
c38e47d169 control character in string is invalid 2017-07-10 15:13:31 +08:00
b27718d16b Merge pull request #125 from liggitt/exponents
Fix exponent parsing
2017-07-10 14:51:10 +08:00
a447a8f797 Add tests for malformed string input 2017-07-10 02:44:15 -04:00
0d6dae80e1 Fix exponent parsing 2017-07-10 02:06:37 -04:00
d336ee6da6 fix build 2017-07-09 16:28:35 +08:00
3606750b83 document public symbols 2017-07-09 16:26:30 +08:00
db3f5046d7 remove GetObject & GetArray from Any 2017-07-09 16:15:45 +08:00
f0487718f6 document public symbols 2017-07-09 16:09:23 +08:00
46574e7d09 document public symbols 2017-07-09 15:23:18 +08:00
3a6ecf051c make receiver names consistent 2017-07-09 15:11:24 +08:00
5862c51768 extract out feature_reflect_struct_decoder 2017-07-09 15:07:53 +08:00
ce479f3476 fix golint: document exported symbols 2017-07-09 14:57:49 +08:00
bede1d7f40 fix build; add document for exported symbols 2017-07-09 14:48:34 +08:00
d3448d3dbd fix golint: document exported symbols 2017-07-09 14:21:12 +08:00
8fbed91768 fix golint: document exported symbols 2017-07-09 14:17:40 +08:00
3b6853d209 fix golint: do not export test types 2017-07-09 14:12:58 +08:00
4351a2e6e9 fix golint: do not export test types 2017-07-09 11:55:58 +08:00
891d33b415 fix golint: do not export test types 2017-07-09 11:40:45 +08:00
ad20f12c34 fix golint: do not export test types 2017-07-09 11:33:03 +08:00
9ecb1fd36d fix go vet 2017-07-09 11:24:26 +08:00
6d0e6f3733 fix go report card 2017-07-09 11:12:37 +08:00
711f836582 fix go report card 2017-07-09 11:10:44 +08:00
37ba1b32b5 Merge branch 'javierprovecho-master' 2017-07-09 01:01:13 +08:00
2c10d8e6bb test(object): add test for ignored field on not valid type 2017-07-08 16:50:11 +02:00
aaf6160146 fix(reflect): don't process ignored struct fields 2017-07-08 16:50:05 +02:00
b1afefe058 Merge pull request #119 from cch123/feature-increase-coverage
user defined simple type test
2017-07-07 21:43:33 +08:00
3bb49c1e47 add type def marshal/unmarshal test 2017-07-07 19:01:53 +08:00
eb68fff85c Merge pull request #118 from cch123/feature-increase-coverage
add int/int8/int32/int64 overflow test
2017-07-07 18:49:35 +08:00
e07a4ca5ec add int/int8/int32/int64 overflow test 2017-07-07 17:14:52 +08:00
dfa4bdf888 merge 2017-07-07 09:14:24 +08:00
b74ffb2e03 import github.com/stretchr/testify/require 2017-07-07 09:13:25 +08:00
a46060dedc Merge pull request #117 from cch123/feature-increase-extra-coverage
increase extra coverage
2017-07-06 21:09:34 +08:00
5eadecbb66 increase extra coverage 2017-07-06 20:44:38 +08:00
5bc013d6a3 merge 2017-07-06 16:06:30 +08:00
f7df62f1b5 #115 check object end 2017-07-06 16:04:52 +08:00
07f423d248 Merge pull request #114 from cch123/feature-increase-coverage
increase reflect object coverage, perhaps need to optimize in the future
2017-07-06 15:44:56 +08:00
d4c0cb2986 increase reflect object coverage, need optimize in the future 2017-07-06 15:31:35 +08:00
84ed6b3caf Merge pull request #113 from cch123/feature-increase-coverage
increase coverage
2017-07-06 12:27:18 +08:00
ee6536c50a increase coverage 2017-07-06 11:44:39 +08:00
b6eb62e96b Merge pull request #112 from cch123/feature-increase-coverage
increase coverage
2017-07-05 20:42:42 +08:00
8675af13bf increase coverage 2017-07-05 20:30:54 +08:00
21ca11f96a Merge pull request #111 from cch123/feature-increase-coverage
fix codecov yaml ignore
2017-07-05 19:24:44 +08:00
dd88d25090 add require 2017-07-05 18:59:28 +08:00
6a289f32c2 fix codecov.yml curl https://codecov.io/validate --data-binary @.codecov.yml 2017-07-05 16:49:42 +08:00
4907dc00f6 change codecov file 2017-07-05 16:41:24 +08:00
2350982504 Merge pull request #109 from cch123/feature-increase-coverage
increase coverage
2017-07-05 16:18:34 +08:00
27725b7139 update codecov.yml 2017-07-05 15:17:39 +08:00
ca6a524d4f add codecov.yml ignore output tests 2017-07-05 14:36:15 +08:00
1de44419ea increase coverage 2017-07-05 13:55:10 +08:00
550531a046 increase coverage 2017-07-05 11:40:20 +08:00
1745078ab7 Merge pull request #108 from cch123/feature-add-string-tests
add string convert tests
2017-07-05 07:20:29 +08:00
6129e85d53 increase coverage 2017-07-05 01:21:33 +08:00
ee3313111c add string tests 2017-07-05 00:39:20 +08:00
4e65952c09 fix float convert 2017-07-05 00:23:00 +08:00
3829a470ae Merge pull request #106 from cch123/feature-add-int-tests
fix negative number to uint
2017-07-04 22:37:09 +08:00
3f35bed884 simplify float convert 2017-07-04 22:28:24 +08:00
8d7efe886c Merge pull request #107 from cch123/feature-add-convert-table-doc
add convert table doc
2017-07-04 21:07:43 +08:00
f245011c7d add any to float 2017-07-04 19:59:34 +08:00
4ea96ac7c3 change all negative convert to uint 0 2017-07-04 18:48:55 +08:00
50beb4f15d update fuzzy convert table 2017-07-04 17:05:39 +08:00
e5d7a65616 add convert table 2017-07-04 16:38:07 +08:00
d7b6b4e0bb add convert table document 2017-07-04 15:29:47 +08:00
712ddb1942 fix negative number to uint 2017-07-04 14:00:24 +08:00
ca8dd93d0b Merge pull request #105 from cch123/feature-add-int-tests
add any to int/uint test
2017-07-04 08:09:48 +08:00
ac8dd56dfb object cannot covert to int/float in php, so change covert result to zero 2017-07-04 00:53:10 +08:00
d8dbf14af4 add array to int 2017-07-04 00:29:19 +08:00
402c6c79e2 Merge branch 'master' of https://github.com/json-iterator/go into feature-add-int-tests 2017-07-04 00:20:16 +08:00
2e10d5fdad add basic int test 2017-07-04 00:19:41 +08:00
f0b07a2313 Merge pull request #104 from cch123/feature-add-bool-convert-test
add bool convert test map
2017-07-03 20:00:29 +08:00
919a2eff5c fix bool test 2017-07-03 19:40:12 +08:00
a743df1b8a add bool convert test map 2017-07-03 19:10:49 +08:00
8700644196 cut 0.9.18 2017-07-03 10:17:02 +08:00
6a7bf91c45 only test against 1.8 2017-07-02 19:26:06 +08:00
e417330822 fuzzy test from 1000 to 100 2017-07-02 18:18:12 +08:00
af876518e1 remove 1.6 and 1.7 2017-07-02 15:25:28 +08:00
21b9254da4 skip t.Run below 1.8 2017-07-02 15:20:18 +08:00
c009421781 fix write float compatibility 2017-07-02 15:11:36 +08:00
c4f54740f7 separate 1.8 tests 2017-07-02 14:27:16 +08:00
368bd0c1d8 fix 1.6 compatibility 2017-07-02 13:22:55 +08:00
11975d2a26 test with more go versions 2017-07-02 13:18:20 +08:00
e7404f3065 add badges 2017-07-02 13:10:00 +08:00
f60a6a17c2 test and cover multiple packages 2017-07-02 12:58:14 +08:00
2dfdcdd9db add travis.yml 2017-07-02 12:09:49 +08:00
496a8ea225 list contributors 2017-07-02 11:59:11 +08:00
ed6c434851 gofmt 2017-07-02 11:56:01 +08:00
bf002a02be #102 create correct type when pointer to Marshaler/Unmarshaler is nil 2017-07-02 11:35:30 +08:00
815aa331a8 #101 checkIsEmpty can not reuse createEncoderOfSimpleType, otherwise it will fail when struct member is not serializable 2017-07-02 11:13:21 +08:00
54ab168362 #99 support uintptr 2017-07-02 11:03:13 +08:00
87d1ea0c60 Merge pull request #100 from thockin/output_tests
Output tests for text marshal and uintptr
2017-07-02 07:41:50 +08:00
6268a1cbc8 add test for uintptr 2017-07-01 10:57:42 -07:00
d56566286c Add tests for omitempty and text methods 2017-07-01 10:28:59 -07:00
f2c50ef73b #97 omit empty behavior should follow the original type 2017-07-02 00:41:23 +08:00
d0c5988985 Merge pull request #98 from thockin/output_tests
Add tests for omitempty and json methods
2017-07-02 00:15:51 +08:00
17eed15fb5 Add tests for omitempty and json methods 2017-07-01 08:26:42 -07:00
e260979001 #95 struct and empty struct is considered as notempty 2017-07-01 11:48:17 +08:00
3cf9b7c253 Merge pull request #96 from thockin/output_tests
Add test for omitempty on struct
2017-07-01 11:33:17 +08:00
800df52ccd Add test for omitempty on struct 2017-06-30 20:32:27 -07:00
e3ba0e7b7e #93 fix omitempty within embedded struct 2017-07-01 09:27:48 +08:00
e366c72b81 Merge pull request #94 from thockin/output_tests
Add test for embedded+omitempty
2017-07-01 09:01:13 +08:00
69551ef38f Add test for embedded+omitempty 2017-06-30 15:16:42 -07:00
abcf9a8d76 fix tests 2017-07-01 00:35:19 +08:00
fa0965a968 #90 trim spaces when UnmarshalJSON 2017-07-01 00:33:42 +08:00
04eae11ba5 #91 fix one ptr embedded struct 2017-07-01 00:09:40 +08:00
ea8fa7cc63 #91 fix embedded and , 2017-07-01 00:00:38 +08:00
6540266aaf Merge pull request #92 from thockin/output_tests
Better tests for field name tags
2017-06-30 23:53:02 +08:00
ddfbb0c62e better tests for field name tags 2017-06-30 08:16:43 -07:00
a3a2d1cd25 #87 fix embedded field sorting order 2017-06-30 14:01:50 +08:00
401a56bc20 #88 #85 support embedded struct with json tags 2017-06-30 13:26:50 +08:00
cfaa11f837 Merge pull request #89 from thockin/output_tests
More output tests
2017-06-30 13:21:54 +08:00
c1411e0ad5 Add test for json tag on embedded field 2017-06-29 22:01:16 -07:00
9ec64591b6 Enhance test for overlap and embedded 2017-06-29 22:01:16 -07:00
b07d1abc4f Add output_test for partial unmarshal 2017-06-29 22:01:14 -07:00
79c4040505 sync up some straggler json_tests 2017-06-29 22:00:39 -07:00
7fa780bd5d Uncommit files accidentally added 2017-06-29 22:00:27 -07:00
7244d730b9 Merge pull request #84 from thockin/output_tests
Output tests for embedded marshalers and arrays
2017-06-29 22:51:46 +08:00
12be6e0d43 Add tests for arrays 2017-06-29 07:25:19 -07:00
3cfe590a13 Add output test for embedded marshalers 2017-06-29 07:14:33 -07:00
678c297af3 #75 support MarshalIndent 2017-06-29 20:48:27 +08:00
1253b8edd3 gofmt 2017-06-29 20:40:25 +08:00
fb382c0ec1 fix -, 2017-06-29 20:40:07 +08:00
09cb1d9236 #81 handle field name conflict properly 2017-06-29 20:34:40 +08:00
e6c24947ee array is just like struct, one element case special for interface{ 2017-06-29 18:58:40 +08:00
a6673c983a slice embedded in struct will not be optimized by interface{} 2017-06-29 18:52:03 +08:00
ec7b40d104 update ignore 2017-06-29 18:45:20 +08:00
84fa033353 #80 fix the case when embedded struct ptr is nil 2017-06-29 18:45:11 +08:00
4e608af2c7 #80 fix embedded builtins 2017-06-29 10:45:29 +08:00
3458ccdb20 Merge pull request #83 from thockin/output_tests
Methods on aliased types are not valid map keys
2017-06-29 09:43:12 +08:00
45ccfb031f Methods on aliased types are not valid map keys 2017-06-28 09:38:15 -07:00
545a32f2a1 #76 support TextUnmarshal 2017-06-29 00:14:55 +08:00
08dbc98040 #76 fix UnmarshalJSON 2017-06-28 23:55:32 +08:00
82dabdcdbf #77 support -, 2017-06-28 23:47:32 +08:00
76e62088df #78 not null pointer is considered not empty 2017-06-28 23:39:46 +08:00
faaa59222a #79 fix string tag on string field 2017-06-28 23:37:10 +08:00
91ef89a6a2 WIP: MarshalIndent 2017-06-28 23:22:05 +08:00
3e3caf9184 Merge pull request #82 from thockin/output_tests
Huge updates to output tests
2017-06-28 23:20:59 +08:00
03a2daaeee Add a test for caseless unmarshal 2017-06-28 08:11:38 -07:00
4652ac6cc2 Add output tests for embedded types 2017-06-28 08:11:38 -07:00
8a9f2b9179 Add output test for manual text marshalers 2017-06-28 08:11:33 -07:00
0db2d74de8 Add output test for manual json marshalers 2017-06-28 08:11:02 -07:00
d6ef711c18 Better errors in output_tests 2017-06-28 07:48:55 -07:00
628fedf63c Add output tests for map keys with TextMarshal 2017-06-28 07:48:55 -07:00
5bb7a1f7af Add output test for 'string' tag 2017-06-28 07:48:55 -07:00
c2c9981062 catch errors in output_tests 2017-06-28 07:48:55 -07:00
e40d614037 Add output test for json tag field names 2017-06-28 07:48:55 -07:00
1589ab2fd7 Add output tests for omitempty 2017-06-28 07:48:49 -07:00
97ee4ad4a2 Reorganize output_tests for structs
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

   e.g. struct { F *string } -> struct/ptr_string

This exposed some redundant cases and some missing cases, too.
2017-06-27 19:51:13 +01:00
f09f778ca9 Reorganize output_tests for slices
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

   e.g. []*string -> slice/ptr_string

This exposed some redundant cases and some missing cases, too.
2017-06-27 19:21:17 +01:00
9fc858b117 Reorganize output_tests for maps
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

e.g. map[string]*string -> map/string/ptr_string

This exposed some redundant cases and some missing cases, too. Now
map[string] is the "main" test case that exercises all the variants of
value types, and the other key types are just to prove that they
basically work.
2017-06-27 19:19:37 +01:00
f93d25f8b1 Add output tests for slices of slices 2017-06-27 18:28:52 +01:00
7cd7a6cc7c Add output tests for maps of maps/slices/structs 2017-06-27 11:58:07 +01:00
7d6c9374e8 Add output test for structs of struct ptrs 2017-06-27 11:46:55 +01:00
e16ee7f8ac Add output tests for structs of slices and maps 2017-06-27 11:41:17 +01:00
cf6367546b Add output tests for structs of structs 2017-06-27 11:28:17 +01:00
dc44e85a86 #73 fix interface{} optimization for one ptr field struct and array 2017-06-26 22:37:24 +08:00
85f7a1b0b3 Merge pull request #74 from thockin/output_tests
Output tests for structs
2017-06-26 21:49:06 +08:00
507a446eda Add output_tests for structs of ptrs 2017-06-26 06:09:00 -07:00
76eefc25ba Add output_test for empty structs 2017-06-26 05:45:54 -07:00
21a16bd252 Add output_tests for structs of builtins 2017-06-26 05:37:47 -07:00
0c0c9f119f update value proposition 2017-06-26 14:28:03 +08:00
cf77980493 #71 fixed []byte alias 2017-06-26 14:25:56 +08:00
7d681fe2c2 #71 fix map key type of string alias 2017-06-26 11:52:22 +08:00
0c07128d3c #71 sort non string map keys 2017-06-26 10:42:47 +08:00
f771d32291 #70 decode null to nil for map/slice 2017-06-26 10:20:49 +08:00
d100b0d41f fix typo 2017-06-26 10:02:45 +08:00
81e64121ba #71 fix html escape for string alias 2017-06-26 09:57:45 +08:00
dcc91365ee Merge pull request #72 from thockin/output_tests
Output tests
2017-06-26 08:51:10 +08:00
8f3de9c412 Adapt tests to use new Config structs
the unit test uses compatible mode.  The benchmarks measure compat,
default, and fastest.

This still fails for strings and slices and maps all over the place.
2017-06-25 10:29:48 -07:00
5d3508979f Add output tests for slices of builtins 2017-06-25 10:00:35 -07:00
8f8e16b4c2 #63 keep struct field order 2017-06-23 08:21:02 +08:00
d7ea1acd3f #63 fix embed struct at last 2017-06-23 07:45:18 +08:00
ebed7df895 fix unicode and escape 2017-06-22 16:00:47 +08:00
caaa04195e #69 fix unicode support 2017-06-21 18:25:37 +08:00
ff3c624fa9 fix anonymous fields 2017-06-21 00:26:18 +08:00
3333ec11a0 support private fields 2017-06-20 23:48:41 +08:00
29a928e1d2 support naming strategy 2017-06-20 23:09:53 +08:00
83fa27ff9a #67 time as int64 with decoder 2017-06-20 17:52:41 +08:00
cefb2972fd #67 time as int64 with specified precision 2017-06-20 17:46:29 +08:00
486534c67c #67 time as int64 2017-06-20 17:43:47 +08:00
ed79b1726e fix encoder/decoder cast issue 2017-06-20 17:01:21 +08:00
85be06b145 #68 empty array to object/map 2017-06-20 16:36:22 +08:00
086001225d #68 string to float64 2017-06-20 16:20:56 +08:00
2ea4d48e1f #68 string to float32 2017-06-20 16:17:00 +08:00
417011b497 #68 remove redundant math max constants 2017-06-20 16:10:29 +08:00
ae6ce2fc3f #68 fuzzy all kinds of integer 2017-06-20 16:07:30 +08:00
8ef0c22f25 #68 handle float to int safely 2017-06-20 15:46:22 +08:00
a5ae3a2649 #68 float to int 2017-06-20 15:20:56 +08:00
306b2896cf #68 string to int 2017-06-20 15:18:24 +08:00
818ae1331a #68 number to string 2017-06-20 15:11:01 +08:00
8f6a840c63 fix anonymous struct 2017-06-20 13:33:40 +08:00
be221df432 #66 Make extension api like the java version 2017-06-20 10:41:54 +08:00
499412ec4c #66 extract out feacture_reflect_extension 2017-06-20 08:42:36 +08:00
c36a7ed7cd #66 extract out feacture_reflect_extension 2017-06-20 08:42:25 +08:00
14588726a1 expose ValEncoder & ValDecoder 2017-06-20 08:08:59 +08:00
aa01f57b7f rename AdaptedDecoder => Decoder and AdaptedEncoder => Encoder 2017-06-20 08:00:43 +08:00
b3170a8cef rename Encoder => ValEncoder and Decoder => ValDecoder 2017-06-20 07:59:45 +08:00
43a832beee add isEmptyFunc 2017-06-20 07:57:23 +08:00
39c9bb226a fix lossy float marshal and omit empty 2017-06-20 07:51:38 +08:00
945fe53724 fix html escape test and omit empty 2017-06-20 07:46:13 +08:00
8367a97ad8 gofmt 2017-06-20 07:39:54 +08:00
365d399192 #65 make placeholder thread safe 2017-06-20 07:39:38 +08:00
839247df05 #63 fix Marshaler and Unmarshaler on struct 2017-06-20 07:23:22 +08:00
f5edf564c8 gofmt 2017-06-19 23:43:53 +08:00
c3f5a2c536 #64 support fixed array 2017-06-19 23:43:28 +08:00
c6a598e292 # add jsoniter.RawMessage 2017-06-19 23:10:20 +08:00
eecb062c32 #63 support decode anonymous struct 2017-06-19 23:02:57 +08:00
50583f6bae #63 support *json.RawMessage 2017-06-19 22:57:43 +08:00
3b883aeffc #63 add more tests for json.RawMessage 2017-06-19 21:24:59 +08:00
baca358b53 add MustBeValid to Any 2017-06-19 21:21:20 +08:00
514db10f97 add Any.ToVal 2017-06-19 15:40:00 +08:00
31afe6450e add Api interface to allow save the frozen config 2017-06-19 13:43:22 +08:00
7e9017caa2 remove unused files 2017-06-18 23:43:01 +08:00
b6dfbbd6bc add document 2017-06-18 23:42:23 +08:00
3ffa5af7ec #61 remove internal buffer from mapAny 2017-06-18 23:18:32 +08:00
02cf6a73cc #61 remove internal buffer from objectAny 2017-06-18 23:09:30 +08:00
15c92d48df #61 remove internal buffer from numberLazyAny 2017-06-18 22:48:28 +08:00
a84cdaa694 #61 remove internal buffer from arrayAny 2017-06-18 22:40:18 +08:00
9f9ca4c9fc #61 remove stringLazyAny 2017-06-18 22:24:11 +08:00
985e263300 #61 removed internal buffer from lazy array and object; jsoniter.Get replaced jsoniter.UnmarshalAny 2017-06-18 22:22:13 +08:00
1ec246d16b #61 read any reuse skip impl 2017-06-18 17:00:28 +08:00
54dbcda64d #62 SkipAndReturnBytes should support reader 2017-06-18 16:28:43 +08:00
7a049ec79c #60 support read interface{} as json.Number 2017-06-18 15:22:37 +08:00
77dcffe77d tweak performance 2017-06-17 22:42:11 +08:00
8ab46965bd extract out any tests 2017-06-17 21:32:48 +08:00
2503ef17eb marshal lazy array/object using the config 2017-06-17 21:13:17 +08:00
0195110b5b gofmt 2017-06-17 21:11:23 +08:00
55fc498d27 use iterator from cache for any 2017-06-17 21:10:08 +08:00
50e4910c63 document how to get best performance 2017-06-17 17:14:34 +08:00
f29fe7407e downgrade to lower golang version 2017-06-17 16:27:19 +08:00
3c8bd9ef54 #57 copy bytes 2017-06-17 14:36:38 +08:00
952a42af6c #57 copy bytes 2017-06-17 14:36:05 +08:00
17bd91fd71 #57 reuse stream and iterator 2017-06-17 14:23:02 +08:00
3d5f6d3a4a #58 string mode support both encoding and decoding 2017-06-17 11:38:09 +08:00
b31b1301e2 #59 add ConfigFastest 2017-06-17 10:21:37 +08:00
69bc64b6d8 #54 support sort map keys 2017-06-16 16:46:30 +08:00
e0e2423e9a #53 test compatibility without html escape 2017-06-16 16:03:02 +08:00
a6ea770365 #53 implement SetEscapeHtml 2017-06-16 00:10:05 +08:00
5f22e50c89 #53 support escapeHtml 2017-06-15 23:55:04 +08:00
d867c8ba5c #53 split config into Config and frozenConfig 2017-06-13 18:49:35 +08:00
d0418857ce #53 move current config EnableXXX 2017-06-13 17:47:40 +08:00
48e9f6ec84 move IndentionStep to config 2017-06-13 17:03:27 +08:00
acddcf5bbf #53 extract out config 2017-06-13 16:58:53 +08:00
788918b85d #56 nil map or array should be null not [] or {} 2017-06-13 09:14:19 +08:00
6e5817b773 Merge pull request #55 from thockin/output_tests
WIP: Tests to compare against stdlib
2017-06-12 01:47:50 -05:00
7480e41836 Add output tests for maps of builtins
This tests for exact stdlib compatibility.
2017-06-11 21:09:56 -07:00
9215b3c508 Add output tests for builtin types
This fuzzes a type, marshals it with stdlib and json-iterator, compares,
then unmarshals with stdlib and json-iterator and compares.  This is
checking for literal, byte-for-byte compatibility.

In every case the test is exactly the same.

It also include benchmark functions to compare stdlib vs json-iterator.

This depends on a couple PRs to be merged in gofuzz.
2017-06-11 21:04:59 -07:00
64e500f3c8 Merge branch 'master' of https://github.com/json-iterator/go 2017-06-12 10:13:22 +08:00
3307ce3ba2 #50 map key unlike object field, can contain escaped char 2017-06-12 10:13:13 +08:00
6f50f15678 decoder/encoder;float precision doc 2017-06-11 16:30:31 +08:00
cee09816e3 decoder/encoder;float precision doc 2017-06-11 16:28:31 +08:00
cdbad22d22 test more package description 2017-06-11 15:35:45 +08:00
b0c9f047e2 test more than one pakcage description 2017-06-11 15:32:58 +08:00
6bd13c2948 Merge branch 'master' of https://github.com/json-iterator/go 2017-06-09 17:06:38 +08:00
84ad508437 #48 should return error if concrete tpye unknown 2017-06-09 17:06:27 +08:00
4f909776cf Merge pull request #49 from zhaitianduo/master
Use jsoniter instead of json in example
2017-06-09 03:32:31 -05:00
962c470806 fix import not use 2017-06-09 16:28:20 +08:00
46d443fbad use jsoniter for example 2017-06-09 16:25:58 +08:00
2608d40f2a example unmarshal 2017-06-08 12:08:47 +08:00
3cf822853f example unmarshal 2017-06-08 12:07:03 +08:00
26708bccc9 report error when string end not found 2017-06-08 09:46:19 +08:00
d75b539bad add test for scientific float 2017-06-07 21:34:56 +08:00
cfffa29c8a gofmt 2017-06-06 23:27:00 +08:00
925df245d3 good enough indent implementation 2017-06-06 23:18:37 +08:00
962a8cd303 #40 support UseNumber 2017-06-06 23:15:15 +08:00
6509ba05df Merge pull request #41 from 1046102779/master
解析时,如果输出参数不是指针类型,直接报错,避免程序挂掉
2017-06-06 10:03:06 -05:00
579dbf3c1d Merge pull request #42 from 1046102779/patch-1
把floatDigits改为intDigits
2017-06-06 10:02:26 -05:00
aa5181db67 把floatDigits改为intDigits 2017-06-06 21:08:04 +08:00
67be6df2b1 Update feature_adapter.go 2017-06-06 20:01:43 +08:00
0f5379494a unmarshal failed return non-pointer error 2017-06-06 19:36:33 +08:00
d09e2419ba update benchmark 2017-06-06 16:55:32 +08:00
e1a71f6ba1 update benchmark 2017-06-06 16:54:26 +08:00
dcb78991c4 flush when buffer is large enough 2017-06-06 14:16:54 +08:00
9e8238cdc6 remove unused file 2017-06-06 12:41:13 +08:00
a4e5abf492 support []byte; marshal without copy 2017-06-06 09:44:56 +08:00
3979955e69 support TextMarshaler as map key 2017-06-06 00:09:33 +08:00
5fd09f0e02 remove mapInterfaceEncoder 2017-06-05 23:56:37 +08:00
af4982b22c support decode int key map 2017-06-05 23:53:48 +08:00
29dc1d407d write map with int key 2017-06-05 23:01:00 +08:00
5b27aaa62c update test 2017-06-05 22:10:01 +08:00
106636a191 update test 2017-06-05 22:08:28 +08:00
f50c4cfbbe Merge branch 'master' of https://github.com/json-iterator/go 2017-06-05 22:05:02 +08:00
87149ae489 add simple marshal benchmark 2017-06-05 22:04:52 +08:00
c0a4ad72e1 example test 2017-06-05 20:37:08 +08:00
404c0ee44b Decoder doc 2017-06-05 19:57:20 +08:00
10c1506f87 link test 2017-06-05 19:38:34 +08:00
9a43fe6468 adapter api comment 2017-06-05 19:31:30 +08:00
95e03f2937 Marshal comment 2017-06-05 19:19:46 +08:00
4406ed9e62 Marshal comment 2017-06-05 19:18:12 +08:00
ff027701f5 Marshal comment 2017-06-05 19:15:56 +08:00
c69b61f879 Marshal comment 2017-06-05 19:14:40 +08:00
d97f5db769 Marshal comment 2017-06-05 19:11:16 +08:00
45bbb40a9f #34 implement NewEncoder 2017-06-02 18:46:44 +08:00
e36f926072 fix random go test failure 2017-06-02 17:34:40 +08:00
59e71bacc8 #36 handle anonymous 2017-06-02 16:52:20 +08:00
5cb0d35610 eof is not error 2017-06-02 16:06:33 +08:00
69b742e73a #34 support More() and Buffered() 2017-06-02 16:00:12 +08:00
a7f992f0e1 #35 fix json.Number matches string 2017-06-02 15:43:58 +08:00
4cc44e7380 #34 add decoder adapter 2017-06-02 15:38:20 +08:00
5310d4aa9a syntax highlight 2017-06-02 11:20:54 +08:00
2051e3b8ae simplify readme 2017-06-02 11:20:24 +08:00
fe9fa8900e #31 support json.RawMessage 2017-06-02 10:50:23 +08:00
ad3a7fde32 #30 support json.Number 2017-06-02 10:21:43 +08:00
377b892102 support big float and int 2017-05-31 12:40:50 +08:00
707ed3b091 support non empty interface 2017-05-27 00:36:21 +08:00
a7a7c7879a allocate less buffer for writing to []byte 2017-05-26 07:52:54 +08:00
f20f74519d RegisterTypeEncoder and RegisterTypeDecoder should have higher priority 2017-05-24 23:39:34 +08:00
7d2ae80c37 #27 support json.Unmarshaler 2017-05-24 16:04:11 +08:00
f6f159e108 #27 support json.Marshaler 2017-05-24 14:34:00 +08:00
e5a1e704ad #25 make fielding binding case insensitive 2017-05-24 13:16:09 +08:00
7d5f90261e #28 extension should support specifying encoder 2017-05-24 10:58:56 +08:00
6126a6d3ca #23 hide unexported fields by default 2017-05-24 09:39:11 +08:00
5fbe4e387d #21 #22 marshal float precisely by default 2017-05-24 09:08:15 +08:00
fc44cb2d91 #26 do not enforce pointer as optional 2017-05-23 18:46:11 +08:00
7e046e6aa7 simplify read string, and support null 2017-05-23 18:32:39 +08:00
5488fde97f fix one field struct interface{} optimization compatibility 2017-05-23 17:44:50 +08:00
53f8d370b5 fix wrap any 2017-05-19 19:44:27 +08:00
3f1fcaff87 demonstrate how to customize float encoding 2017-05-11 08:00:50 +08:00
1df353727b customize []byte encoder 2017-05-06 20:52:36 +08:00
b893a0359d trim end space 2017-05-05 17:44:09 +08:00
a92111261c fix struct with one pointer field 2017-05-05 17:27:41 +08:00
91b9e828b7 support recursive type 2017-05-05 16:51:05 +08:00
6bd835aeb1 test type encoder 2017-05-05 08:22:19 +08:00
90888390bc fix readFloat64SlowPath not advancing the cursor 2017-05-02 10:15:21 +08:00
ccb972f58c merge 2017-04-28 09:10:06 +08:00
8711c74c85 support Any as field type 2017-04-28 09:09:24 +08:00
abcf2759ed Merge pull request #14 from eruca/master
fix the omitempty bug
2017-04-18 10:23:05 +10:00
e5476f70e7 #16 fix slice of map 2017-04-16 14:05:08 +08:00
b986d86f26 add test 2017-03-11 18:19:39 +08:00
9a138f8b6a fix bug of another comma added
previous code will add comma if the field is empty when is not first, like 
```
{"account":"1120","name":"nick",,"department":"fsaf","role":1,"privilege":32,,"created_at":1489226500}
```
2017-03-11 18:17:34 +08:00
d1aa59e34e #12 implement omitempty 2017-03-08 07:38:25 -08:00
ceb8c8a733 create map if nil 2017-03-07 18:36:58 -08:00
62028f1ede Merge pull request #11 from Kisesy/patch-1
fix #10
2017-02-26 09:42:45 -06:00
696f962eda fix https://github.com/json-iterator/go/issues/10
Update feature_iter_string.go

Update feature_iter_string.go
2017-02-25 13:30:55 +08:00
9d1feb5431 fix tests 2017-02-12 22:49:45 +08:00
9670a03165 fix issue on 32bit platform 2017-02-09 13:50:06 +08:00
17cbb770f0 fix issue on 32bit platform 2017-02-09 13:35:58 +08:00
da7ed7809b use hash for field dispatching 2017-02-08 13:46:28 +08:00
14f696c6f5 Merge branch 'master' of https://github.com/json-iterator/go 2017-02-07 09:24:46 +08:00
2922666717 fix read int 2017-02-07 09:24:36 +08:00
06078a3afb Merge pull request #8 from smartczy/patch-1
Update README.md
2017-02-04 07:25:34 -06:00
232e0df179 Update README.md
modify the useage of function Read().
2017-02-04 14:34:00 +08:00
6880076b44 fix map[string]interface{} in struct 2017-02-03 18:44:54 +08:00
1e91dbbf58 map any support get all 2017-01-31 23:16:40 +08:00
bf459b9a49 object lazy any support get all 2017-01-31 20:26:35 +08:00
498ab96d90 array any support get all 2017-01-31 20:10:16 +08:00
edb96991a8 add all integer types 2017-01-29 16:55:32 +08:00
10a1fb8762 wrap map 2017-01-28 23:11:29 +08:00
95823d0bf1 wrap object 2017-01-28 22:45:03 +08:00
64c9bd0b1c wrap array 2017-01-28 21:11:36 +08:00
fa16f3c75c wrap string 2017-01-26 16:41:49 +08:00
cf4113fc22 wrap float 2017-01-26 16:33:16 +08:00
85edb698c8 Any Get will never return nil 2017-01-26 16:24:01 +08:00
9abc2f52b0 add Any GetInterface 2017-01-26 15:44:10 +08:00
97472ecd96 expose Parse from Any 2017-01-26 15:40:38 +08:00
4d7e181f9f implement Any WriteTo 2017-01-26 14:56:31 +08:00
9b587c0f22 suport encode interface 2017-01-26 00:25:17 +08:00
ce1a1f1e98 test reading into custom interface 2017-01-25 23:25:25 +08:00
f1c4dbde29 support map of interface{} 2017-01-25 22:43:57 +08:00
94ae645ab9 array/object to bool/int 2017-01-24 23:13:58 +08:00
ee54218b0a lazy array get 2017-01-24 22:56:18 +08:00
fa165c684f array lazy iterator 2017-01-24 22:47:56 +08:00
8656482625 array lazy fill and full fill 2017-01-24 22:36:16 +08:00
2d647f04ca implement lazy object three iteration way 2017-01-24 00:23:07 +08:00
38d613acf2 bool any 2017-01-23 08:45:57 +08:00
b9fe012eea string any 2017-01-23 08:33:43 +08:00
d49ea1bc49 float any 2017-01-22 23:38:55 +08:00
ba410b045b int lazy any 2017-01-22 23:29:48 +08:00
9df37bbd68 remove any 2017-01-22 19:36:19 +08:00
9c2b1d24b3 optimize true/false/null: 2017-01-22 19:28:14 +08:00
1163c348f6 fix write string 2017-01-22 00:04:08 +08:00
102cd8748e fix write int 2017-01-21 23:22:38 +08:00
8345c731dd rename 2017-01-21 17:11:38 +08:00
1d29fa38ef read to interface{} 2017-01-21 16:09:38 +08:00
928bc4ce72 ReadArrayCB 2017-01-20 13:54:51 +08:00
80c86e63b1 read object cb 2017-01-20 12:56:49 +08:00
d14b025931 make ReadObject return safe string 2017-01-20 12:40:52 +08:00
a73e48e8bf optimize read nil/true/false 2017-01-18 23:33:40 +08:00
6efc6c44ac test read string and read string as slice 2017-01-16 23:57:09 +08:00
e7ff7339b2 optimize read string 2017-01-16 23:43:20 +08:00
b9e3f01bfd fix int min max 2017-01-16 09:01:44 +08:00
54a69f1da2 fix read int ranges 2017-01-15 23:17:17 +08:00
399ee3faaa optimize read int32 2017-01-15 22:50:31 +08:00
c3c57d61b5 optimize read float64 2017-01-15 18:08:49 +08:00
5b2a731963 implement readFloat32 fast path 2017-01-15 17:30:02 +08:00
a57c8c6202 test array of interface 2017-01-09 21:00:01 +08:00
1f0a0e9a2d encode optional 2017-01-09 20:51:09 +08:00
90fc0b822f array encoder 2017-01-09 19:48:57 +08:00
552afb3625 struct encoder 2017-01-09 19:19:48 +08:00
5b0609f901 support int/string encode 2017-01-09 17:47:21 +08:00
bdbf8dcd42 write float64 2017-01-07 23:32:54 +08:00
ba3c30799b write float 32 2017-01-07 23:06:48 +08:00
e034897e69 write array 2017-01-07 22:16:20 +08:00
21549b9fd8 write object 2017-01-07 22:08:45 +08:00
5e50b3e11c write int 2017-01-07 21:25:23 +08:00
6f57d41461 add stream 2017-01-07 12:28:16 +08:00
5af8cc4b09 split files 2017-01-07 07:49:50 +08:00
c78023531e add more tests 2017-01-06 20:27:48 +08:00
101dfdbb2a fix 4 fields object decoding 2017-01-06 20:17:47 +08:00
c70437c6b9 Merge pull request #5 from hanbang-wang/master
Formated, doced. Also fixed few minor bugs.
2017-01-05 21:41:45 +08:00
d63a00f0bf Formated, doced. Also fixed few minor bugs. 2017-01-05 21:23:08 +08:00
97849e019f fix read any; reuse existing obj for optional 2017-01-05 13:53:38 +08:00
247a23a637 remove skipWhitespaces 2017-01-05 09:16:41 +08:00
e7ec3988a6 fix tag field name not used bug 2016-12-19 12:12:15 +08:00
c7d0dd5b66 Merge branch 'master' of https://github.com/json-iterator/go 2016-12-17 17:38:33 +08:00
7d5f2aed7b update extension 2016-12-17 17:38:13 +08:00
e38192e48f #3 fix nextToken 2016-12-16 00:25:35 +08:00
663 changed files with 58372 additions and 3154 deletions

3
.codecov.yml Normal file
View File

@ -0,0 +1,3 @@
ignore:
- "output_tests/.*"

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
/coverage.txt
/profile.out

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
language: go
go:
- 1.8.x
before_install:
- go get -t -v ./...
script:
- ./test.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,60 +1,67 @@
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)
[![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge)
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/json-iterator/go)
[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go)
[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go)
[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go)
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE)
[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
# Why jsoniter?
A high-performance 100% compatible drop-in replacement of "encoding/json"
* Jsoniter is the fastest JSON parser. It could be up to 10x faster than normal parser, data binding included. Shameless self [benchmark](http://jsoniter.com/benchmark.html)
* Extremely flexible api. You can mix and match three different styles: bind-api, any-api or iterator-api. Checkout your [api choices](http://jsoniter.com/api.html)
* Unique iterator api can iterate through JSON directly, zero memory allocation! See how [iterator](http://jsoniter.com/api.html#iterator-api) works
```
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
```
# Show off
# Benchmark
Here is a quick show off, for more complete report you can checkout the full [benchmark](http://jsoniter.com/benchmark.html) with [in-depth optimization](http://jsoniter.com/benchmark.html#optimization-used) to back the numbers up
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
![go-medium](http://jsoniter.com/benchmarks/go-medium.png)
Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go
# Bind-API is the best
Raw Result (easyjson requires static code generation)
Bind-api should always be the first choice. Given this JSON document `[0,1,2,3]`
| | 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 |
Parse with Go bind-api
# Usage
100% compatibility with standard lib
Replace
```go
import "encoding/json"
json.Marshal(&data)
```
with
```go
import "github.com/json-iterator/go"
iter := jsoniter.ParseString(`[0,1,2,3]`)
val := []int{}
iter.Read(&val)
fmt.Println(val[3])
jsoniter.Marshal(&data)
```
# Iterator-API for quick extraction
Replace
When you do not need to get all the data back, just extract some.
```go
import "encoding/json"
json.Unmarshal(input, &data)
```
Parse with Go iterator-api
with
```go
import "github.com/json-iterator/go"
iter := ParseString(`[0, [1, 2], [3, 4], 5]`)
count := 0
for iter.ReadArray() {
iter.skip()
count++
}
fmt.Println(count) // 4
jsoniter.Unmarshal(input, &data)
```
# Any-API for maximum flexibility
Parse with Go any-api
```go
import "github.com/json-iterator/go"
iter := jsoniter.ParseString(`[{"field1":"11","field2":"12"},{"field1":"21","field2":"22"}]`)
val := iter.ReadAny()
fmt.Println(val.ToInt(1, "field2")) // 22
```
Notice you can extract from nested data structure, and convert any type to the type to you want.
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
# How to get
@ -64,4 +71,10 @@ go get github.com/json-iterator/go
# Contribution Welcomed !
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
Contributors
* [thockin](https://github.com/thockin)
* [mattn](https://github.com/mattn)
* [cch123](https://github.com/cch123)
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)

427
any.go
View File

@ -1,427 +0,0 @@
package jsoniter
import (
"fmt"
"reflect"
"strconv"
)
type Any struct {
val interface{}
Error error
LastAccessed interface{}
}
func MakeAny(val interface{}) *Any {
return &Any{val, nil, nil}
}
func (any *Any) Get(keys ...interface{}) interface{} {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return "";
}
return ret
}
func (any *Any) GetValueType(keys ...interface{}) ValueType {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return Invalid;
}
switch reflect.TypeOf(ret).Kind() {
case reflect.Uint8:
return Number;
case reflect.Int8:
return Number;
case reflect.Uint16:
return Number;
case reflect.Int16:
return Number;
case reflect.Uint32:
return Number;
case reflect.Int32:
return Number;
case reflect.Uint64:
return Number;
case reflect.Int64:
return Number;
case reflect.Int:
return Number;
case reflect.Uint:
return Number;
case reflect.Float32:
return Number;
case reflect.Float64:
return Number;
case reflect.String:
return String;
case reflect.Bool:
return Bool;
case reflect.Array:
return Array;
case reflect.Struct:
return Object;
default:
return Invalid
}
}
func (any *Any) ToString(keys ...interface{}) string {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return "";
}
switch ret := ret.(type) {
case uint8:
return strconv.FormatInt(int64(ret), 10);
case int8:
return strconv.FormatInt(int64(ret), 10);
case uint16:
return strconv.FormatInt(int64(ret), 10);
case int16:
return strconv.FormatInt(int64(ret), 10);
case uint32:
return strconv.FormatInt(int64(ret), 10);
case int32:
return strconv.FormatInt(int64(ret), 10);
case uint64:
return strconv.FormatUint(uint64(ret), 10);
case int64:
return strconv.FormatInt(int64(ret), 10);
case int:
return strconv.FormatInt(int64(ret), 10);
case uint:
return strconv.FormatInt(int64(ret), 10);
case float32:
return strconv.FormatFloat(float64(ret), 'E', -1, 32);
case float64:
return strconv.FormatFloat(ret, 'E', -1, 64);
case string:
return ret
default:
return fmt.Sprintf("%v", ret)
}
}
func (any *Any) ToUint8(keys ...interface{}) uint8 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint8(ret)
}
func (any *Any) ToInt8(keys ...interface{}) int8 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int8(ret)
}
func (any *Any) ToUint16(keys ...interface{}) uint16 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint16(ret)
}
func (any *Any) ToInt16(keys ...interface{}) int16 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int16(ret)
}
func (any *Any) ToUint32(keys ...interface{}) uint32 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint32(ret)
}
func (any *Any) ToInt32(keys ...interface{}) int32 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int32(ret)
}
func (any *Any) ToUint64(keys ...interface{}) uint64 {
ret, err := getPathAsUint64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint64(ret)
}
func (any *Any) ToInt64(keys ...interface{}) int64 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int64(ret)
}
func (any *Any) ToInt(keys ...interface{}) int {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int(ret)
}
func (any *Any) ToUint(keys ...interface{}) uint {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint(ret)
}
func (any *Any) ToFloat32(keys ...interface{}) float32 {
ret, err := getPathAsFloat64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return float32(ret)
}
func (any *Any) ToFloat64(keys ...interface{}) float64 {
ret, err := getPathAsFloat64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return ret
}
func (any *Any) ToBool(keys ...interface{}) bool {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return false;
}
typedRet, ok := ret.(bool)
if !ok {
any.Error = fmt.Errorf("%v is not bool", ret)
return false;
}
return typedRet
}
func (any *Any) IsNull(keys ...interface{}) bool {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return false;
}
return reflect.ValueOf(ret).IsNil()
}
func getPathAsInt64(any *Any, keys ...interface{}) (int64, error) {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return 0, err
}
switch ret := ret.(type) {
case uint8:
return int64(ret), nil;
case int8:
return int64(ret), nil;
case uint16:
return int64(ret), nil;
case int16:
return int64(ret), nil;
case uint32:
return int64(ret), nil;
case int32:
return int64(ret), nil;
case uint64:
return int64(ret), nil;
case int64:
return int64(ret), nil;
case int:
return int64(ret), nil;
case uint:
return int64(ret), nil;
case float32:
return int64(ret), nil;
case float64:
return int64(ret), nil;
case string:
intVal, err := strconv.ParseInt(ret, 10, 64)
if err != nil {
return 0, err
}
return intVal, nil;
default:
return 0, fmt.Errorf("%v is not number", ret)
}
}
func getPathAsUint64(any *Any, keys ...interface{}) (uint64, error) {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return 0, err
}
switch ret := ret.(type) {
case uint8:
return uint64(ret), nil;
case int8:
return uint64(ret), nil;
case uint16:
return uint64(ret), nil;
case int16:
return uint64(ret), nil;
case uint32:
return uint64(ret), nil;
case int32:
return uint64(ret), nil;
case uint64:
return uint64(ret), nil;
case int64:
return uint64(ret), nil;
case int:
return uint64(ret), nil;
case uint:
return uint64(ret), nil;
case float32:
return uint64(ret), nil;
case float64:
return uint64(ret), nil;
case string:
intVal, err := strconv.ParseUint(ret, 10, 64)
if err != nil {
return 0, err
}
return intVal, nil;
default:
return 0, fmt.Errorf("%v is not number", ret)
}
}
func getPathAsFloat64(any *Any, keys ...interface{}) (float64, error) {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return 0, err
}
switch ret := ret.(type) {
case uint8:
return float64(ret), nil;
case int8:
return float64(ret), nil;
case uint16:
return float64(ret), nil;
case int16:
return float64(ret), nil;
case uint32:
return float64(ret), nil;
case int32:
return float64(ret), nil;
case uint64:
return float64(ret), nil;
case int64:
return float64(ret), nil;
case int:
return float64(ret), nil;
case uint:
return float64(ret), nil;
case float32:
return float64(ret), nil;
case float64:
return float64(ret), nil;
case string:
floatVal, err := strconv.ParseFloat(ret, 64)
if err != nil {
return 0, err
}
return floatVal, nil;
default:
return 0, fmt.Errorf("%v is not number", ret)
}
}
func getPath(val interface{}, keys ...interface{}) (interface{}, error) {
if (len(keys) == 0) {
return val, nil;
}
switch key := keys[0].(type) {
case string:
nextVal, err := getFromMap(val, key)
if err != nil {
return nil, err
}
nextKeys := make([]interface{}, len(keys) - 1)
copy(nextKeys, keys[1:])
return getPath(nextVal, nextKeys...)
case int:
nextVal, err := getFromArray(val, key)
if err != nil {
return nil, err
}
nextKeys := make([]interface{}, len(keys) - 1)
copy(nextKeys, keys[1:])
return getPath(nextVal, nextKeys...)
default:
return nil, fmt.Errorf("%v is not string or int", keys[0]);
}
return getPath(val, keys);
}
func getFromMap(val interface{}, key string) (interface{}, error) {
mapVal, ok := val.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%v is not map[string]interface{}", val)
}
ret, found := mapVal[key]
if !found {
return nil, fmt.Errorf("%v not found in %v", key, mapVal)
}
return ret, nil
}
func getFromArray(val interface{}, key int) (interface{}, error) {
arrayVal, ok := val.([]interface{})
if !ok {
return nil, fmt.Errorf("%v is not []interface{}", val)
}
if key >= len(arrayVal) {
return nil, fmt.Errorf("%v exceed %v", key, arrayVal)
}
if key < 0 {
return nil, fmt.Errorf("%v exceed %v", key, arrayVal)
}
return arrayVal[key], nil
}

View File

@ -1,72 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
)
func Test_get_from_map(t *testing.T) {
any := Any{val: map[string]interface{}{
"hello": "world",
}}
if any.ToString("hello") != "world" {
t.FailNow()
}
}
func Test_get_from_array(t *testing.T) {
any := Any{val: []interface{}{
"hello", "world",
}}
if any.ToString(1) != "world" {
t.FailNow()
}
}
func Test_get_int(t *testing.T) {
any := Any{val: []interface{}{
1, 2, 3,
}}
if any.ToInt(1) != 2 {
t.FailNow()
}
}
func Test_is_null(t *testing.T) {
any := Any{val: []interface{}{
1, 2, 3,
}}
if any.IsNull() != false {
t.FailNow()
}
}
func Test_get_bool(t *testing.T) {
any := Any{val: []interface{}{
true, true, false,
}}
if any.ToBool(1) != true {
t.FailNow()
}
}
func Test_nested_read(t *testing.T) {
any := Any{val: []interface{}{
true, map[string]interface{}{
"hello": "world",
}, false,
}}
if any.ToString(1, "hello") != "world" {
fmt.Println(any.Error)
t.FailNow()
}
}
func Test_int_to_string(t *testing.T) {
any := Any{val: []interface{}{
true, 5, false,
}}
if any.ToString(1) != "5" {
t.FailNow()
}
}

95
example_test.go Normal file
View File

@ -0,0 +1,95 @@
package jsoniter
import (
"fmt"
"os"
)
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
}

256
extra/fuzzy_decoder.go Normal file
View File

@ -0,0 +1,256 @@
package extra
import (
"encoding/json"
"github.com/json-iterator/go"
"math"
"reflect"
"strings"
"unsafe"
)
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 reflect.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()
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()
default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
}
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 {
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 {
iter.Error = newIter.Error
}
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 {
iter.Error = newIter.Error
}
default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
}
}

259
extra/fuzzy_decoder_test.go Normal file
View File

@ -0,0 +1,259 @@
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(`-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)
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)
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)
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)
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)
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)
// 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)
// 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)
// 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.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)
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)
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)
}

41
extra/naming_strategy.go Normal file
View File

@ -0,0 +1,41 @@
package extra
import (
"github.com/json-iterator/go"
"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 {
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)
}

View File

@ -0,0 +1,23 @@
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))
}

25
extra/privat_fields.go Normal file
View File

@ -0,0 +1,25 @@
package extra
import (
"github.com/json-iterator/go"
"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 {
binding.FromNames = []string{binding.Field.Name}
binding.ToNames = []string{binding.Field.Name}
}
}
}

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

View File

@ -0,0 +1,34 @@
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())
}
func (codec *timeAsInt64Codec) EncodeInterface(val interface{}, stream *jsoniter.Stream) {
jsoniter.WriteToStream(val, stream, codec)
}

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

127
feature_adapter.go Normal file
View File

@ -0,0 +1,127 @@
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)
}
func lastNotSpacePos(data []byte) int {
for i := len(data) - 1; i >= 0; i-- {
if data[i] != ' ' && data[i] != '\t' && data[i] != '\r' && data[i] != '\n' {
return i + 1
}
}
return 0
}
// UnmarshalFromString convenient method to read from string instead of []byte
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 {
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 {
return adapter.iter.head != adapter.iter.tail
}
// Buffered remaining buffer
func (adapter *Decoder) Buffered() io.Reader {
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
return bytes.NewReader(remaining)
}
// UseNumber for number JSON element, use float64 or json.NumberValue (alias of string)
func (adapter *Decoder) UseNumber() {
origCfg := adapter.iter.cfg.configBeforeFrozen
origCfg.UseNumber = true
adapter.iter.cfg = origCfg.Froze().(*frozenConfig)
}
// 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.Flush()
return adapter.stream.Error
}
// SetIndent set the indention. Prefix is not supported
func (adapter *Encoder) SetIndent(prefix, indent string) {
adapter.stream.cfg.indentionStep = len(indent)
}
// SetEscapeHTML escape html by default, set to false to disable
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
config := adapter.stream.cfg.configBeforeFrozen
config.EscapeHTML = escapeHTML
adapter.stream.cfg = config.Froze().(*frozenConfig)
}

242
feature_any.go Normal file
View File

@ -0,0 +1,242 @@
package jsoniter
import (
"fmt"
"io"
"reflect"
)
// 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
// TODO: add Set
Size() int
Keys() []string
GetInterface() interface{}
WriteTo(stream *Stream)
}
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) 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 := reflect.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:
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{}
}
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)
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()
}

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

137
feature_any_bool.go Normal file
View File

@ -0,0 +1,137 @@
package jsoniter
type trueAny struct {
baseAny
}
func (any *trueAny) LastError() error {
return nil
}
func (any *trueAny) ToBool() bool {
return true
}
func (any *trueAny) ToInt() int {
return 1
}
func (any *trueAny) ToInt32() int32 {
return 1
}
func (any *trueAny) ToInt64() int64 {
return 1
}
func (any *trueAny) ToUint() uint {
return 1
}
func (any *trueAny) ToUint32() uint32 {
return 1
}
func (any *trueAny) ToUint64() uint64 {
return 1
}
func (any *trueAny) ToFloat32() float32 {
return 1
}
func (any *trueAny) ToFloat64() float64 {
return 1
}
func (any *trueAny) ToString() string {
return "true"
}
func (any *trueAny) WriteTo(stream *Stream) {
stream.WriteTrue()
}
func (any *trueAny) Parse() *Iterator {
return nil
}
func (any *trueAny) GetInterface() interface{} {
return true
}
func (any *trueAny) ValueType() ValueType {
return BoolValue
}
func (any *trueAny) MustBeValid() Any {
return any
}
type falseAny struct {
baseAny
}
func (any *falseAny) LastError() error {
return nil
}
func (any *falseAny) ToBool() bool {
return false
}
func (any *falseAny) ToInt() int {
return 0
}
func (any *falseAny) ToInt32() int32 {
return 0
}
func (any *falseAny) ToInt64() int64 {
return 0
}
func (any *falseAny) ToUint() uint {
return 0
}
func (any *falseAny) ToUint32() uint32 {
return 0
}
func (any *falseAny) ToUint64() uint64 {
return 0
}
func (any *falseAny) ToFloat32() float32 {
return 0
}
func (any *falseAny) ToFloat64() float64 {
return 0
}
func (any *falseAny) ToString() string {
return "false"
}
func (any *falseAny) WriteTo(stream *Stream) {
stream.WriteFalse()
}
func (any *falseAny) Parse() *Iterator {
return nil
}
func (any *falseAny) GetInterface() interface{} {
return false
}
func (any *falseAny) ValueType() ValueType {
return BoolValue
}
func (any *falseAny) MustBeValid() Any {
return any
}

83
feature_any_float.go Normal file
View 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
}

74
feature_any_int32.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type int32Any struct {
baseAny
val int32
}
func (any *int32Any) LastError() error {
return nil
}
func (any *int32Any) ValueType() ValueType {
return NumberValue
}
func (any *int32Any) MustBeValid() Any {
return any
}
func (any *int32Any) ToBool() bool {
return any.val != 0
}
func (any *int32Any) ToInt() int {
return int(any.val)
}
func (any *int32Any) ToInt32() int32 {
return any.val
}
func (any *int32Any) ToInt64() int64 {
return int64(any.val)
}
func (any *int32Any) ToUint() uint {
return uint(any.val)
}
func (any *int32Any) ToUint32() uint32 {
return uint32(any.val)
}
func (any *int32Any) ToUint64() uint64 {
return uint64(any.val)
}
func (any *int32Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *int32Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *int32Any) ToString() string {
return strconv.FormatInt(int64(any.val), 10)
}
func (any *int32Any) WriteTo(stream *Stream) {
stream.WriteInt32(any.val)
}
func (any *int32Any) Parse() *Iterator {
return nil
}
func (any *int32Any) GetInterface() interface{} {
return any.val
}

74
feature_any_int64.go Normal file
View 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
}

82
feature_any_invalid.go Normal file
View File

@ -0,0 +1,82 @@
package jsoniter
import "fmt"
type invalidAny struct {
baseAny
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 InvalidValue
}
func (any *invalidAny) MustBeValid() Any {
panic(any.err)
}
func (any *invalidAny) ToBool() bool {
return false
}
func (any *invalidAny) ToInt() int {
return 0
}
func (any *invalidAny) ToInt32() int32 {
return 0
}
func (any *invalidAny) ToInt64() int64 {
return 0
}
func (any *invalidAny) ToUint() uint {
return 0
}
func (any *invalidAny) ToUint32() uint32 {
return 0
}
func (any *invalidAny) ToUint64() uint64 {
return 0
}
func (any *invalidAny) ToFloat32() float32 {
return 0
}
func (any *invalidAny) ToFloat64() float64 {
return 0
}
func (any *invalidAny) ToString() string {
return ""
}
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)}
}
return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)}
}
func (any *invalidAny) Parse() *Iterator {
return nil
}
func (any *invalidAny) GetInterface() interface{} {
return nil
}

69
feature_any_nil.go Normal file
View File

@ -0,0 +1,69 @@
package jsoniter
type nilAny struct {
baseAny
}
func (any *nilAny) LastError() error {
return nil
}
func (any *nilAny) ValueType() ValueType {
return NilValue
}
func (any *nilAny) MustBeValid() Any {
return any
}
func (any *nilAny) ToBool() bool {
return false
}
func (any *nilAny) ToInt() int {
return 0
}
func (any *nilAny) ToInt32() int32 {
return 0
}
func (any *nilAny) ToInt64() int64 {
return 0
}
func (any *nilAny) ToUint() uint {
return 0
}
func (any *nilAny) ToUint32() uint32 {
return 0
}
func (any *nilAny) ToUint64() uint64 {
return 0
}
func (any *nilAny) ToFloat32() float32 {
return 0
}
func (any *nilAny) ToFloat64() float64 {
return 0
}
func (any *nilAny) ToString() string {
return ""
}
func (any *nilAny) WriteTo(stream *Stream) {
stream.WriteNil()
}
func (any *nilAny) Parse() *Iterator {
return nil
}
func (any *nilAny) GetInterface() interface{} {
return nil
}

104
feature_any_number.go Normal file
View File

@ -0,0 +1,104 @@
package jsoniter
import "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()
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()
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()
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()
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()
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()
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()
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()
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
feature_any_object.go Normal file
View 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
feature_any_string.go Normal file
View 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("Get %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
endPos := 0
if any.val[0] == '+' || any.val[0] == '-' {
startPos = 1
}
if any.val[0] == '-' {
flag = -1
}
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
endPos := 0
if any.val[0] == '-' {
return 0
}
if any.val[0] == '+' {
startPos = 1
}
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
}

74
feature_any_uint32.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type uint32Any struct {
baseAny
val uint32
}
func (any *uint32Any) LastError() error {
return nil
}
func (any *uint32Any) ValueType() ValueType {
return NumberValue
}
func (any *uint32Any) MustBeValid() Any {
return any
}
func (any *uint32Any) ToBool() bool {
return any.val != 0
}
func (any *uint32Any) ToInt() int {
return int(any.val)
}
func (any *uint32Any) ToInt32() int32 {
return int32(any.val)
}
func (any *uint32Any) ToInt64() int64 {
return int64(any.val)
}
func (any *uint32Any) ToUint() uint {
return uint(any.val)
}
func (any *uint32Any) ToUint32() uint32 {
return any.val
}
func (any *uint32Any) ToUint64() uint64 {
return uint64(any.val)
}
func (any *uint32Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *uint32Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *uint32Any) ToString() string {
return strconv.FormatInt(int64(any.val), 10)
}
func (any *uint32Any) WriteTo(stream *Stream) {
stream.WriteUint32(any.val)
}
func (any *uint32Any) Parse() *Iterator {
return nil
}
func (any *uint32Any) GetInterface() interface{} {
return any.val
}

74
feature_any_uint64.go Normal file
View 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
}

312
feature_config.go Normal file
View File

@ -0,0 +1,312 @@
package jsoniter
import (
"encoding/json"
"errors"
"io"
"reflect"
"sync/atomic"
"unsafe"
)
// Config customize how the API should behave.
// The API is created from Config by Froze.
type Config struct {
IndentionStep int
MarshalFloatWith6Digits bool
EscapeHTML bool
SortMapKeys bool
UseNumber bool
TagKey string
}
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
decoderCache unsafe.Pointer
encoderCache unsafe.Pointer
extensions []Extension
streamPool chan *Stream
iteratorPool chan *Iterator
}
// API the public interface of this package.
// Primary Marshal and Unmarshal.
type API interface {
IteratorPool
StreamPool
MarshalToString(v interface{}) (string, error)
Marshal(v interface{}) ([]byte, error)
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
UnmarshalFromString(str string, v interface{}) error
Unmarshal(data []byte, v interface{}) error
Get(data []byte, path ...interface{}) Any
NewEncoder(writer io.Writer) *Encoder
NewDecoder(reader io.Reader) *Decoder
}
// ConfigDefault the default API
var ConfigDefault = Config{
EscapeHTML: true,
}.Froze()
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true,
SortMapKeys: true,
}.Froze()
// ConfigFastest marshals float with only 6 digits precision
var ConfigFastest = Config{
EscapeHTML: false,
MarshalFloatWith6Digits: true,
}.Froze()
// Froze forge API from config
func (cfg Config) Froze() API {
// TODO: cache frozen config
frozenConfig := &frozenConfig{
sortMapKeys: cfg.SortMapKeys,
indentionStep: cfg.IndentionStep,
streamPool: make(chan *Stream, 16),
iteratorPool: make(chan *Iterator, 16),
}
atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
if cfg.MarshalFloatWith6Digits {
frozenConfig.marshalFloatWith6Digits()
}
if cfg.EscapeHTML {
frozenConfig.escapeHTML()
}
if cfg.UseNumber {
frozenConfig.useNumber()
}
frozenConfig.configBeforeFrozen = cfg
return frozenConfig
}
func (cfg *frozenConfig) useNumber() {
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == 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.extensions = append(cfg.extensions, extension)
}
type lossyFloat32Encoder struct {
}
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat32Lossy(*((*float32)(ptr)))
}
func (encoder *lossyFloat32Encoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float32)(ptr)) == 0
}
type lossyFloat64Encoder struct {
}
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat64Lossy(*((*float64)(ptr)))
}
func (encoder *lossyFloat64Encoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float64)(ptr)) == 0
}
// EnableLossyFloatMarshalling keeps 10**(-6) precision
// for float variables for better performance.
func (cfg *frozenConfig) marshalFloatWith6Digits() {
// for better performance
cfg.addEncoderToCache(reflect.TypeOf((*float32)(nil)).Elem(), &lossyFloat32Encoder{})
cfg.addEncoderToCache(reflect.TypeOf((*float64)(nil)).Elem(), &lossyFloat64Encoder{})
}
type htmlEscapedStringEncoder struct {
}
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
stream.WriteStringWithHTMLEscaped(str)
}
func (encoder *htmlEscapedStringEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*string)(ptr)) == ""
}
func (cfg *frozenConfig) escapeHTML() {
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
done := false
for !done {
ptr := atomic.LoadPointer(&cfg.decoderCache)
cache := *(*map[reflect.Type]ValDecoder)(ptr)
copied := map[reflect.Type]ValDecoder{}
for k, v := range cache {
copied[k] = v
}
copied[cacheKey] = decoder
done = atomic.CompareAndSwapPointer(&cfg.decoderCache, ptr, unsafe.Pointer(&copied))
}
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
done := false
for !done {
ptr := atomic.LoadPointer(&cfg.encoderCache)
cache := *(*map[reflect.Type]ValEncoder)(ptr)
copied := map[reflect.Type]ValEncoder{}
for k, v := range cache {
copied[k] = v
}
copied[cacheKey] = encoder
done = atomic.CompareAndSwapPointer(&cfg.encoderCache, ptr, unsafe.Pointer(&copied))
}
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
ptr := atomic.LoadPointer(&cfg.decoderCache)
cache := *(*map[reflect.Type]ValDecoder)(ptr)
return cache[cacheKey]
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
ptr := atomic.LoadPointer(&cfg.encoderCache)
cache := *(*map[reflect.Type]ValEncoder)(ptr)
return cache[cacheKey]
}
func (cfg *frozenConfig) cleanDecoders() {
typeDecoders = map[string]ValDecoder{}
fieldDecoders = map[string]ValDecoder{}
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
}
func (cfg *frozenConfig) cleanEncoders() {
typeEncoders = map[string]ValEncoder{}
fieldEncoders = map[string]ValEncoder{}
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
}
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
stream := cfg.BorrowStream(nil)
defer cfg.ReturnStream(stream)
stream.WriteVal(v)
if stream.Error != nil {
return "", stream.Error
}
return string(stream.Buffer()), nil
}
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
stream := cfg.BorrowStream(nil)
defer cfg.ReturnStream(stream)
stream.WriteVal(v)
if stream.Error != nil {
return nil, stream.Error
}
result := stream.Buffer()
copied := make([]byte, len(result))
copy(copied, result)
return copied, nil
}
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
if prefix != "" {
panic("prefix is not supported")
}
for _, r := range indent {
if r != ' ' {
panic("indent can only be space")
}
}
newCfg := cfg.configBeforeFrozen
newCfg.IndentionStep = len(indent)
return newCfg.Froze().Marshal(v)
}
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
data := []byte(str)
data = data[:lastNotSpacePos(data)]
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.ReadVal(v)
if iter.head == iter.tail {
iter.loadMore()
}
if iter.Error == io.EOF {
return nil
}
if iter.Error == nil {
iter.ReportError("UnmarshalFromString", "there are bytes left after unmarshal")
}
return iter.Error
}
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
return locatePath(iter, path)
}
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
data = data[:lastNotSpacePos(data)]
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
typ := reflect.TypeOf(v)
if typ.Kind() != reflect.Ptr {
// return non-pointer error
return errors.New("the second param must be ptr type")
}
iter.ReadVal(v)
if iter.head == iter.tail {
iter.loadMore()
}
if iter.Error == io.EOF {
return nil
}
if iter.Error == nil {
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
}
return iter.Error
}
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
stream := NewStream(cfg, writer, 512)
return &Encoder{stream}
}
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
iter := Parse(cfg, reader, 512)
return &Decoder{iter}
}

307
feature_iter.go Normal file
View File

@ -0,0 +1,307 @@
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
captureStartedAt int
captured []byte
Error error
}
// NewIterator creates an empty Iterator instance
func NewIterator(cfg API) *Iterator {
return &Iterator{
cfg: cfg.(*frozenConfig),
reader: nil,
buf: nil,
head: 0,
tail: 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,
}
}
// 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),
}
}
// 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
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)
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")
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
}
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 for debugging purpose
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.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
}
}

58
feature_iter_array.go Normal file
View File

@ -0,0 +1,58 @@
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 == '[' {
c = iter.nextToken()
if c != ']' {
iter.unreadByte()
if !callback(iter) {
return false
}
c = iter.nextToken()
for c == ',' {
if !callback(iter) {
return false
}
c = iter.nextToken()
}
if c != ']' {
iter.ReportError("ReadArrayCB", "expect ] in the end")
return false
}
return true
}
return true
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadArrayCB", "expect [ or n, but found: "+string([]byte{c}))
return false
}

341
feature_iter_float.go Normal file
View File

@ -0,0 +1,341 @@
package jsoniter
import (
"io"
"math/big"
"strconv"
"strings"
"unsafe"
)
var floatDigits []int8
const invalidCharForNumber = int8(-1)
const endOfNumber = int8(-2)
const dotInNumber = int8(-3)
func init() {
floatDigits = make([]int8, 256)
for i := 0; i < len(floatDigits); i++ {
floatDigits[i] = invalidCharForNumber
}
for i := int8('0'); i <= int8('9'); i++ {
floatDigits[i] = i - int8('0')
}
floatDigits[','] = endOfNumber
floatDigits[']'] = endOfNumber
floatDigits['}'] = endOfNumber
floatDigits[' '] = endOfNumber
floatDigits['\t'] = endOfNumber
floatDigits['\n'] = endOfNumber
floatDigits['.'] = dotInNumber
}
// ReadBigFloat read big.Float
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
prec := 64
if len(str) > prec {
prec = len(str)
}
val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero)
if err != nil {
iter.Error = err
return nil
}
return val
}
// ReadBigInt read big.Int
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
ret = big.NewInt(0)
var success bool
ret, success = ret.SetString(str, 10)
if !success {
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()
}
iter.unreadByte()
return iter.readPositiveFloat32()
}
func (iter *Iterator) readPositiveFloat32() (ret float32) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat32SlowPath()
}
c = iter.buf[i]
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]
switch ind {
case invalidCharForNumber:
return iter.readFloat32SlowPath()
case endOfNumber:
iter.head = i
return float32(value)
case dotInNumber:
break non_decimal_loop
}
if value > uint64SafeToMultiple10 {
return iter.readFloat32SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
}
// chars after dot
if c == '.' {
i++
decimalPlaces := 0
if i == iter.tail {
return iter.readFloat32SlowPath()
}
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case endOfNumber:
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
iter.head = i
return float32(float64(value) / float64(pow10[decimalPlaces]))
}
// too many decimal places
return iter.readFloat32SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
return iter.readFloat32SlowPath()
}
decimalPlaces++
if value > uint64SafeToMultiple10 {
return iter.readFloat32SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind)
}
}
return iter.readFloat32SlowPath()
}
func (iter *Iterator) readNumberAsString() (ret string) {
strBuf := [16]byte{}
str := strBuf[0:0]
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':
str = append(str, c)
continue
default:
iter.head = i
break load_loop
}
}
if !iter.loadMore() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
if len(str) == 0 {
iter.ReportError("readNumberAsString", "invalid number")
}
return *(*string)(unsafe.Pointer(&str))
}
func (iter *Iterator) readFloat32SlowPath() (ret float32) {
str := iter.readNumberAsString()
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
return
}
return float32(val)
}
// ReadFloat64 read float64
func (iter *Iterator) ReadFloat64() (ret float64) {
c := iter.nextToken()
if c == '-' {
return -iter.readPositiveFloat64()
}
iter.unreadByte()
return iter.readPositiveFloat64()
}
func (iter *Iterator) readPositiveFloat64() (ret float64) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat64SlowPath()
}
c = iter.buf[i]
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]
switch ind {
case invalidCharForNumber:
return iter.readFloat64SlowPath()
case endOfNumber:
iter.head = i
return float64(value)
case dotInNumber:
break non_decimal_loop
}
if value > uint64SafeToMultiple10 {
return iter.readFloat64SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
}
// chars after dot
if c == '.' {
i++
decimalPlaces := 0
if i == iter.tail {
return iter.readFloat64SlowPath()
}
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case endOfNumber:
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
iter.head = i
return float64(value) / float64(pow10[decimalPlaces])
}
// too many decimal places
return iter.readFloat64SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
return iter.readFloat64SlowPath()
}
decimalPlaces++
if value > uint64SafeToMultiple10 {
return iter.readFloat64SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind)
}
}
return iter.readFloat64SlowPath()
}
func (iter *Iterator) readFloat64SlowPath() (ret float64) {
str := iter.readNumberAsString()
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
return
}
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 ""
}

258
feature_iter_int.go Normal file
View File

@ -0,0 +1,258 @@
package jsoniter
import (
"math"
"strconv"
)
var intDigits []int8
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 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 {
return uint(iter.ReadUint64())
}
// ReadInt read int
func (iter *Iterator) ReadInt() int {
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 {
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
}
value = value2
continue
}
value = (value << 3) + (value << 1) + uint32(ind)
}
if !iter.loadMore() {
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 {
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
}
value = value2
continue
}
value = (value << 3) + (value << 1) + uint64(ind)
}
if !iter.loadMore() {
return value
}
}
}

212
feature_iter_object.go Normal file
View File

@ -0,0 +1,212 @@
package jsoniter
import (
"fmt"
"unicode"
"unsafe"
)
// ReadObject read one field from object.
// If object ended, returns empty string.
// Otherwise, returns the field name.
func (iter *Iterator) ReadObject() (ret string) {
c := iter.nextToken()
switch c {
case 'n':
iter.skipThreeBytes('u', 'l', 'l')
return "" // null
case '{':
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
return string(iter.readObjectFieldAsBytes())
}
if c == '}' {
return "" // end of object
}
iter.ReportError("ReadObject", `expect " after {`)
return
case ',':
return string(iter.readObjectFieldAsBytes())
case '}':
return "" // end of object
default:
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
return
}
}
func (iter *Iterator) readFieldHash() int32 {
hash := int64(0x811c9dc5)
c := iter.nextToken()
if c == '"' {
for {
for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape
b := iter.buf[i]
if 'A' <= b && b <= 'Z' {
b += 'a' - 'A'
}
if b == '"' {
iter.head = i + 1
c = iter.nextToken()
if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
}
return int32(hash)
}
hash ^= int64(b)
hash *= 0x1000193
}
if !iter.loadMore() {
iter.ReportError("readFieldHash", `incomplete field name`)
return 0
}
}
}
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
return 0
}
func calcHash(str string) int32 {
hash := int64(0x811c9dc5)
for _, b := range str {
hash ^= int64(unicode.ToLower(b))
hash *= 0x1000193
}
return int32(hash)
}
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken()
if c == '{' {
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field := iter.readObjectFieldAsBytes()
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.readObjectFieldAsBytes()
if !callback(iter, *(*string)(unsafe.Pointer(&field))) {
return false
}
c = iter.nextToken()
}
if c != '}' {
iter.ReportError("ReadObjectCB", `object not ended with }`)
return false
}
return true
}
if c == '}' {
return true
}
iter.ReportError("ReadObjectCB", `expect " after }`)
return false
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadObjectCB", `expect { or n`)
return false
}
// ReadMapCB read map with callback, the key can be any string
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken()
if c == '{' {
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field := iter.ReadString()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field")
return false
}
if !callback(iter, field) {
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.ReadString()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field")
return false
}
if !callback(iter, field) {
return false
}
c = iter.nextToken()
}
if c != '}' {
iter.ReportError("ReadMapCB", `object not ended with }`)
return false
}
return true
}
if c == '}' {
return true
}
iter.ReportError("ReadMapCB", `expect " after }`)
return false
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadMapCB", `expect { or n`)
return false
}
func (iter *Iterator) readObjectStart() bool {
c := iter.nextToken()
if c == '{' {
c = iter.nextToken()
if c == '}' {
return false
}
iter.unreadByte()
return true
} else if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return false
}
iter.ReportError("readObjectStart", "expect { or n")
return false
}
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
str := iter.ReadStringAsSlice()
if iter.skipWhitespacesWithoutLoadMore() {
if ret == nil {
ret = make([]byte, len(str))
copy(ret, str)
}
if !iter.loadMore() {
return
}
}
if iter.buf[iter.head] != ':' {
iter.ReportError("readObjectFieldAsBytes", "expect : after object field")
return
}
iter.head++
if iter.skipWhitespacesWithoutLoadMore() {
if ret == nil {
ret = make([]byte, len(str))
copy(ret, str)
}
if !iter.loadMore() {
return
}
}
if ret == nil {
return str
}
return ret
}

127
feature_iter_skip.go Normal file
View File

@ -0,0 +1,127 @@
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")
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()
}
type captureBuffer struct {
startedAt int
captured []byte
}
func (iter *Iterator) startCapture(captureStartedAt int) {
if iter.captured != nil {
panic("already in capture mode")
}
iter.captureStartedAt = captureStartedAt
iter.captured = make([]byte, 0, 32)
}
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
if len(captured) == 0 {
return remaining
}
captured = append(captured, remaining...)
return captured
}
// 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
}
}

144
feature_iter_skip_sloppy.go Normal file
View File

@ -0,0 +1,144 @@
//+build jsoniter-sloppy
package jsoniter
// sloppy but faster implementation, do not validate the input json
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
}
}
}
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) skipString() {
for {
end, escaped := iter.findStringEnd()
if end == -1 {
if !iter.loadMore() {
iter.ReportError("skipString", "incomplete string")
return
}
if escaped {
iter.head = 1 // skip the first char as last char read is \
}
} else {
iter.head = end
return
}
}
}
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
// Tries to find the end of string
// Support if string contains escaped quote symbols.
func (iter *Iterator) findStringEnd() (int, bool) {
escaped := false
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
if !escaped {
return i + 1, false
}
j := i - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return i + 1, true
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
} else if c == '\\' {
escaped = true
}
}
j := iter.tail - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return -1, false // do not end with \
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
return -1, true // end with \
}

View File

@ -0,0 +1,89 @@
//+build !jsoniter-sloppy
package jsoniter
import "fmt"
func (iter *Iterator) skipNumber() {
if !iter.trySkipNumber() {
iter.unreadByte()
iter.ReadFloat32()
}
}
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("ReadString",
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
feature_iter_string.go Normal file
View 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`)
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("ReadString", "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("ReadString",
`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("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
}
}

15
feature_json_number.go Normal file
View File

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

57
feature_pool.go Normal file
View File

@ -0,0 +1,57 @@
package jsoniter
import (
"io"
)
// IteratorPool a thread safe pool of iterators with same configuration
type IteratorPool interface {
BorrowIterator(data []byte) *Iterator
ReturnIterator(iter *Iterator)
}
// StreamPool a thread safe pool of streams with same configuration
type StreamPool interface {
BorrowStream(writer io.Writer) *Stream
ReturnStream(stream *Stream)
}
func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
select {
case stream := <-cfg.streamPool:
stream.Reset(writer)
return stream
default:
return NewStream(cfg, writer, 512)
}
}
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
stream.Error = nil
select {
case cfg.streamPool <- stream:
return
default:
return
}
}
func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator {
select {
case iter := <-cfg.iteratorPool:
iter.ResetBytes(data)
return iter
default:
return ParseBytes(cfg, data)
}
}
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
iter.Error = nil
select {
case cfg.iteratorPool <- iter:
return
default:
return
}
}

691
feature_reflect.go Normal file
View File

@ -0,0 +1,691 @@
package jsoniter
import (
"encoding"
"encoding/json"
"fmt"
"reflect"
"time"
"unsafe"
)
// ValDecoder is an internal type registered to cache as needed.
// Don't confuse jsoniter.ValDecoder with json.Decoder.
// For json.Decoder's adapter, refer to jsoniter.AdapterDecoder(todo link).
//
// Reflection on type to create decoders, which is then cached
// Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions
// 1. create instance of new value, for example *int will need a int to be allocated
// 2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New
// 3. assignment to map, both key and value will be reflect.Value
// For a simple struct binding, it will be reflect.Value free and allocation free
type ValDecoder interface {
Decode(ptr unsafe.Pointer, iter *Iterator)
}
// ValEncoder is an internal type registered to cache as needed.
// Don't confuse jsoniter.ValEncoder with json.Encoder.
// For json.Encoder's adapter, refer to jsoniter.AdapterEncoder(todo godoc link).
type ValEncoder interface {
IsEmpty(ptr unsafe.Pointer) bool
Encode(ptr unsafe.Pointer, stream *Stream)
EncodeInterface(val interface{}, stream *Stream)
}
type checkIsEmpty interface {
IsEmpty(ptr unsafe.Pointer) bool
}
// WriteToStream the default implementation for TypeEncoder method EncodeInterface
func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
e := (*emptyInterface)(unsafe.Pointer(&val))
if e.word == nil {
stream.WriteNil()
return
}
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoder.Encode(unsafe.Pointer(&e.word), stream)
} else {
encoder.Encode(e.word, stream)
}
}
var jsonNumberType reflect.Type
var jsoniterNumberType reflect.Type
var jsonRawMessageType reflect.Type
var jsoniterRawMessageType reflect.Type
var anyType reflect.Type
var marshalerType reflect.Type
var unmarshalerType reflect.Type
var textMarshalerType reflect.Type
var textUnmarshalerType reflect.Type
func init() {
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
jsoniterNumberType = reflect.TypeOf((*Number)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem()
anyType = reflect.TypeOf((*Any)(nil)).Elem()
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
}
type optionalDecoder struct {
valueType reflect.Type
valueDecoder ValDecoder
}
func (decoder *optionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*unsafe.Pointer)(ptr)) = nil
} else {
if *((*unsafe.Pointer)(ptr)) == nil {
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.valueType)
newPtr := extractInterface(value.Interface()).word
decoder.valueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
} else {
//reuse existing instance
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
}
}
}
type deferenceDecoder struct {
// only to deference a pointer
valueType reflect.Type
valueDecoder ValDecoder
}
func (decoder *deferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if *((*unsafe.Pointer)(ptr)) == nil {
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.valueType)
newPtr := extractInterface(value.Interface()).word
decoder.valueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
} else {
//reuse existing instance
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
}
}
type optionalEncoder struct {
valueEncoder ValEncoder
}
func (encoder *optionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
if *((*unsafe.Pointer)(ptr)) == nil {
stream.WriteNil()
} else {
encoder.valueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream)
}
}
func (encoder *optionalEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
if *((*unsafe.Pointer)(ptr)) == nil {
return true
}
return false
}
type placeholderEncoder struct {
cfg *frozenConfig
cacheKey reflect.Type
}
func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
encoder.getRealEncoder().Encode(ptr, stream)
}
func (encoder *placeholderEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.getRealEncoder().IsEmpty(ptr)
}
func (encoder *placeholderEncoder) getRealEncoder() ValEncoder {
for i := 0; i < 30; i++ {
realDecoder := encoder.cfg.getEncoderFromCache(encoder.cacheKey)
_, isPlaceholder := realDecoder.(*placeholderEncoder)
if isPlaceholder {
time.Sleep(time.Second)
} else {
return realDecoder
}
}
panic(fmt.Sprintf("real encoder not found for cache key: %v", encoder.cacheKey))
}
type placeholderDecoder struct {
cfg *frozenConfig
cacheKey reflect.Type
}
func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
for i := 0; i < 30; i++ {
realDecoder := decoder.cfg.getDecoderFromCache(decoder.cacheKey)
_, isPlaceholder := realDecoder.(*placeholderDecoder)
if isPlaceholder {
time.Sleep(time.Second)
} else {
realDecoder.Decode(ptr, iter)
return
}
}
panic(fmt.Sprintf("real decoder not found for cache key: %v", decoder.cacheKey))
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ unsafe.Pointer
word unsafe.Pointer
}
// emptyInterface is the header for an interface with method (not interface{})
type nonEmptyInterface struct {
// see ../runtime/iface.go:/Itab
itab *struct {
ityp unsafe.Pointer // static interface type
typ unsafe.Pointer // dynamic concrete type
link unsafe.Pointer
bad int32
unused int32
fun [100000]unsafe.Pointer // method table
}
word unsafe.Pointer
}
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
func (iter *Iterator) ReadVal(obj interface{}) {
typ := reflect.TypeOf(obj)
cacheKey := typ.Elem()
decoder, err := decoderOfType(iter.cfg, cacheKey)
if err != nil {
iter.Error = err
return
}
e := (*emptyInterface)(unsafe.Pointer(&obj))
decoder.Decode(e.word, iter)
}
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
func (stream *Stream) WriteVal(val interface{}) {
if nil == val {
stream.WriteNil()
return
}
typ := reflect.TypeOf(val)
cacheKey := typ
encoder, err := encoderOfType(stream.cfg, cacheKey)
if err != nil {
stream.Error = err
return
}
encoder.EncodeInterface(val, stream)
}
type prefix string
func (p prefix) addToDecoder(decoder ValDecoder, err error) (ValDecoder, error) {
if err != nil {
return nil, fmt.Errorf("%s: %s", p, err.Error())
}
return decoder, err
}
func (p prefix) addToEncoder(encoder ValEncoder, err error) (ValEncoder, error) {
if err != nil {
return nil, fmt.Errorf("%s: %s", p, err.Error())
}
return encoder, err
}
func decoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
cacheKey := typ
decoder := cfg.getDecoderFromCache(cacheKey)
if decoder != nil {
return decoder, nil
}
decoder = getTypeDecoderFromExtension(typ)
if decoder != nil {
cfg.addDecoderToCache(cacheKey, decoder)
return decoder, nil
}
decoder = &placeholderDecoder{cfg: cfg, cacheKey: cacheKey}
cfg.addDecoderToCache(cacheKey, decoder)
decoder, err := createDecoderOfType(cfg, typ)
for _, extension := range extensions {
decoder = extension.DecorateDecoder(typ, decoder)
}
cfg.addDecoderToCache(cacheKey, decoder)
return decoder, err
}
func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
typeName := typ.String()
if typ == jsonRawMessageType {
return &jsonRawMessageCodec{}, nil
}
if typ == jsoniterRawMessageType {
return &jsoniterRawMessageCodec{}, nil
}
if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil
}
if typ.AssignableTo(jsoniterNumberType) {
return &jsoniterNumberCodec{}, nil
}
if typ.Implements(unmarshalerType) {
templateInterface := reflect.New(typ).Elem().Interface()
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
if typ.Kind() == reflect.Ptr {
decoder = &optionalDecoder{typ.Elem(), decoder}
}
return decoder, nil
}
if reflect.PtrTo(typ).Implements(unmarshalerType) {
templateInterface := reflect.New(typ).Interface()
var decoder ValDecoder = &unmarshalerDecoder{extractInterface(templateInterface)}
return decoder, nil
}
if typ.Implements(textUnmarshalerType) {
templateInterface := reflect.New(typ).Elem().Interface()
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
if typ.Kind() == reflect.Ptr {
decoder = &optionalDecoder{typ.Elem(), decoder}
}
return decoder, nil
}
if reflect.PtrTo(typ).Implements(textUnmarshalerType) {
templateInterface := reflect.New(typ).Interface()
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
return decoder, nil
}
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
sliceDecoder, err := prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ))
if err != nil {
return nil, err
}
return &base64Codec{sliceDecoder: sliceDecoder}, nil
}
if typ.Implements(anyType) {
return &anyCodec{}, nil
}
switch typ.Kind() {
case reflect.String:
if typeName != "string" {
return decoderOfType(cfg, reflect.TypeOf((*string)(nil)).Elem())
}
return &stringCodec{}, nil
case reflect.Int:
if typeName != "int" {
return decoderOfType(cfg, reflect.TypeOf((*int)(nil)).Elem())
}
return &intCodec{}, nil
case reflect.Int8:
if typeName != "int8" {
return decoderOfType(cfg, reflect.TypeOf((*int8)(nil)).Elem())
}
return &int8Codec{}, nil
case reflect.Int16:
if typeName != "int16" {
return decoderOfType(cfg, reflect.TypeOf((*int16)(nil)).Elem())
}
return &int16Codec{}, nil
case reflect.Int32:
if typeName != "int32" {
return decoderOfType(cfg, reflect.TypeOf((*int32)(nil)).Elem())
}
return &int32Codec{}, nil
case reflect.Int64:
if typeName != "int64" {
return decoderOfType(cfg, reflect.TypeOf((*int64)(nil)).Elem())
}
return &int64Codec{}, nil
case reflect.Uint:
if typeName != "uint" {
return decoderOfType(cfg, reflect.TypeOf((*uint)(nil)).Elem())
}
return &uintCodec{}, nil
case reflect.Uint8:
if typeName != "uint8" {
return decoderOfType(cfg, reflect.TypeOf((*uint8)(nil)).Elem())
}
return &uint8Codec{}, nil
case reflect.Uint16:
if typeName != "uint16" {
return decoderOfType(cfg, reflect.TypeOf((*uint16)(nil)).Elem())
}
return &uint16Codec{}, nil
case reflect.Uint32:
if typeName != "uint32" {
return decoderOfType(cfg, reflect.TypeOf((*uint32)(nil)).Elem())
}
return &uint32Codec{}, nil
case reflect.Uintptr:
if typeName != "uintptr" {
return decoderOfType(cfg, reflect.TypeOf((*uintptr)(nil)).Elem())
}
return &uintptrCodec{}, nil
case reflect.Uint64:
if typeName != "uint64" {
return decoderOfType(cfg, reflect.TypeOf((*uint64)(nil)).Elem())
}
return &uint64Codec{}, nil
case reflect.Float32:
if typeName != "float32" {
return decoderOfType(cfg, reflect.TypeOf((*float32)(nil)).Elem())
}
return &float32Codec{}, nil
case reflect.Float64:
if typeName != "float64" {
return decoderOfType(cfg, reflect.TypeOf((*float64)(nil)).Elem())
}
return &float64Codec{}, nil
case reflect.Bool:
if typeName != "bool" {
return decoderOfType(cfg, reflect.TypeOf((*bool)(nil)).Elem())
}
return &boolCodec{}, nil
case reflect.Interface:
if typ.NumMethod() == 0 {
return &emptyInterfaceCodec{}, nil
}
return &nonEmptyInterfaceCodec{}, nil
case reflect.Struct:
return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(cfg, typ))
case reflect.Array:
return prefix("[array]").addToDecoder(decoderOfArray(cfg, typ))
case reflect.Slice:
return prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ))
case reflect.Map:
return prefix("[map]").addToDecoder(decoderOfMap(cfg, typ))
case reflect.Ptr:
return prefix("[optional]").addToDecoder(decoderOfOptional(cfg, typ))
default:
return nil, fmt.Errorf("unsupported type: %v", typ)
}
}
func encoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
cacheKey := typ
encoder := cfg.getEncoderFromCache(cacheKey)
if encoder != nil {
return encoder, nil
}
encoder = getTypeEncoderFromExtension(typ)
if encoder != nil {
cfg.addEncoderToCache(cacheKey, encoder)
return encoder, nil
}
encoder = &placeholderEncoder{cfg: cfg, cacheKey: cacheKey}
cfg.addEncoderToCache(cacheKey, encoder)
encoder, err := createEncoderOfType(cfg, typ)
for _, extension := range extensions {
encoder = extension.DecorateEncoder(typ, encoder)
}
cfg.addEncoderToCache(cacheKey, encoder)
return encoder, err
}
func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
if typ == jsonRawMessageType {
return &jsonRawMessageCodec{}, nil
}
if typ == jsoniterRawMessageType {
return &jsoniterRawMessageCodec{}, nil
}
if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil
}
if typ.AssignableTo(jsoniterNumberType) {
return &jsoniterNumberCodec{}, nil
}
if typ.Implements(marshalerType) {
checkIsEmpty, err := createCheckIsEmpty(typ)
if err != nil {
return nil, err
}
templateInterface := reflect.New(typ).Elem().Interface()
var encoder ValEncoder = &marshalerEncoder{
templateInterface: extractInterface(templateInterface),
checkIsEmpty: checkIsEmpty,
}
if typ.Kind() == reflect.Ptr {
encoder = &optionalEncoder{encoder}
}
return encoder, nil
}
if typ.Implements(textMarshalerType) {
checkIsEmpty, err := createCheckIsEmpty(typ)
if err != nil {
return nil, err
}
templateInterface := reflect.New(typ).Elem().Interface()
var encoder ValEncoder = &textMarshalerEncoder{
templateInterface: extractInterface(templateInterface),
checkIsEmpty: checkIsEmpty,
}
if typ.Kind() == reflect.Ptr {
encoder = &optionalEncoder{encoder}
}
return encoder, nil
}
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
return &base64Codec{}, nil
}
if typ.Implements(anyType) {
return &anyCodec{}, nil
}
return createEncoderOfSimpleType(cfg, typ)
}
func createCheckIsEmpty(typ reflect.Type) (checkIsEmpty, error) {
kind := typ.Kind()
switch kind {
case reflect.String:
return &stringCodec{}, nil
case reflect.Int:
return &intCodec{}, nil
case reflect.Int8:
return &int8Codec{}, nil
case reflect.Int16:
return &int16Codec{}, nil
case reflect.Int32:
return &int32Codec{}, nil
case reflect.Int64:
return &int64Codec{}, nil
case reflect.Uint:
return &uintCodec{}, nil
case reflect.Uint8:
return &uint8Codec{}, nil
case reflect.Uint16:
return &uint16Codec{}, nil
case reflect.Uint32:
return &uint32Codec{}, nil
case reflect.Uintptr:
return &uintptrCodec{}, nil
case reflect.Uint64:
return &uint64Codec{}, nil
case reflect.Float32:
return &float32Codec{}, nil
case reflect.Float64:
return &float64Codec{}, nil
case reflect.Bool:
return &boolCodec{}, nil
case reflect.Interface:
if typ.NumMethod() == 0 {
return &emptyInterfaceCodec{}, nil
}
return &nonEmptyInterfaceCodec{}, nil
case reflect.Struct:
return &structEncoder{}, nil
case reflect.Array:
return &arrayEncoder{}, nil
case reflect.Slice:
return &sliceEncoder{}, nil
case reflect.Map:
return &mapEncoder{}, nil
case reflect.Ptr:
return &optionalEncoder{}, nil
default:
return nil, fmt.Errorf("unsupported type: %v", typ)
}
}
func createEncoderOfSimpleType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
typeName := typ.String()
kind := typ.Kind()
switch kind {
case reflect.String:
if typeName != "string" {
return encoderOfType(cfg, reflect.TypeOf((*string)(nil)).Elem())
}
return &stringCodec{}, nil
case reflect.Int:
if typeName != "int" {
return encoderOfType(cfg, reflect.TypeOf((*int)(nil)).Elem())
}
return &intCodec{}, nil
case reflect.Int8:
if typeName != "int8" {
return encoderOfType(cfg, reflect.TypeOf((*int8)(nil)).Elem())
}
return &int8Codec{}, nil
case reflect.Int16:
if typeName != "int16" {
return encoderOfType(cfg, reflect.TypeOf((*int16)(nil)).Elem())
}
return &int16Codec{}, nil
case reflect.Int32:
if typeName != "int32" {
return encoderOfType(cfg, reflect.TypeOf((*int32)(nil)).Elem())
}
return &int32Codec{}, nil
case reflect.Int64:
if typeName != "int64" {
return encoderOfType(cfg, reflect.TypeOf((*int64)(nil)).Elem())
}
return &int64Codec{}, nil
case reflect.Uint:
if typeName != "uint" {
return encoderOfType(cfg, reflect.TypeOf((*uint)(nil)).Elem())
}
return &uintCodec{}, nil
case reflect.Uint8:
if typeName != "uint8" {
return encoderOfType(cfg, reflect.TypeOf((*uint8)(nil)).Elem())
}
return &uint8Codec{}, nil
case reflect.Uint16:
if typeName != "uint16" {
return encoderOfType(cfg, reflect.TypeOf((*uint16)(nil)).Elem())
}
return &uint16Codec{}, nil
case reflect.Uint32:
if typeName != "uint32" {
return encoderOfType(cfg, reflect.TypeOf((*uint32)(nil)).Elem())
}
return &uint32Codec{}, nil
case reflect.Uintptr:
if typeName != "uintptr" {
return encoderOfType(cfg, reflect.TypeOf((*uintptr)(nil)).Elem())
}
return &uintptrCodec{}, nil
case reflect.Uint64:
if typeName != "uint64" {
return encoderOfType(cfg, reflect.TypeOf((*uint64)(nil)).Elem())
}
return &uint64Codec{}, nil
case reflect.Float32:
if typeName != "float32" {
return encoderOfType(cfg, reflect.TypeOf((*float32)(nil)).Elem())
}
return &float32Codec{}, nil
case reflect.Float64:
if typeName != "float64" {
return encoderOfType(cfg, reflect.TypeOf((*float64)(nil)).Elem())
}
return &float64Codec{}, nil
case reflect.Bool:
if typeName != "bool" {
return encoderOfType(cfg, reflect.TypeOf((*bool)(nil)).Elem())
}
return &boolCodec{}, nil
case reflect.Interface:
if typ.NumMethod() == 0 {
return &emptyInterfaceCodec{}, nil
}
return &nonEmptyInterfaceCodec{}, nil
case reflect.Struct:
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(cfg, typ))
case reflect.Array:
return prefix("[array]").addToEncoder(encoderOfArray(cfg, typ))
case reflect.Slice:
return prefix("[slice]").addToEncoder(encoderOfSlice(cfg, typ))
case reflect.Map:
return prefix("[map]").addToEncoder(encoderOfMap(cfg, typ))
case reflect.Ptr:
return prefix("[optional]").addToEncoder(encoderOfOptional(cfg, typ))
default:
return nil, fmt.Errorf("unsupported type: %v", typ)
}
}
func decoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
elemType := typ.Elem()
decoder, err := decoderOfType(cfg, elemType)
if err != nil {
return nil, err
}
return &optionalDecoder{elemType, decoder}, nil
}
func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
elemType := typ.Elem()
elemEncoder, err := encoderOfType(cfg, elemType)
if err != nil {
return nil, err
}
encoder := &optionalEncoder{elemEncoder}
if elemType.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
return encoder, nil
}
func decoderOfMap(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
decoder, err := decoderOfType(cfg, typ.Elem())
if err != nil {
return nil, err
}
mapInterface := reflect.New(typ).Interface()
return &mapDecoder{typ, typ.Key(), typ.Elem(), decoder, extractInterface(mapInterface)}, nil
}
func extractInterface(val interface{}) emptyInterface {
return *((*emptyInterface)(unsafe.Pointer(&val)))
}
func encoderOfMap(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
elemType := typ.Elem()
encoder, err := encoderOfType(cfg, elemType)
if err != nil {
return nil, err
}
mapInterface := reflect.New(typ).Elem().Interface()
if cfg.sortMapKeys {
return &sortKeysMapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
}
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
}

99
feature_reflect_array.go Normal file
View File

@ -0,0 +1,99 @@
package jsoniter
import (
"fmt"
"io"
"reflect"
"unsafe"
)
func decoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
decoder, err := decoderOfType(cfg, typ.Elem())
if err != nil {
return nil, err
}
return &arrayDecoder{typ, typ.Elem(), decoder}, nil
}
func encoderOfArray(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
encoder, err := encoderOfType(cfg, typ.Elem())
if err != nil {
return nil, err
}
if typ.Elem().Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
return &arrayEncoder{typ, typ.Elem(), encoder}, nil
}
type arrayEncoder struct {
arrayType reflect.Type
elemType reflect.Type
elemEncoder ValEncoder
}
func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteArrayStart()
elemPtr := unsafe.Pointer(ptr)
encoder.elemEncoder.Encode(elemPtr, stream)
for i := 1; i < encoder.arrayType.Len(); i++ {
stream.WriteMore()
elemPtr = unsafe.Pointer(uintptr(elemPtr) + encoder.elemType.Size())
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
}
stream.WriteArrayEnd()
if stream.Error != nil && stream.Error != io.EOF {
stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error())
}
}
func (encoder *arrayEncoder) EncodeInterface(val interface{}, stream *Stream) {
// special optimization for interface{}
e := (*emptyInterface)(unsafe.Pointer(&val))
if e.word == nil {
stream.WriteArrayStart()
stream.WriteNil()
stream.WriteArrayEnd()
return
}
elemType := encoder.arrayType.Elem()
if encoder.arrayType.Len() == 1 && (elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map) {
ptr := uintptr(e.word)
e.word = unsafe.Pointer(&ptr)
}
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoder.Encode(unsafe.Pointer(&e.word), stream)
} else {
encoder.Encode(e.word, stream)
}
}
func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
type arrayDecoder struct {
arrayType reflect.Type
elemType reflect.Type
elemDecoder ValDecoder
}
func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.doDecode(ptr, iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error())
}
}
func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
offset := uintptr(0)
iter.ReadArrayCB(func(iter *Iterator) bool {
if offset < decoder.arrayType.Size() {
decoder.elemDecoder.Decode(unsafe.Pointer(uintptr(ptr)+offset), iter)
offset += decoder.elemType.Size()
} else {
iter.Skip()
}
return true
})
}

View File

@ -0,0 +1,413 @@
package jsoniter
import (
"fmt"
"reflect"
"sort"
"strings"
"unicode"
"unsafe"
)
var typeDecoders = map[string]ValDecoder{}
var fieldDecoders = map[string]ValDecoder{}
var typeEncoders = map[string]ValEncoder{}
var fieldEncoders = map[string]ValEncoder{}
var extensions = []Extension{}
// StructDescriptor describe how should we encode/decode the struct
type StructDescriptor struct {
onePtrEmbedded bool
onePtrOptimization bool
Type reflect.Type
Fields []*Binding
}
// GetField get one field from the descriptor by its name.
// Can not use map here to keep field orders.
func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding {
for _, binding := range structDescriptor.Fields {
if binding.Field.Name == fieldName {
return binding
}
}
return nil
}
// Binding describe how should we encode/decode the struct field
type Binding struct {
levels []int
Field *reflect.StructField
FromNames []string
ToNames []string
Encoder ValEncoder
Decoder ValDecoder
}
// Extension the one for all SPI. Customize encoding/decoding by specifying alternate encoder/decoder.
// Can also rename fields by UpdateStructDescriptor.
type Extension interface {
UpdateStructDescriptor(structDescriptor *StructDescriptor)
CreateDecoder(typ reflect.Type) ValDecoder
CreateEncoder(typ reflect.Type) ValEncoder
DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder
DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder
}
// DummyExtension embed this type get dummy implementation for all methods of Extension
type DummyExtension struct {
}
// UpdateStructDescriptor No-op
func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}
// CreateDecoder No-op
func (extension *DummyExtension) CreateDecoder(typ reflect.Type) ValDecoder {
return nil
}
// CreateEncoder No-op
func (extension *DummyExtension) CreateEncoder(typ reflect.Type) ValEncoder {
return nil
}
// DecorateDecoder No-op
func (extension *DummyExtension) DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder {
return decoder
}
// DecorateEncoder No-op
func (extension *DummyExtension) DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder {
return encoder
}
type funcDecoder struct {
fun DecoderFunc
}
func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.fun(ptr, iter)
}
type funcEncoder struct {
fun EncoderFunc
isEmptyFunc func(ptr unsafe.Pointer) bool
}
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 {
if encoder.isEmptyFunc == nil {
return false
}
return encoder.isEmptyFunc(ptr)
}
// DecoderFunc the function form of TypeDecoder
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
// EncoderFunc the function form of TypeEncoder
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
// RegisterTypeDecoderFunc register TypeDecoder for a type with function
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
typeDecoders[typ] = &funcDecoder{fun}
}
// RegisterTypeDecoder register TypeDecoder for a typ
func RegisterTypeDecoder(typ string, decoder ValDecoder) {
typeDecoders[typ] = decoder
}
// RegisterFieldDecoderFunc register TypeDecoder for a struct field with function
func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) {
RegisterFieldDecoder(typ, field, &funcDecoder{fun})
}
// RegisterFieldDecoder register TypeDecoder for a struct field
func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) {
fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder
}
// RegisterTypeEncoderFunc register TypeEncoder for a type with encode/isEmpty function
func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc}
}
// RegisterTypeEncoder register TypeEncoder for a type
func RegisterTypeEncoder(typ string, encoder ValEncoder) {
typeEncoders[typ] = encoder
}
// RegisterFieldEncoderFunc register TypeEncoder for a struct field with encode/isEmpty function
func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc})
}
// RegisterFieldEncoder register TypeEncoder for a struct field
func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
}
// RegisterExtension register extension
func RegisterExtension(extension Extension) {
extensions = append(extensions, extension)
}
func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
decoder := _getTypeDecoderFromExtension(typ)
if decoder != nil {
for _, extension := range extensions {
decoder = extension.DecorateDecoder(typ, decoder)
}
}
return decoder
}
func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
for _, extension := range extensions {
decoder := extension.CreateDecoder(typ)
if decoder != nil {
return decoder
}
}
typeName := typ.String()
decoder := typeDecoders[typeName]
if decoder != nil {
return decoder
}
if typ.Kind() == reflect.Ptr {
decoder := typeDecoders[typ.Elem().String()]
if decoder != nil {
return &optionalDecoder{typ.Elem(), decoder}
}
}
return nil
}
func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
encoder := _getTypeEncoderFromExtension(typ)
if encoder != nil {
for _, extension := range extensions {
encoder = extension.DecorateEncoder(typ, encoder)
}
}
return encoder
}
func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
for _, extension := range extensions {
encoder := extension.CreateEncoder(typ)
if encoder != nil {
return encoder
}
}
typeName := typ.String()
encoder := typeEncoders[typeName]
if encoder != nil {
return encoder
}
if typ.Kind() == reflect.Ptr {
encoder := typeEncoders[typ.Elem().String()]
if encoder != nil {
return &optionalEncoder{encoder}
}
}
return nil
}
func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) {
embeddedBindings := []*Binding{}
bindings := []*Binding{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
tag := field.Tag.Get(cfg.getTagKey())
tagParts := strings.Split(tag, ",")
if tag == "-" {
continue
}
if field.Anonymous && (tag == "" || tagParts[0] == "") {
if field.Type.Kind() == reflect.Struct {
structDescriptor, err := describeStruct(cfg, field.Type)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
binding.levels = append([]int{i}, binding.levels...)
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
embeddedBindings = append(embeddedBindings, binding)
}
continue
} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
structDescriptor, err := describeStruct(cfg, field.Type.Elem())
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
binding.levels = append([]int{i}, binding.levels...)
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
binding.Encoder = &optionalEncoder{binding.Encoder}
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
embeddedBindings = append(embeddedBindings, binding)
}
continue
}
}
fieldNames := calcFieldNames(field.Name, tagParts[0], tag)
fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
decoder := fieldDecoders[fieldCacheKey]
if decoder == nil {
var err error
decoder, err = decoderOfType(cfg, field.Type)
if err != nil {
return nil, err
}
}
encoder := fieldEncoders[fieldCacheKey]
if encoder == nil {
var err error
encoder, err = encoderOfType(cfg, field.Type)
if err != nil {
return nil, err
}
// map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
}
binding := &Binding{
Field: &field,
FromNames: fieldNames,
ToNames: fieldNames,
Decoder: decoder,
Encoder: encoder,
}
binding.levels = []int{i}
bindings = append(bindings, binding)
}
return createStructDescriptor(cfg, typ, bindings, embeddedBindings), nil
}
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
onePtrEmbedded := false
onePtrOptimization := false
if typ.NumField() == 1 {
firstField := typ.Field(0)
switch firstField.Type.Kind() {
case reflect.Ptr:
if firstField.Anonymous && firstField.Type.Elem().Kind() == reflect.Struct {
onePtrEmbedded = true
}
fallthrough
case reflect.Map:
onePtrOptimization = true
case reflect.Struct:
onePtrOptimization = isStructOnePtr(firstField.Type)
}
}
structDescriptor := &StructDescriptor{
onePtrEmbedded: onePtrEmbedded,
onePtrOptimization: onePtrOptimization,
Type: typ,
Fields: bindings,
}
for _, extension := range extensions {
extension.UpdateStructDescriptor(structDescriptor)
}
processTags(structDescriptor, cfg)
// merge normal & embedded bindings & sort with original order
allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
sort.Sort(allBindings)
structDescriptor.Fields = allBindings
return structDescriptor
}
func isStructOnePtr(typ reflect.Type) bool {
if typ.NumField() == 1 {
firstField := typ.Field(0)
switch firstField.Type.Kind() {
case reflect.Ptr:
return true
case reflect.Map:
return true
case reflect.Struct:
return isStructOnePtr(firstField.Type)
}
}
return false
}
type sortableBindings []*Binding
func (bindings sortableBindings) Len() int {
return len(bindings)
}
func (bindings sortableBindings) Less(i, j int) bool {
left := bindings[i].levels
right := bindings[j].levels
k := 0
for {
if left[k] < right[k] {
return true
} else if left[k] > right[k] {
return false
}
k++
}
}
func (bindings sortableBindings) Swap(i, j int) {
bindings[i], bindings[j] = bindings[j], bindings[i]
}
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
for _, binding := range structDescriptor.Fields {
shouldOmitEmpty := false
tagParts := strings.Split(binding.Field.Tag.Get(cfg.getTagKey()), ",")
for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" {
shouldOmitEmpty = true
} else if tagPart == "string" {
if binding.Field.Type.Kind() == reflect.String {
binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg}
binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg}
} else {
binding.Decoder = &stringModeNumberDecoder{binding.Decoder}
binding.Encoder = &stringModeNumberEncoder{binding.Encoder}
}
}
}
binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder}
binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty}
}
}
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
}

244
feature_reflect_map.go Normal file
View File

@ -0,0 +1,244 @@
package jsoniter
import (
"encoding"
"encoding/json"
"reflect"
"sort"
"strconv"
"unsafe"
)
type mapDecoder struct {
mapType reflect.Type
keyType reflect.Type
elemType reflect.Type
elemDecoder ValDecoder
mapInterface emptyInterface
}
func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
mapInterface := decoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface).Elem()
if iter.ReadNil() {
realVal.Set(reflect.Zero(decoder.mapType))
return
}
if realVal.IsNil() {
realVal.Set(reflect.MakeMap(realVal.Type()))
}
iter.ReadMapCB(func(iter *Iterator, keyStr string) bool {
elem := reflect.New(decoder.elemType)
decoder.elemDecoder.Decode(unsafe.Pointer(elem.Pointer()), iter)
// to put into map, we have to use reflection
keyType := decoder.keyType
// TODO: remove this from loop
switch {
case keyType.Kind() == reflect.String:
realVal.SetMapIndex(reflect.ValueOf(keyStr).Convert(keyType), elem.Elem())
return true
case keyType.Implements(textUnmarshalerType):
textUnmarshaler := reflect.New(keyType.Elem()).Interface().(encoding.TextUnmarshaler)
err := textUnmarshaler.UnmarshalText([]byte(keyStr))
if err != nil {
iter.ReportError("read map key as TextUnmarshaler", err.Error())
return false
}
realVal.SetMapIndex(reflect.ValueOf(textUnmarshaler), elem.Elem())
return true
case reflect.PtrTo(keyType).Implements(textUnmarshalerType):
textUnmarshaler := reflect.New(keyType).Interface().(encoding.TextUnmarshaler)
err := textUnmarshaler.UnmarshalText([]byte(keyStr))
if err != nil {
iter.ReportError("read map key as TextUnmarshaler", err.Error())
return false
}
realVal.SetMapIndex(reflect.ValueOf(textUnmarshaler).Elem(), elem.Elem())
return true
default:
switch keyType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.ParseInt(keyStr, 10, 64)
if err != nil || reflect.Zero(keyType).OverflowInt(n) {
iter.ReportError("read map key as int64", "read int64 failed")
return false
}
realVal.SetMapIndex(reflect.ValueOf(n).Convert(keyType), elem.Elem())
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, err := strconv.ParseUint(keyStr, 10, 64)
if err != nil || reflect.Zero(keyType).OverflowUint(n) {
iter.ReportError("read map key as uint64", "read uint64 failed")
return false
}
realVal.SetMapIndex(reflect.ValueOf(n).Convert(keyType), elem.Elem())
return true
}
}
iter.ReportError("read map key", "unexpected map key type "+keyType.String())
return true
})
}
type mapEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder ValEncoder
mapInterface emptyInterface
}
func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
stream.WriteObjectStart()
for i, key := range realVal.MapKeys() {
if i != 0 {
stream.WriteMore()
}
encodeMapKey(key, stream)
if stream.indention > 0 {
stream.writeTwoBytes(byte(':'), byte(' '))
} else {
stream.writeByte(':')
}
val := realVal.MapIndex(key).Interface()
encoder.elemEncoder.EncodeInterface(val, stream)
}
stream.WriteObjectEnd()
}
func encodeMapKey(key reflect.Value, stream *Stream) {
if key.Kind() == reflect.String {
stream.WriteString(key.String())
return
}
if tm, ok := key.Interface().(encoding.TextMarshaler); ok {
buf, err := tm.MarshalText()
if err != nil {
stream.Error = err
return
}
stream.writeByte('"')
stream.Write(buf)
stream.writeByte('"')
return
}
switch key.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
stream.writeByte('"')
stream.WriteInt64(key.Int())
stream.writeByte('"')
return
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
stream.writeByte('"')
stream.WriteUint64(key.Uint())
stream.writeByte('"')
return
}
stream.Error = &json.UnsupportedTypeError{Type: key.Type()}
}
func (encoder *mapEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
return realVal.Len() == 0
}
type sortKeysMapEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder ValEncoder
mapInterface emptyInterface
}
func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
// Extract and sort the keys.
keys := realVal.MapKeys()
sv := stringValues(make([]reflectWithString, len(keys)))
for i, v := range keys {
sv[i].v = v
if err := sv[i].resolve(); err != nil {
stream.Error = err
return
}
}
sort.Sort(sv)
stream.WriteObjectStart()
for i, key := range sv {
if i != 0 {
stream.WriteMore()
}
stream.WriteVal(key.s) // might need html escape, so can not WriteString directly
if stream.indention > 0 {
stream.writeTwoBytes(byte(':'), byte(' '))
} else {
stream.writeByte(':')
}
val := realVal.MapIndex(key.v).Interface()
encoder.elemEncoder.EncodeInterface(val, stream)
}
stream.WriteObjectEnd()
}
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflectWithString
type reflectWithString struct {
v reflect.Value
s string
}
func (w *reflectWithString) resolve() error {
if w.v.Kind() == reflect.String {
w.s = w.v.String()
return nil
}
if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
buf, err := tm.MarshalText()
w.s = string(buf)
return err
}
switch w.v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
w.s = strconv.FormatInt(w.v.Int(), 10)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
w.s = strconv.FormatUint(w.v.Uint(), 10)
return nil
}
return &json.UnsupportedTypeError{Type: w.v.Type()}
}
func (sv stringValues) Len() int { return len(sv) }
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
func (sv stringValues) Less(i, j int) bool { return sv[i].s < sv[j].s }
func (encoder *sortKeysMapEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *sortKeysMapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
return realVal.Len() == 0
}

672
feature_reflect_native.go Normal file
View File

@ -0,0 +1,672 @@
package jsoniter
import (
"encoding"
"encoding/base64"
"encoding/json"
"unsafe"
)
type stringCodec struct {
}
func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = iter.ReadString()
}
func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
stream.WriteString(str)
}
func (codec *stringCodec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *stringCodec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*string)(ptr)) == ""
}
type intCodec struct {
}
func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int)(ptr)) = iter.ReadInt()
}
func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt(*((*int)(ptr)))
}
func (codec *intCodec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *intCodec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*int)(ptr)) == 0
}
type uintptrCodec struct {
}
func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
}
func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint64(uint64(*((*uintptr)(ptr))))
}
func (codec *uintptrCodec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uintptrCodec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uintptr)(ptr)) == 0
}
type int8Codec struct {
}
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int8)(ptr)) = iter.ReadInt8()
}
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt8(*((*int8)(ptr)))
}
func (codec *int8Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *int8Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*int8)(ptr)) == 0
}
type int16Codec struct {
}
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int16)(ptr)) = iter.ReadInt16()
}
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt16(*((*int16)(ptr)))
}
func (codec *int16Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *int16Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*int16)(ptr)) == 0
}
type int32Codec struct {
}
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int32)(ptr)) = iter.ReadInt32()
}
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt32(*((*int32)(ptr)))
}
func (codec *int32Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *int32Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*int32)(ptr)) == 0
}
type int64Codec struct {
}
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int64)(ptr)) = iter.ReadInt64()
}
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt64(*((*int64)(ptr)))
}
func (codec *int64Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *int64Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*int64)(ptr)) == 0
}
type uintCodec struct {
}
func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint)(ptr)) = iter.ReadUint()
}
func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint(*((*uint)(ptr)))
}
func (codec *uintCodec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uintCodec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uint)(ptr)) == 0
}
type uint8Codec struct {
}
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint8)(ptr)) = iter.ReadUint8()
}
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint8(*((*uint8)(ptr)))
}
func (codec *uint8Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uint8Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uint8)(ptr)) == 0
}
type uint16Codec struct {
}
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint16)(ptr)) = iter.ReadUint16()
}
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint16(*((*uint16)(ptr)))
}
func (codec *uint16Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uint16Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uint16)(ptr)) == 0
}
type uint32Codec struct {
}
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint32)(ptr)) = iter.ReadUint32()
}
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint32(*((*uint32)(ptr)))
}
func (codec *uint32Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uint32Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uint32)(ptr)) == 0
}
type uint64Codec struct {
}
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint64)(ptr)) = iter.ReadUint64()
}
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint64(*((*uint64)(ptr)))
}
func (codec *uint64Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *uint64Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*uint64)(ptr)) == 0
}
type float32Codec struct {
}
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float32)(ptr)) = iter.ReadFloat32()
}
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat32(*((*float32)(ptr)))
}
func (codec *float32Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *float32Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float32)(ptr)) == 0
}
type float64Codec struct {
}
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float64)(ptr)) = iter.ReadFloat64()
}
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat64(*((*float64)(ptr)))
}
func (codec *float64Codec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *float64Codec) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float64)(ptr)) == 0
}
type boolCodec struct {
}
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*bool)(ptr)) = iter.ReadBool()
}
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteBool(*((*bool)(ptr)))
}
func (codec *boolCodec) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, codec)
}
func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
return !(*((*bool)(ptr)))
}
type emptyInterfaceCodec struct {
}
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.Read()
}
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteVal(*((*interface{})(ptr)))
}
func (codec *emptyInterfaceCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteVal(val)
}
func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
return ptr == nil
}
type nonEmptyInterfaceCodec struct {
}
func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
if nonEmptyInterface.itab == nil {
iter.ReportError("read non-empty interface", "do not know which concrete type to decode to")
return
}
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
iter.ReadVal(&i)
nonEmptyInterface.word = e.word
}
func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
stream.WriteVal(i)
}
func (codec *nonEmptyInterfaceCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteVal(val)
}
func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
return nonEmptyInterface.word == nil
}
type anyCodec struct {
}
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Any)(ptr)) = iter.ReadAny()
}
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
(*((*Any)(ptr))).WriteTo(stream)
}
func (codec *anyCodec) EncodeInterface(val interface{}, stream *Stream) {
(val.(Any)).WriteTo(stream)
}
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
return (*((*Any)(ptr))).Size() == 0
}
type jsonNumberCodec struct {
}
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
}
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*json.Number)(ptr))))
}
func (codec *jsonNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(json.Number)))
}
func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.Number)(ptr))) == 0
}
type jsoniterNumberCodec struct {
}
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
}
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*Number)(ptr))))
}
func (codec *jsoniterNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(Number)))
}
func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*Number)(ptr))) == 0
}
type jsonRawMessageCodec struct {
}
func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes())
}
func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
}
func (codec *jsonRawMessageCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(json.RawMessage)))
}
func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.RawMessage)(ptr))) == 0
}
type jsoniterRawMessageCodec struct {
}
func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*RawMessage)(ptr)) = RawMessage(iter.SkipAndReturnBytes())
}
func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*RawMessage)(ptr))))
}
func (codec *jsoniterRawMessageCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(RawMessage)))
}
func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*RawMessage)(ptr))) == 0
}
type base64Codec struct {
sliceDecoder ValDecoder
}
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
ptrSlice := (*sliceHeader)(ptr)
ptrSlice.Len = 0
ptrSlice.Cap = 0
ptrSlice.Data = nil
return
}
switch iter.WhatIsNext() {
case StringValue:
encoding := base64.StdEncoding
src := iter.SkipAndReturnBytes()
src = src[1 : len(src)-1]
decodedLen := encoding.DecodedLen(len(src))
dst := make([]byte, decodedLen)
len, err := encoding.Decode(dst, src)
if err != nil {
iter.ReportError("decode base64", err.Error())
} else {
dst = dst[:len]
dstSlice := (*sliceHeader)(unsafe.Pointer(&dst))
ptrSlice := (*sliceHeader)(ptr)
ptrSlice.Data = dstSlice.Data
ptrSlice.Cap = dstSlice.Cap
ptrSlice.Len = dstSlice.Len
}
case ArrayValue:
codec.sliceDecoder.Decode(ptr, iter)
default:
iter.ReportError("base64Codec", "invalid input")
}
}
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
src := *((*[]byte)(ptr))
if len(src) == 0 {
stream.WriteNil()
return
}
encoding := base64.StdEncoding
stream.writeByte('"')
toGrow := encoding.EncodedLen(len(src))
stream.ensure(toGrow)
encoding.Encode(stream.buf[stream.n:], src)
stream.n += toGrow
stream.writeByte('"')
}
func (codec *base64Codec) EncodeInterface(val interface{}, stream *Stream) {
ptr := extractInterface(val).word
src := *((*[]byte)(ptr))
if len(src) == 0 {
stream.WriteNil()
return
}
encoding := base64.StdEncoding
stream.writeByte('"')
toGrow := encoding.EncodedLen(len(src))
stream.ensure(toGrow)
encoding.Encode(stream.buf[stream.n:], src)
stream.n += toGrow
stream.writeByte('"')
}
func (codec *base64Codec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*[]byte)(ptr))) == 0
}
type stringModeNumberDecoder struct {
elemDecoder ValDecoder
}
func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
c := iter.nextToken()
if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`)
return
}
decoder.elemDecoder.Decode(ptr, iter)
if iter.Error != nil {
return
}
c = iter.readByte()
if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`)
return
}
}
type stringModeStringDecoder struct {
elemDecoder ValDecoder
cfg *frozenConfig
}
func (decoder *stringModeStringDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.elemDecoder.Decode(ptr, iter)
str := *((*string)(ptr))
tempIter := decoder.cfg.BorrowIterator([]byte(str))
defer decoder.cfg.ReturnIterator(tempIter)
*((*string)(ptr)) = tempIter.ReadString()
}
type stringModeNumberEncoder struct {
elemEncoder ValEncoder
}
func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.writeByte('"')
encoder.elemEncoder.Encode(ptr, stream)
stream.writeByte('"')
}
func (encoder *stringModeNumberEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.elemEncoder.IsEmpty(ptr)
}
type stringModeStringEncoder struct {
elemEncoder ValEncoder
cfg *frozenConfig
}
func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
tempStream := encoder.cfg.BorrowStream(nil)
defer encoder.cfg.ReturnStream(tempStream)
encoder.elemEncoder.Encode(ptr, tempStream)
stream.WriteString(string(tempStream.Buffer()))
}
func (encoder *stringModeStringEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.elemEncoder.IsEmpty(ptr)
}
type marshalerEncoder struct {
templateInterface emptyInterface
checkIsEmpty checkIsEmpty
}
func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface := encoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler)
bytes, err := marshaler.MarshalJSON()
if err != nil {
stream.Error = err
} else {
stream.Write(bytes)
}
}
func (encoder *marshalerEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.checkIsEmpty.IsEmpty(ptr)
}
type textMarshalerEncoder struct {
templateInterface emptyInterface
checkIsEmpty checkIsEmpty
}
func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface := encoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(encoding.TextMarshaler)
bytes, err := marshaler.MarshalText()
if err != nil {
stream.Error = err
} else {
stream.WriteString(string(bytes))
}
}
func (encoder *textMarshalerEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.checkIsEmpty.IsEmpty(ptr)
}
type unmarshalerDecoder struct {
templateInterface emptyInterface
}
func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
templateInterface := decoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
unmarshaler := (*realInterface).(json.Unmarshaler)
iter.nextToken()
iter.unreadByte() // skip spaces
bytes := iter.SkipAndReturnBytes()
err := unmarshaler.UnmarshalJSON(bytes)
if err != nil {
iter.ReportError("unmarshalerDecoder", err.Error())
}
}
type textUnmarshalerDecoder struct {
templateInterface emptyInterface
}
func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
templateInterface := decoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
unmarshaler := (*realInterface).(encoding.TextUnmarshaler)
str := iter.ReadString()
err := unmarshaler.UnmarshalText([]byte(str))
if err != nil {
iter.ReportError("textUnmarshalerDecoder", err.Error())
}
}

196
feature_reflect_object.go Normal file
View File

@ -0,0 +1,196 @@
package jsoniter
import (
"fmt"
"io"
"reflect"
"strings"
"unsafe"
)
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
type bindingTo struct {
binding *Binding
toName string
ignored bool
}
orderedBindings := []*bindingTo{}
structDescriptor, err := describeStruct(cfg, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
for _, toName := range binding.ToNames {
new := &bindingTo{
binding: binding,
toName: toName,
}
for _, old := range orderedBindings {
if old.toName != toName {
continue
}
old.ignored, new.ignored = resolveConflictBinding(cfg, old.binding, new.binding)
}
orderedBindings = append(orderedBindings, new)
}
}
if len(orderedBindings) == 0 {
return &emptyStructEncoder{}, nil
}
finalOrderedFields := []structFieldTo{}
for _, bindingTo := range orderedBindings {
if !bindingTo.ignored {
finalOrderedFields = append(finalOrderedFields, structFieldTo{
encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
toName: bindingTo.toName,
})
}
}
return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil
}
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
newTagged := new.Field.Tag.Get(cfg.getTagKey()) != ""
oldTagged := old.Field.Tag.Get(cfg.getTagKey()) != ""
if newTagged {
if oldTagged {
if len(old.levels) > len(new.levels) {
return true, false
} else if len(new.levels) > len(old.levels) {
return false, true
} else {
return true, true
}
} else {
return true, false
}
} else {
if oldTagged {
return true, false
}
if len(old.levels) > len(new.levels) {
return true, false
} else if len(new.levels) > len(old.levels) {
return false, true
} else {
return true, true
}
}
}
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
bindings := map[string]*Binding{}
structDescriptor, err := describeStruct(cfg, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
for _, fromName := range binding.FromNames {
old := bindings[fromName]
if old == nil {
bindings[fromName] = binding
continue
}
ignoreOld, ignoreNew := resolveConflictBinding(cfg, old, binding)
if ignoreOld {
delete(bindings, fromName)
}
if !ignoreNew {
bindings[fromName] = binding
}
}
}
fields := map[string]*structFieldDecoder{}
for k, binding := range bindings {
fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
}
return createStructDecoder(typ, fields)
}
type structFieldEncoder struct {
field *reflect.StructField
fieldEncoder ValEncoder
omitempty bool
}
func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
encoder.fieldEncoder.Encode(fieldPtr, stream)
if stream.Error != nil && stream.Error != io.EOF {
stream.Error = fmt.Errorf("%s: %s", encoder.field.Name, stream.Error.Error())
}
}
func (encoder *structFieldEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
return encoder.fieldEncoder.IsEmpty(fieldPtr)
}
type structEncoder struct {
onePtrEmbedded bool
onePtrOptimization bool
fields []structFieldTo
}
type structFieldTo struct {
encoder *structFieldEncoder
toName string
}
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteObjectStart()
isNotFirst := false
for _, field := range encoder.fields {
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
continue
}
if isNotFirst {
stream.WriteMore()
}
stream.WriteObjectField(field.toName)
field.encoder.Encode(ptr, stream)
isNotFirst = true
}
stream.WriteObjectEnd()
}
func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {
e := (*emptyInterface)(unsafe.Pointer(&val))
if encoder.onePtrOptimization {
if e.word == nil && encoder.onePtrEmbedded {
stream.WriteObjectStart()
stream.WriteObjectEnd()
return
}
ptr := uintptr(e.word)
e.word = unsafe.Pointer(&ptr)
}
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoder.Encode(unsafe.Pointer(&e.word), stream)
} else {
encoder.Encode(e.word, stream)
}
}
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
type emptyStructEncoder struct {
}
func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteEmptyObject()
}
func (encoder *emptyStructEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}

149
feature_reflect_slice.go Normal file
View File

@ -0,0 +1,149 @@
package jsoniter
import (
"fmt"
"io"
"reflect"
"unsafe"
)
func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
decoder, err := decoderOfType(cfg, typ.Elem())
if err != nil {
return nil, err
}
return &sliceDecoder{typ, typ.Elem(), decoder}, nil
}
func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
encoder, err := encoderOfType(cfg, typ.Elem())
if err != nil {
return nil, err
}
if typ.Elem().Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
}
type sliceEncoder struct {
sliceType reflect.Type
elemType reflect.Type
elemEncoder ValEncoder
}
func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
slice := (*sliceHeader)(ptr)
if slice.Data == nil {
stream.WriteNil()
return
}
if slice.Len == 0 {
stream.WriteEmptyArray()
return
}
stream.WriteArrayStart()
elemPtr := unsafe.Pointer(slice.Data)
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
for i := 1; i < slice.Len; i++ {
stream.WriteMore()
elemPtr = unsafe.Pointer(uintptr(elemPtr) + encoder.elemType.Size())
encoder.elemEncoder.Encode(unsafe.Pointer(elemPtr), stream)
}
stream.WriteArrayEnd()
if stream.Error != nil && stream.Error != io.EOF {
stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error())
}
}
func (encoder *sliceEncoder) EncodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
slice := (*sliceHeader)(ptr)
return slice.Len == 0
}
type sliceDecoder struct {
sliceType reflect.Type
elemType reflect.Type
elemDecoder ValDecoder
}
// sliceHeader is a safe version of SliceHeader used within this package.
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.doDecode(ptr, iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error())
}
}
func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
slice := (*sliceHeader)(ptr)
if iter.ReadNil() {
slice.Len = 0
slice.Cap = 0
slice.Data = nil
return
}
reuseSlice(slice, decoder.sliceType, 4)
slice.Len = 0
offset := uintptr(0)
iter.ReadArrayCB(func(iter *Iterator) bool {
growOne(slice, decoder.sliceType, decoder.elemType)
decoder.elemDecoder.Decode(unsafe.Pointer(uintptr(slice.Data)+offset), iter)
offset += decoder.elemType.Size()
return true
})
}
// grow grows the slice s so that it can hold extra more values, allocating
// more capacity if needed. It also returns the old and new slice lengths.
func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Type) {
newLen := slice.Len + 1
if newLen <= slice.Cap {
slice.Len = newLen
return
}
newCap := slice.Cap
if newCap == 0 {
newCap = 1
} else {
for newCap < newLen {
if slice.Len < 1024 {
newCap += newCap
} else {
newCap += newCap / 4
}
}
}
newVal := reflect.MakeSlice(sliceType, newLen, newCap)
dst := unsafe.Pointer(newVal.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.Data = dst
slice.Len = newLen
slice.Cap = newCap
}
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
if expectedCap <= slice.Cap {
return
}
newVal := reflect.MakeSlice(sliceType, 0, expectedCap)
dst := unsafe.Pointer(newVal.Pointer())
slice.Data = dst
slice.Cap = expectedCap
}

View File

@ -0,0 +1,916 @@
package jsoniter
import (
"fmt"
"io"
"reflect"
"strings"
"unsafe"
)
func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (ValDecoder, error) {
knownHash := map[int32]struct{}{
0: {},
}
switch len(fields) {
case 0:
return &skipObjectDecoder{typ}, nil
case 1:
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder}, nil
}
case 2:
var fieldHash1 int32
var fieldHash2 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldHash1 == 0 {
fieldHash1 = fieldHash
fieldDecoder1 = fieldDecoder
} else {
fieldHash2 = fieldHash
fieldDecoder2 = fieldDecoder
}
}
return &twoFieldsStructDecoder{typ, fieldHash1, fieldDecoder1, fieldHash2, fieldDecoder2}, nil
case 3:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
}
}
return &threeFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
case 4:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
}
}
return &fourFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
case 5:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
}
}
return &fiveFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
case 6:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldName6 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else if fieldName5 == 0 {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
} else {
fieldName6 = fieldHash
fieldDecoder6 = fieldDecoder
}
}
return &sixFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
case 7:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldName6 int32
var fieldName7 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
var fieldDecoder7 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else if fieldName5 == 0 {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
} else if fieldName6 == 0 {
fieldName6 = fieldHash
fieldDecoder6 = fieldDecoder
} else {
fieldName7 = fieldHash
fieldDecoder7 = fieldDecoder
}
}
return &sevenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}, nil
case 8:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldName6 int32
var fieldName7 int32
var fieldName8 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
var fieldDecoder7 *structFieldDecoder
var fieldDecoder8 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else if fieldName5 == 0 {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
} else if fieldName6 == 0 {
fieldName6 = fieldHash
fieldDecoder6 = fieldDecoder
} else if fieldName7 == 0 {
fieldName7 = fieldHash
fieldDecoder7 = fieldDecoder
} else {
fieldName8 = fieldHash
fieldDecoder8 = fieldDecoder
}
}
return &eightFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
case 9:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldName6 int32
var fieldName7 int32
var fieldName8 int32
var fieldName9 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
var fieldDecoder7 *structFieldDecoder
var fieldDecoder8 *structFieldDecoder
var fieldDecoder9 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else if fieldName5 == 0 {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
} else if fieldName6 == 0 {
fieldName6 = fieldHash
fieldDecoder6 = fieldDecoder
} else if fieldName7 == 0 {
fieldName7 = fieldHash
fieldDecoder7 = fieldDecoder
} else if fieldName8 == 0 {
fieldName8 = fieldHash
fieldDecoder8 = fieldDecoder
} else {
fieldName9 = fieldHash
fieldDecoder9 = fieldDecoder
}
}
return &nineFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
case 10:
var fieldName1 int32
var fieldName2 int32
var fieldName3 int32
var fieldName4 int32
var fieldName5 int32
var fieldName6 int32
var fieldName7 int32
var fieldName8 int32
var fieldName9 int32
var fieldName10 int32
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
var fieldDecoder7 *structFieldDecoder
var fieldDecoder8 *structFieldDecoder
var fieldDecoder9 *structFieldDecoder
var fieldDecoder10 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields}, nil
}
knownHash[fieldHash] = struct{}{}
if fieldName1 == 0 {
fieldName1 = fieldHash
fieldDecoder1 = fieldDecoder
} else if fieldName2 == 0 {
fieldName2 = fieldHash
fieldDecoder2 = fieldDecoder
} else if fieldName3 == 0 {
fieldName3 = fieldHash
fieldDecoder3 = fieldDecoder
} else if fieldName4 == 0 {
fieldName4 = fieldHash
fieldDecoder4 = fieldDecoder
} else if fieldName5 == 0 {
fieldName5 = fieldHash
fieldDecoder5 = fieldDecoder
} else if fieldName6 == 0 {
fieldName6 = fieldHash
fieldDecoder6 = fieldDecoder
} else if fieldName7 == 0 {
fieldName7 = fieldHash
fieldDecoder7 = fieldDecoder
} else if fieldName8 == 0 {
fieldName8 = fieldHash
fieldDecoder8 = fieldDecoder
} else if fieldName9 == 0 {
fieldName9 = fieldHash
fieldDecoder9 = fieldDecoder
} else {
fieldName10 = fieldHash
fieldDecoder10 = fieldDecoder
}
}
return &tenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}, nil
}
return &generalStructDecoder{typ, fields}, nil
}
type generalStructDecoder struct {
typ reflect.Type
fields map[string]*structFieldDecoder
}
func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
fieldBytes := iter.readObjectFieldAsBytes()
field := *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder := decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil {
iter.Skip()
} else {
fieldDecoder.Decode(ptr, iter)
}
for iter.nextToken() == ',' {
fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil {
iter.Skip()
} else {
fieldDecoder.Decode(ptr, iter)
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type skipObjectDecoder struct {
typ reflect.Type
}
func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
valueType := iter.WhatIsNext()
if valueType != ObjectValue && valueType != NilValue {
iter.ReportError("skipObjectDecoder", "expect object or null")
return
}
iter.Skip()
}
type oneFieldStructDecoder struct {
typ reflect.Type
fieldHash int32
fieldDecoder *structFieldDecoder
}
func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
if iter.readFieldHash() == decoder.fieldHash {
decoder.fieldDecoder.Decode(ptr, iter)
} else {
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type twoFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
}
func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type threeFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
}
func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type fourFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
}
func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type fiveFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
}
func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type sixFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
}
func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
case decoder.fieldHash6:
decoder.fieldDecoder6.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type sevenFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
}
func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
case decoder.fieldHash6:
decoder.fieldDecoder6.Decode(ptr, iter)
case decoder.fieldHash7:
decoder.fieldDecoder7.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type eightFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
}
func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
case decoder.fieldHash6:
decoder.fieldDecoder6.Decode(ptr, iter)
case decoder.fieldHash7:
decoder.fieldDecoder7.Decode(ptr, iter)
case decoder.fieldHash8:
decoder.fieldDecoder8.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type nineFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
fieldHash9 int32
fieldDecoder9 *structFieldDecoder
}
func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
case decoder.fieldHash6:
decoder.fieldDecoder6.Decode(ptr, iter)
case decoder.fieldHash7:
decoder.fieldDecoder7.Decode(ptr, iter)
case decoder.fieldHash8:
decoder.fieldDecoder8.Decode(ptr, iter)
case decoder.fieldHash9:
decoder.fieldDecoder9.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type tenFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
fieldHash9 int32
fieldDecoder9 *structFieldDecoder
fieldHash10 int32
fieldDecoder10 *structFieldDecoder
}
func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.readObjectStart() {
return
}
for {
switch iter.readFieldHash() {
case decoder.fieldHash1:
decoder.fieldDecoder1.Decode(ptr, iter)
case decoder.fieldHash2:
decoder.fieldDecoder2.Decode(ptr, iter)
case decoder.fieldHash3:
decoder.fieldDecoder3.Decode(ptr, iter)
case decoder.fieldHash4:
decoder.fieldDecoder4.Decode(ptr, iter)
case decoder.fieldHash5:
decoder.fieldDecoder5.Decode(ptr, iter)
case decoder.fieldHash6:
decoder.fieldDecoder6.Decode(ptr, iter)
case decoder.fieldHash7:
decoder.fieldDecoder7.Decode(ptr, iter)
case decoder.fieldHash8:
decoder.fieldDecoder8.Decode(ptr, iter)
case decoder.fieldHash9:
decoder.fieldDecoder9.Decode(ptr, iter)
case decoder.fieldHash10:
decoder.fieldDecoder10.Decode(ptr, iter)
default:
iter.Skip()
}
if iter.isObjectEnd() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.typ, iter.Error.Error())
}
}
type structFieldDecoder struct {
field *reflect.StructField
fieldDecoder ValDecoder
}
func (decoder *structFieldDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
fieldPtr := unsafe.Pointer(uintptr(ptr) + decoder.field.Offset)
decoder.fieldDecoder.Decode(fieldPtr, iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%s: %s", decoder.field.Name, iter.Error.Error())
}
}

305
feature_stream.go Normal file
View File

@ -0,0 +1,305 @@
package jsoniter
import (
"io"
)
// Stream is a io.Writer like object, with JSON specific write functions.
// Error is not returned as return value, but stored as Error member on this stream instance.
type Stream struct {
cfg *frozenConfig
out io.Writer
buf []byte
n int
Error error
indention int
}
// NewStream create new stream instance.
// cfg can be jsoniter.ConfigDefault.
// out can be nil if write to internal buffer.
// bufSize is the initial size for the internal buffer in bytes.
func NewStream(cfg API, out io.Writer, bufSize int) *Stream {
return &Stream{
cfg: cfg.(*frozenConfig),
out: out,
buf: make([]byte, bufSize),
n: 0,
Error: nil,
indention: 0,
}
}
// Pool returns a pool can provide more stream with same configuration
func (stream *Stream) Pool() StreamPool {
return stream.cfg
}
// Reset reuse this stream instance by assign a new writer
func (stream *Stream) Reset(out io.Writer) {
stream.out = out
stream.n = 0
}
// Available returns how many bytes are unused in the buffer.
func (stream *Stream) Available() int {
return len(stream.buf) - stream.n
}
// Buffered returns the number of bytes that have been written into the current buffer.
func (stream *Stream) Buffered() int {
return stream.n
}
// Buffer if writer is nil, use this method to take the result
func (stream *Stream) Buffer() []byte {
return stream.buf[:stream.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 (stream *Stream) Write(p []byte) (nn int, err error) {
for len(p) > stream.Available() && stream.Error == nil {
if stream.out == nil {
stream.growAtLeast(len(p))
} else {
var n int
if stream.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, stream.Error = stream.out.Write(p)
} else {
n = copy(stream.buf[stream.n:], p)
stream.n += n
stream.Flush()
}
nn += n
p = p[n:]
}
}
if stream.Error != nil {
return nn, stream.Error
}
n := copy(stream.buf[stream.n:], p)
stream.n += n
nn += n
return nn, nil
}
// WriteByte writes a single byte.
func (stream *Stream) writeByte(c byte) {
if stream.Error != nil {
return
}
if stream.Available() < 1 {
stream.growAtLeast(1)
}
stream.buf[stream.n] = c
stream.n++
}
func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) {
if stream.Error != nil {
return
}
if stream.Available() < 2 {
stream.growAtLeast(2)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.n += 2
}
func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) {
if stream.Error != nil {
return
}
if stream.Available() < 3 {
stream.growAtLeast(3)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.n += 3
}
func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) {
if stream.Error != nil {
return
}
if stream.Available() < 4 {
stream.growAtLeast(4)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.buf[stream.n+3] = c4
stream.n += 4
}
func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) {
if stream.Error != nil {
return
}
if stream.Available() < 5 {
stream.growAtLeast(5)
}
stream.buf[stream.n] = c1
stream.buf[stream.n+1] = c2
stream.buf[stream.n+2] = c3
stream.buf[stream.n+3] = c4
stream.buf[stream.n+4] = c5
stream.n += 5
}
// Flush writes any buffered data to the underlying io.Writer.
func (stream *Stream) Flush() error {
if stream.out == nil {
return nil
}
if stream.Error != nil {
return stream.Error
}
if stream.n == 0 {
return nil
}
n, err := stream.out.Write(stream.buf[0:stream.n])
if n < stream.n && err == nil {
err = io.ErrShortWrite
}
if err != nil {
if n > 0 && n < stream.n {
copy(stream.buf[0:stream.n-n], stream.buf[n:stream.n])
}
stream.n -= n
stream.Error = err
return err
}
stream.n = 0
return nil
}
func (stream *Stream) ensure(minimal int) {
available := stream.Available()
if available < minimal {
stream.growAtLeast(minimal)
}
}
func (stream *Stream) growAtLeast(minimal int) {
if stream.out != nil {
stream.Flush()
}
toGrow := len(stream.buf)
if toGrow < minimal {
toGrow = minimal
}
newBuf := make([]byte, len(stream.buf)+toGrow)
copy(newBuf, stream.Buffer())
stream.buf = newBuf
}
// WriteRaw write string out without quotes, just like []byte
func (stream *Stream) WriteRaw(s string) {
stream.ensure(len(s))
if stream.Error != nil {
return
}
n := copy(stream.buf[stream.n:], s)
stream.n += n
}
// WriteNil write null to stream
func (stream *Stream) WriteNil() {
stream.writeFourBytes('n', 'u', 'l', 'l')
}
// WriteTrue write true to stream
func (stream *Stream) WriteTrue() {
stream.writeFourBytes('t', 'r', 'u', 'e')
}
// WriteFalse write false to stream
func (stream *Stream) WriteFalse() {
stream.writeFiveBytes('f', 'a', 'l', 's', 'e')
}
// WriteBool write true or false into stream
func (stream *Stream) WriteBool(val bool) {
if val {
stream.WriteTrue()
} else {
stream.WriteFalse()
}
}
// WriteObjectStart write { with possible indention
func (stream *Stream) WriteObjectStart() {
stream.indention += stream.cfg.indentionStep
stream.writeByte('{')
stream.writeIndention(0)
}
// WriteObjectField write "field": with possible indention
func (stream *Stream) WriteObjectField(field string) {
stream.WriteString(field)
if stream.indention > 0 {
stream.writeTwoBytes(':', ' ')
} else {
stream.writeByte(':')
}
}
// WriteObjectEnd write } with possible indention
func (stream *Stream) WriteObjectEnd() {
stream.writeIndention(stream.cfg.indentionStep)
stream.indention -= stream.cfg.indentionStep
stream.writeByte('}')
}
// WriteEmptyObject write {}
func (stream *Stream) WriteEmptyObject() {
stream.writeByte('{')
stream.writeByte('}')
}
// WriteMore write , with possible indention
func (stream *Stream) WriteMore() {
stream.writeByte(',')
stream.writeIndention(0)
}
// WriteArrayStart write [ with possible indention
func (stream *Stream) WriteArrayStart() {
stream.indention += stream.cfg.indentionStep
stream.writeByte('[')
stream.writeIndention(0)
}
// WriteEmptyArray write []
func (stream *Stream) WriteEmptyArray() {
stream.writeByte('[')
stream.writeByte(']')
}
// WriteArrayEnd write ] with possible indention
func (stream *Stream) WriteArrayEnd() {
stream.writeIndention(stream.cfg.indentionStep)
stream.indention -= stream.cfg.indentionStep
stream.writeByte(']')
}
func (stream *Stream) writeIndention(delta int) {
if stream.indention == 0 {
return
}
stream.writeByte('\n')
toWrite := stream.indention - delta
stream.ensure(toWrite)
for i := 0; i < toWrite && stream.n < len(stream.buf); i++ {
stream.buf[stream.n] = ' '
stream.n++
}
}

96
feature_stream_float.go Normal file
View File

@ -0,0 +1,96 @@
package jsoniter
import (
"math"
"strconv"
)
var pow10 []uint64
func init() {
pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000}
}
// WriteFloat32 write float32 to stream
func (stream *Stream) WriteFloat32(val float32) {
abs := math.Abs(float64(val))
fmt := byte('f')
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
if float32(abs) < 1e-6 || float32(abs) >= 1e21 {
fmt = 'e'
}
}
stream.WriteRaw(strconv.FormatFloat(float64(val), fmt, -1, 32))
}
// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster
func (stream *Stream) WriteFloat32Lossy(val float32) {
if val < 0 {
stream.writeByte('-')
val = -val
}
if val > 0x4ffffff {
stream.WriteFloat32(val)
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('.')
stream.ensure(10)
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--
}
}
// WriteFloat64 write float64 to stream
func (stream *Stream) WriteFloat64(val float64) {
abs := math.Abs(val)
fmt := byte('f')
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
if abs < 1e-6 || abs >= 1e21 {
fmt = 'e'
}
}
stream.WriteRaw(strconv.FormatFloat(float64(val), fmt, -1, 64))
}
// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster
func (stream *Stream) WriteFloat64Lossy(val float64) {
if val < 0 {
stream.writeByte('-')
val = -val
}
if val > 0x4ffffff {
stream.WriteFloat64(val)
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('.')
stream.ensure(10)
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--
}
}

320
feature_stream_int.go Normal file
View File

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

396
feature_stream_string.go Normal file
View File

@ -0,0 +1,396 @@
package jsoniter
import (
"unicode/utf8"
)
// htmlSafeSet holds the value true if the ASCII character with the given
// array position can be safely represented inside a JSON string, embedded
// inside of HTML <script> tags, without any additional escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), the backslash character ("\"), HTML opening and closing
// tags ("<" and ">"), and the ampersand ("&").
var htmlSafeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': false,
'\'': 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,
'<': false,
'=': true,
'>': false,
'?': 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,
}
// 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 hex = "0123456789abcdef"
// WriteStringWithHTMLEscaped write string to stream with html special characters escaped
func (stream *Stream) WriteStringWithHTMLEscaped(s string) {
stream.ensure(32)
valLen := len(s)
toWriteLen := valLen
bufLengthMinusTwo := len(stream.buf) - 2 // make room for the quotes
if stream.n+toWriteLen > bufLengthMinusTwo {
toWriteLen = bufLengthMinusTwo - stream.n
}
n := stream.n
stream.buf[n] = '"'
n++
// write string, the fast path, without utf8 and escape support
i := 0
for ; i < toWriteLen; i++ {
c := s[i]
if c < utf8.RuneSelf && htmlSafeSet[c] {
stream.buf[n] = c
n++
} else {
break
}
}
if i == valLen {
stream.buf[n] = '"'
n++
stream.n = n
return
}
stream.n = n
writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen)
}
func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen int) {
start := i
// for the remaining parts, we process them char by char
for i < valLen {
if b := s[i]; b < utf8.RuneSelf {
if htmlSafeSet[b] {
i++
continue
}
if start < i {
stream.WriteRaw(s[start:i])
}
switch b {
case '\\', '"':
stream.writeTwoBytes('\\', b)
case '\n':
stream.writeTwoBytes('\\', 'n')
case '\r':
stream.writeTwoBytes('\\', 'r')
case '\t':
stream.writeTwoBytes('\\', 't')
default:
// This encodes bytes < 0x20 except for \t, \n and \r.
// If escapeHTML is set, it also escapes <, >, and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
stream.WriteRaw(`\u00`)
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
stream.WriteRaw(s[start:i])
}
stream.WriteRaw(`\ufffd`)
i++
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c == '\u2028' || c == '\u2029' {
if start < i {
stream.WriteRaw(s[start:i])
}
stream.WriteRaw(`\u202`)
stream.writeByte(hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
stream.WriteRaw(s[start:])
}
stream.writeByte('"')
}
// WriteString write string to stream without html escape
func (stream *Stream) WriteString(s string) {
stream.ensure(32)
valLen := len(s)
toWriteLen := valLen
bufLengthMinusTwo := len(stream.buf) - 2 // make room for the quotes
if stream.n+toWriteLen > bufLengthMinusTwo {
toWriteLen = bufLengthMinusTwo - stream.n
}
n := stream.n
stream.buf[n] = '"'
n++
// write string, the fast path, without utf8 and escape support
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
writeStringSlowPath(stream, i, s, valLen)
}
func writeStringSlowPath(stream *Stream, i int, s string, valLen int) {
start := i
// for the remaining parts, we process them char by char
for i < valLen {
if b := s[i]; b < utf8.RuneSelf {
if safeSet[b] {
i++
continue
}
if start < i {
stream.WriteRaw(s[start:i])
}
switch b {
case '\\', '"':
stream.writeTwoBytes('\\', b)
case '\n':
stream.writeTwoBytes('\\', 'n')
case '\r':
stream.writeTwoBytes('\\', 'r')
case '\t':
stream.writeTwoBytes('\\', 't')
default:
// This encodes bytes < 0x20 except for \t, \n and \r.
// If escapeHTML is set, it also escapes <, >, and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
stream.WriteRaw(`\u00`)
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
}
i++
start = i
continue
}
i++
continue
}
if start < len(s) {
stream.WriteRaw(s[start:])
}
stream.writeByte('"')
}

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

View File

@ -1,916 +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
import (
"io"
"fmt"
"unicode/utf16"
"strconv"
"unsafe"
"encoding/base64"
)
type ValueType int
const (
Invalid ValueType = iota
String
Number
Null
Bool
Array
Object
)
var digits []byte
var valueTypes []ValueType
func init() {
digits = make([]byte, 256)
for i := 0; i < len(digits); i++ {
digits[i] = 255
}
for i := '0'; i <= '9'; i++ {
digits[i] = byte(i - '0');
}
for i := 'a'; i <= 'f'; i++ {
digits[i] = byte((i - 'a') + 10);
}
for i := 'A'; i <= 'F'; i++ {
digits[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'] = Null;
valueTypes['['] = Array;
valueTypes['{'] = Object;
}
type Iterator struct {
reader io.Reader
buf []byte
head int
tail int
Error error
}
func Parse(reader io.Reader, bufSize int) *Iterator {
iter := &Iterator{
reader: reader,
buf: make([]byte, bufSize),
head: 0,
tail: 0,
}
iter.skipWhitespaces()
return iter
}
func ParseBytes(input []byte) *Iterator {
iter := &Iterator{
reader: nil,
buf: input,
head: 0,
tail: len(input),
}
iter.skipWhitespaces()
return iter
}
func ParseString(input string) *Iterator {
return ParseBytes([]byte(input))
}
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
iter.reader = reader
iter.head = 0
iter.tail = 0
iter.skipWhitespaces()
return iter
}
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
// only for benchmarking
iter.reader = nil
iter.Error = nil
iter.buf = input
iter.head = 0
iter.tail = len(input)
iter.skipWhitespaces()
return iter
}
func (iter *Iterator) WhatIsNext() ValueType {
valueType := valueTypes[iter.readByte()];
iter.unreadByte();
return valueType;
}
func (iter *Iterator) skipWhitespaces() {
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', '\r':
continue
}
iter.head = i
return
}
if !iter.loadMore() {
return
}
}
}
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 {
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]))
}
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
} else {
return 0
}
}
ret = iter.buf[iter.head]
iter.head++
return ret
}
func (iter *Iterator) loadMore() bool {
if iter.reader == nil {
iter.Error = io.EOF
return false
}
for {
n, err := iter.reader.Read(iter.buf)
if n == 0 {
if err != nil {
iter.Error = err
return false
} else {
// n == 0, err == nil is not EOF
continue
}
} 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 -= 1
return
}
const maxUint64 = (1 << 64 - 1)
const cutoffUint64 = maxUint64 / 10 + 1
const maxUint32 = (1 << 32 - 1)
const cutoffUint32 = maxUint32 / 10 + 1
func (iter *Iterator) ReadUint() (ret uint) {
val := iter.ReadUint64()
converted := uint(val)
if uint64(converted) != val {
iter.ReportError("ReadUint", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint8() (ret uint8) {
val := iter.ReadUint64()
converted := uint8(val)
if uint64(converted) != val {
iter.ReportError("ReadUint8", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint16() (ret uint16) {
val := iter.ReadUint64()
converted := uint16(val)
if uint64(converted) != val {
iter.ReportError("ReadUint16", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint32() (ret uint32) {
val := iter.ReadUint64()
converted := uint32(val)
if uint64(converted) != val {
iter.ReportError("ReadUint32", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint64() (ret uint64) {
c := iter.readByte()
v := digits[c]
if v == 0 {
return 0 // single zero
}
if v == 255 {
iter.ReportError("ReadUint64", "unexpected character")
return
}
for {
if ret >= cutoffUint64 {
iter.ReportError("ReadUint64", "overflow")
return
}
ret = ret * 10 + uint64(v)
c = iter.readByte()
v = digits[c]
if v == 255 {
iter.unreadByte()
break
}
}
return ret
}
func (iter *Iterator) ReadInt() (ret int) {
val := iter.ReadInt64()
converted := int(val)
if int64(converted) != val {
iter.ReportError("ReadInt", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt8() (ret int8) {
val := iter.ReadInt64()
converted := int8(val)
if int64(converted) != val {
iter.ReportError("ReadInt8", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt16() (ret int16) {
val := iter.ReadInt64()
converted := int16(val)
if int64(converted) != val {
iter.ReportError("ReadInt16", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt32() (ret int32) {
val := iter.ReadInt64()
converted := int32(val)
if int64(converted) != val {
iter.ReportError("ReadInt32", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt64() (ret int64) {
c := iter.readByte()
if iter.Error != nil {
return
}
/* optional leading minus */
if c == '-' {
n := iter.ReadUint64()
return -int64(n)
} else {
iter.unreadByte()
n := iter.ReadUint64()
return int64(n)
}
}
func (iter *Iterator) ReadString() (ret string) {
return string(iter.readStringAsBytes())
}
func (iter *Iterator) readStringAsBytes() (ret []byte) {
c := iter.readByte()
if c == 'n' {
iter.skipUntilBreak()
return
}
if c != '"' {
iter.ReportError("ReadString", `expects " or n`)
return
}
end := iter.findStringEndWithoutEscape()
if end != -1 {
// fast path: reuse the underlying buffer
ret = iter.buf[iter.head:end-1]
iter.head = end
return ret
}
str := make([]byte, 0, 8)
for iter.Error == nil {
c = iter.readByte()
if c == '"' {
return str
}
if c == '\\' {
c = iter.readByte()
if iter.Error != nil {
return
}
switch c {
case 'u':
r := iter.readU4()
if iter.Error != nil {
return
}
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' {
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) readU4() (ret rune) {
for i := 0; i < 4; i++ {
c := iter.readByte()
if iter.Error != nil {
return
}
if (c >= '0' && c <= '9') {
if ret >= cutoffUint32 {
iter.ReportError("readU4", "overflow")
return
}
ret = ret * 16 + rune(c - '0')
} else if ((c >= 'a' && c <= 'f') ) {
if ret >= cutoffUint32 {
iter.ReportError("readU4", "overflow")
return
}
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
}
}
func (iter *Iterator) ReadArray() (ret bool) {
c := iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case 'n': {
iter.skipUntilBreak()
return false // null
}
case '[': {
c = iter.nextToken()
if iter.Error != nil {
return
}
if c == ']' {
return false
} else {
iter.unreadByte()
return true
}
}
case ']': return false
case ',':
iter.skipWhitespaces()
return true
default:
iter.ReportError("ReadArray", "expect [ or , or ] or n")
return
}
}
func (iter *Iterator) ReadObject() (ret string) {
c := iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case 'n': {
iter.skipUntilBreak()
if iter.Error != nil {
return
}
return "" // null
}
case '{': {
c = iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case '}':
return "" // end of object
case '"':
iter.unreadByte()
return iter.readObjectField()
default:
iter.ReportError("ReadObject", `expect " after {`)
return
}
}
case ',':
iter.skipWhitespaces()
return iter.readObjectField()
case '}':
return "" // end of object
default:
iter.ReportError("ReadObject", `expect { or , or } or n`)
return
}
}
func (iter *Iterator) readObjectField() (ret string) {
str := iter.readStringAsBytes()
if iter.skipWhitespacesWithoutLoadMore() {
if ret == "" {
ret = string(str);
}
if !iter.loadMore() {
return
}
}
if iter.buf[iter.head] != ':' {
iter.ReportError("ReadObject", "expect : after object field")
return
}
iter.head++
if iter.skipWhitespacesWithoutLoadMore() {
if ret == "" {
ret = string(str);
}
if !iter.loadMore() {
return
}
}
if ret == "" {
return *(*string)(unsafe.Pointer(&str))
}
return ret
}
func (iter *Iterator) ReadFloat32() (ret float32) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
for(hasMore) {
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':
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 32)
if err != nil {
iter.Error = err
return
}
return float32(val)
}
func (iter *Iterator) ReadFloat64() (ret float64) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
for(hasMore) {
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':
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 64)
if err != nil {
iter.Error = err
return
}
return val
}
func (iter *Iterator) ReadBool() (ret bool) {
c := iter.readByte()
if iter.Error != nil {
return
}
switch c {
case 't':
iter.skipUntilBreak()
return true
case 'f':
iter.skipUntilBreak()
return false
default:
iter.ReportError("ReadBool", "expect t or f")
return
}
}
func (iter *Iterator) ReadBase64() (ret []byte) {
src := iter.readStringAsBytes()
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]
}
func (iter *Iterator) ReadNull() (ret bool) {
c := iter.readByte()
if c == 'n' {
iter.skipUntilBreak()
return true
}
iter.unreadByte()
return false
}
func (iter *Iterator) Skip() {
c := iter.readByte()
switch c {
case '"':
iter.skipString()
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 't', 'f', 'n':
iter.skipUntilBreak()
case '[':
iter.skipArray()
case '{':
iter.skipObject()
default:
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
return
}
}
func (iter *Iterator) skipString() {
for {
end, escaped := iter.findStringEnd()
if end == -1 {
if !iter.loadMore() {
return
}
if escaped {
iter.head = 1 // skip the first char as last char read is \
}
} else {
iter.head = end
return
}
}
}
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
// Tries to find the end of string
// Support if string contains escaped quote symbols.
func (iter *Iterator) findStringEnd() (int, bool) {
escaped := false
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
if !escaped {
return i + 1, false
} else {
j := i - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return i + 1, true
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
}
} else if c == '\\' {
escaped = true
}
}
j := iter.tail - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return -1, false // do not end with \
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
return -1, true // end with \
}
func (iter *Iterator) findStringEndWithoutEscape() int {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
return i + 1
} else if c == '\\' {
return -1
}
}
return -1
}
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()) {
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()) {
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
}
}
}

View File

@ -0,0 +1,45 @@
// +build go1.8
package jsoniter
import (
"bytes"
"encoding/json"
"testing"
"unicode/utf8"
"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 := NewEncoder(buf2)
encoder2.SetEscapeHTML(false)
encoder2.Encode([]int{1})
should.Equal("[1]", buf2.String())
}
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
api := 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)
}
}

View File

@ -1,14 +0,0 @@
package jsoniter
import "io"
// adapt to json/encoding api
func Unmarshal(data []byte, v interface{}) error {
iter := ParseBytes(data)
iter.Read(v)
if iter.Error == io.EOF {
return nil
}
return iter.Error
}

84
jsoniter_adapter_test.go Normal file
View File

@ -0,0 +1,84 @@
package jsoniter
import (
"bytes"
"encoding/json"
"github.com/stretchr/testify/require"
"io/ioutil"
"testing"
)
func Test_new_decoder(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
decoder2 := NewDecoder(bytes.NewBufferString(`[1][2]`))
arr1 := []int{}
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{1}, arr1)
arr2 := []int{}
should.True(decoder1.More())
buffered, _ := ioutil.ReadAll(decoder1.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{1}, arr2)
should.True(decoder2.More())
buffered, _ = ioutil.ReadAll(decoder2.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{2}, arr1)
should.False(decoder1.More())
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{2}, arr2)
should.False(decoder2.More())
}
func Test_use_number(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
decoder2 := NewDecoder(bytes.NewBufferString(`123`))
decoder2.UseNumber()
var obj1 interface{}
should.Nil(decoder1.Decode(&obj1))
should.Equal(json.Number("123"), obj1)
var obj2 interface{}
should.Nil(decoder2.Decode(&obj2))
should.Equal(json.Number("123"), obj2)
}
func Test_use_number_for_unmarshal(t *testing.T) {
should := require.New(t)
api := Config{UseNumber: true}.Froze()
var obj interface{}
should.Nil(api.UnmarshalFromString("123", &obj))
should.Equal(json.Number("123"), obj)
}
func Test_marshal_indent(t *testing.T) {
should := require.New(t)
obj := struct {
F1 int
F2 []int
}{1, []int{2, 3, 4}}
output, err := json.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
output, err = MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
}
func Test_marshal_indent_map(t *testing.T) {
should := require.New(t)
obj := map[int]int{1: 2}
output, err := json.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
output, err = MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
output, err = ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
}

62
jsoniter_alias_test.go Normal file
View File

@ -0,0 +1,62 @@
package jsoniter
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test_alias(t *testing.T) {
should := require.New(t)
type myint int
type myint8 int8
type myint16 int16
type myint32 int32
type myint64 int64
type myuint uint
type myuint8 uint8
type myuint16 uint16
type myuint32 uint32
type myuint64 uint64
type myfloat32 float32
type myfloat64 float64
type mystring string
type mybool bool
type myuintptr uintptr
var a struct {
A myint8 `json:"a"`
B myint16 `json:"stream"`
C myint32 `json:"c"`
D myint64 `json:"d"`
E myuint8 `json:"e"`
F myuint16 `json:"f"`
G myuint32 `json:"g"`
H myuint64 `json:"h"`
I myfloat32 `json:"i"`
J myfloat64 `json:"j"`
K mystring `json:"k"`
L myint `json:"l"`
M myuint `json:"m"`
N mybool `json:"n"`
O myuintptr `json:"o"`
}
should.Nil(UnmarshalFromString(`{"a" : 1, "stream" : 1, "c": 1, "d" : 1, "e" : 1, "f" : 1, "g" : 1, "h": 1, "i" : 1, "j" : 1, "k" :"xxxx", "l" : 1, "m":1, "n": true, "o" : 1}`, &a))
should.Equal(myfloat32(1), a.I)
should.Equal(myfloat64(1), a.J)
should.Equal(myint8(1), a.A)
should.Equal(myint16(1), a.B)
should.Equal(myint32(1), a.C)
should.Equal(myint64(1), a.D)
should.Equal(myuint8(1), a.E)
should.Equal(myuint16(1), a.F)
should.Equal(myuint32(1), a.G)
should.Equal(myuint64(1), a.H)
should.Equal(mystring("xxxx"), a.K)
should.Equal(mybool(true), a.N)
should.Equal(myuintptr(1), a.O)
b, err := Marshal(a)
should.Nil(err)
should.Equal(`{"a":1,"stream":1,"c":1,"d":1,"e":1,"f":1,"g":1,"h":1,"i":1,"j":1,"k":"xxxx","l":1,"m":1,"n":true,"o":1}`, string(b))
}

122
jsoniter_any_array_test.go Normal file
View File

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

64
jsoniter_any_bool_test.go Normal file
View File

@ -0,0 +1,64 @@
package jsoniter
import (
"fmt"
"testing"
"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 Any
for k, v := range boolConvertMap {
any = 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 := Get([]byte("true"))
stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("true", string(stream.Buffer()))
should.Equal(any.ValueType(), BoolValue)
any = Get([]byte("false"))
stream = NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("false", string(stream.Buffer()))
should.Equal(any.ValueType(), BoolValue)
}

102
jsoniter_any_float_test.go Normal file
View File

@ -0,0 +1,102 @@
package jsoniter
import (
"testing"
"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 := Get([]byte(k))
should.Equal(float64(v), any.ToFloat64(), "the original val is "+k)
}
for k, v := range floatConvertMap {
any := Get([]byte(k))
should.Equal(float32(v), any.ToFloat32(), "the original val is "+k)
}
}
func Test_read_float_to_any(t *testing.T) {
should := require.New(t)
any := WrapFloat64(12.3)
anyFloat64 := float64(12.3)
//negaAnyFloat64 := float64(-1.1)
any2 := WrapFloat64(-1.1)
should.Equal(float64(12.3), any.ToFloat64())
//should.Equal("12.3", any.ToString())
should.True(any.ToBool())
should.Equal(float32(anyFloat64), any.ToFloat32())
should.Equal(int(anyFloat64), any.ToInt())
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(), NumberValue)
should.Equal("1.23E+01", any.ToString())
}

197
jsoniter_any_int_test.go Normal file
View File

@ -0,0 +1,197 @@
package jsoniter
import (
"fmt"
"testing"
"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 := Get([]byte(k))
should.Equal(v, any.ToInt(), fmt.Sprintf("origin val %v", k))
}
// int32
for k, v := range intConvertMap {
any := Get([]byte(k))
should.Equal(int32(v), any.ToInt32(), fmt.Sprintf("original val is %v", k))
}
// int64
for k, v := range intConvertMap {
any := Get([]byte(k))
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 := Get([]byte(k))
should.Equal(uint64(v), any.ToUint64(), fmt.Sprintf("origin val %v", k))
}
for k, v := range uintConvertMap {
any := Get([]byte(k))
should.Equal(uint32(v), any.ToUint32(), fmt.Sprintf("origin val %v", k))
}
for k, v := range uintConvertMap {
any := 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 := 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(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_int32_to_any(t *testing.T) {
should := require.New(t)
any := WrapInt32(12345)
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(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_uint32_to_any(t *testing.T) {
should := require.New(t)
any := WrapUint32(12345)
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(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_uint64_to_any(t *testing.T) {
should := require.New(t)
any := WrapUint64(12345)
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(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
stream = NewStream(ConfigDefault, nil, 32)
stream.WriteUint(uint(123))
should.Equal("123", string(stream.Buffer()))
}
func Test_int_lazy_any_get(t *testing.T) {
should := require.New(t)
any := Get([]byte("1234"))
// panic!!
//should.Equal(any.LastError(), io.EOF)
should.Equal(InvalidValue, any.Get(1, "2").ValueType())
}

14
jsoniter_any_map_test.go Normal file
View File

@ -0,0 +1,14 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_wrap_map(t *testing.T) {
should := require.New(t)
any := Wrap(map[string]string{"Field1": "hello"})
should.Equal("hello", any.Get("Field1").ToString())
any = Wrap(map[string]string{"Field1": "hello"})
should.Equal(1, any.Size())
}

15
jsoniter_any_null_test.go Normal file
View File

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

107
jsoniter_any_object_test.go Normal file
View File

@ -0,0 +1,107 @@
package jsoniter
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test_read_object_as_any(t *testing.T) {
should := require.New(t)
any := Get([]byte(`{"a":"stream","c":"d"}`))
should.Equal(`{"a":"stream","c":"d"}`, any.ToString())
// partial parse
should.Equal("stream", any.Get("a").ToString())
should.Equal("d", any.Get("c").ToString())
should.Equal(2, len(any.Keys()))
any = Get([]byte(`{"a":"stream","c":"d"}`))
// full parse
should.Equal(2, len(any.Keys()))
should.Equal(2, any.Size())
should.True(any.ToBool())
should.Equal(0, any.ToInt())
should.Equal(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 := Get([]byte(`{"a":{"stream":{"c":"d"}}}`))
should.Equal("d", any.Get("a", "stream", "c").ToString())
}
func Test_object_lazy_any_get_all(t *testing.T) {
should := require.New(t)
any := Get([]byte(`{"a":[0],"stream":[1]}`))
should.Contains(any.Get('*', 0).ToString(), `"a":0`)
}
func Test_object_lazy_any_get_invalid(t *testing.T) {
should := require.New(t)
any := Get([]byte(`{}`))
should.Equal(InvalidValue, any.Get("a", "stream", "c").ValueType())
should.Equal(InvalidValue, any.Get(1).ValueType())
}
func Test_wrap_map_and_convert_to_any(t *testing.T) {
should := require.New(t)
any := Wrap(map[string]interface{}{"a": 1})
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 := Wrap(TestObject{"hello", "world"})
should.Equal("hello", any.Get("Field1").ToString())
any = 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 Any
Field2 Any
}
obj := TestObject{}
err := 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())
}

View File

@ -0,0 +1,56 @@
package jsoniter
import (
"testing"
"github.com/stretchr/testify/require"
)
var stringConvertMap = map[string]string{
"null": "",
"321.1": "321.1",
`"1.1"`: "1.1",
`"-123.1"`: "-123.1",
"0.0": "0.0",
"0": "0",
`"0"`: "0",
`"0.0"`: "0.0",
`"00.0"`: "00.0",
"true": "true",
"false": "false",
`"true"`: "true",
`"false"`: "false",
`"true123"`: "true123",
`"+1"`: "+1",
"[]": "[]",
"[1,2]": "[1,2]",
"{}": "{}",
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
}
func Test_read_any_to_string(t *testing.T) {
should := require.New(t)
for k, v := range stringConvertMap {
any := Get([]byte(k))
should.Equal(v, any.ToString(), "original val "+k)
}
}
func Test_read_string_as_any(t *testing.T) {
should := require.New(t)
any := Get([]byte(`"hello"`))
should.Equal("hello", any.ToString())
should.True(any.ToBool())
any = Get([]byte(`" "`))
should.False(any.ToBool())
any = Get([]byte(`"false"`))
should.True(any.ToBool())
any = Get([]byte(`"123"`))
should.Equal(123, any.ToInt())
}
func Test_wrap_string(t *testing.T) {
should := require.New(t)
any := WrapString("123")
should.Equal(123, any.ToInt())
}

View File

@ -1,39 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
)
func Test_read_string_as_any(t *testing.T) {
iter := ParseString(`[1, {"hello": "world"}, 2]`)
any := iter.ReadAny()
if any.ToString(1, "hello") != "world" {
t.FailNow()
}
}
func Test_read_float64_as_any(t *testing.T) {
iter := ParseString(`1.23`)
any := iter.ReadAny()
if any.ToFloat32() != 1.23 {
t.FailNow()
}
}
func Test_read_int_as_any(t *testing.T) {
iter := ParseString(`123`)
any := iter.ReadAny()
if any.ToFloat32() != 123 {
t.FailNow()
}
}
func Test_read_any_from_nested(t *testing.T) {
iter := ParseString(`{"numbers": ["1", "2", ["3", "4"]]}`)
val := iter.ReadAny()
if val.ToInt("numbers", 2, 0) != 3 {
fmt.Println(val.Error)
t.FailNow()
}
}

View File

@ -1,65 +1,51 @@
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)
func Test_empty_array(t *testing.T) {
iter := ParseString(`[]`)
should := require.New(t)
iter := ParseString(ConfigDefault, `[]`)
cont := iter.ReadArray()
if cont != false {
t.FailNow()
}
should.False(cont)
iter = ParseString(ConfigDefault, `[]`)
iter.ReadArrayCB(func(iter *Iterator) bool {
should.FailNow("should not call")
return true
})
}
func Test_one_element(t *testing.T) {
iter := ParseString(`[1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
should := require.New(t)
iter := ParseString(ConfigDefault, `[1]`)
should.True(iter.ReadArray())
should.Equal(1, iter.ReadInt())
should.False(iter.ReadArray())
iter = ParseString(ConfigDefault, `[1]`)
iter.ReadArrayCB(func(iter *Iterator) bool {
should.Equal(1, iter.ReadInt())
return true
})
}
func Test_two_elements(t *testing.T) {
iter := ParseString(`[1,2]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 2 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_invalid_array(t *testing.T) {
iter := ParseString(`[`)
iter.ReadArray()
if iter.Error == nil {
t.FailNow()
}
should := require.New(t)
iter := ParseString(ConfigDefault, `[1,2]`)
should.True(iter.ReadArray())
should.Equal(int64(1), iter.ReadInt64())
should.True(iter.ReadArray())
should.Equal(int64(2), iter.ReadInt64())
should.False(iter.ReadArray())
iter = ParseString(ConfigDefault, `[1,2]`)
should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
}
func Test_whitespace_in_head(t *testing.T) {
iter := ParseString(` [1]`)
iter := ParseString(ConfigDefault, ` [1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
@ -70,7 +56,7 @@ func Test_whitespace_in_head(t *testing.T) {
}
func Test_whitespace_after_array_start(t *testing.T) {
iter := ParseString(`[ 1]`)
iter := ParseString(ConfigDefault, `[ 1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
@ -81,7 +67,7 @@ func Test_whitespace_after_array_start(t *testing.T) {
}
func Test_whitespace_before_array_end(t *testing.T) {
iter := ParseString(`[1 ]`)
iter := ParseString(ConfigDefault, `[1 ]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
@ -96,7 +82,7 @@ func Test_whitespace_before_array_end(t *testing.T) {
}
func Test_whitespace_before_comma(t *testing.T) {
iter := ParseString(`[1 ,2]`)
iter := ParseString(ConfigDefault, `[1 ,2]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
@ -117,10 +103,99 @@ func Test_whitespace_before_comma(t *testing.T) {
}
}
func Test_write_array(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096)
stream.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_encode_byte_array(t *testing.T) {
should := require.New(t)
bytes, err := json.Marshal([]byte{1, 2, 3})
should.Nil(err)
should.Equal(`"AQID"`, string(bytes))
bytes, err = Marshal([]byte{1, 2, 3})
should.Nil(err)
should.Equal(`"AQID"`, string(bytes))
}
func Test_decode_byte_array_from_base64(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`"AQID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = Unmarshal([]byte(`"AQID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_byte_array_from_array(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_slice(t *testing.T) {
should := require.New(t)
slice := make([]string, 0, 5)
UnmarshalFromString(`["hello", "world"]`, &slice)
should.Equal([]string{"hello", "world"}, slice)
}
func Test_decode_large_slice(t *testing.T) {
should := require.New(t)
slice := make([]int, 0, 1)
UnmarshalFromString(`[1,2,3,4,5,6,7,8,9]`, &slice)
should.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, slice)
}
func Benchmark_jsoniter_array(b *testing.B) {
b.ReportAllocs()
input := []byte(`[1,2,3,4,5,6,7,8,9]`)
iter := ParseBytes(input)
iter := ParseBytes(ConfigDefault, input)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(input)
@ -135,4 +210,4 @@ func Benchmark_json_array(b *testing.B) {
result := []interface{}{}
json.Unmarshal([]byte(`[1,2,3]`), &result)
}
}
}

View File

@ -1,17 +1,84 @@
package jsoniter
import "testing"
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func Test_true(t *testing.T) {
iter := ParseString(`true`)
if iter.ReadBool() != true {
t.FailNow()
}
should := require.New(t)
iter := ParseString(ConfigDefault, `true`)
should.True(iter.ReadBool())
iter = ParseString(ConfigDefault, `true`)
should.Equal(true, iter.Read())
}
func Test_false(t *testing.T) {
iter := ParseString(`false`)
if iter.ReadBool() != false {
t.FailNow()
}
should := require.New(t)
iter := ParseString(ConfigDefault, `false`)
should.False(iter.ReadBool())
}
func Test_write_true_false(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteTrue()
stream.WriteFalse()
stream.WriteBool(false)
stream.Flush()
should.Nil(stream.Error)
should.Equal("truefalsefalse", buf.String())
}
func Test_write_val_bool(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(true)
should.Equal(stream.Buffered(), 4)
stream.Flush()
should.Equal(stream.Buffered(), 0)
should.Nil(stream.Error)
should.Equal("true", buf.String())
}
func Test_encode_string_bool(t *testing.T) {
type TestObject struct {
Field bool `json:",omitempty,string"`
}
should := require.New(t)
output, err := json.Marshal(TestObject{true})
should.Nil(err)
should.Equal(`{"Field":"true"}`, string(output))
output, err = Marshal(TestObject{true})
should.Nil(err)
should.Equal(`{"Field":"true"}`, string(output))
}
func Test_decode_string_bool(t *testing.T) {
type TestObject struct {
Field bool `json:",omitempty,string"`
}
should := require.New(t)
obj := TestObject{}
err := json.Unmarshal([]byte(`{"Field":"true"}`), &obj)
should.Nil(err)
should.True(obj.Field)
obj = TestObject{}
err = json.Unmarshal([]byte(`{"Field":true}`), &obj)
should.NotNil(err)
obj = TestObject{}
err = Unmarshal([]byte(`{"Field":"true"}`), &obj)
should.Nil(err)
should.True(obj.Field)
obj = TestObject{}
err = Unmarshal([]byte(`{"Field":true}`), &obj)
should.NotNil(err)
}

View File

@ -1,15 +1,16 @@
package jsoniter
import (
"encoding/json"
"github.com/stretchr/testify/require"
"strconv"
"testing"
"time"
"unsafe"
"strconv"
"reflect"
)
func Test_customize_type_decoder(t *testing.T) {
RegisterTypeDecoder("time.Time", func(ptr unsafe.Pointer, iter *Iterator) {
RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *Iterator) {
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
if err != nil {
iter.Error = err
@ -17,7 +18,7 @@ func Test_customize_type_decoder(t *testing.T) {
}
*((*time.Time)(ptr)) = t
})
defer ClearDecoders()
defer ConfigDefault.(*frozenConfig).cleanDecoders()
val := time.Time{}
err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
if err != nil {
@ -29,15 +30,50 @@ func Test_customize_type_decoder(t *testing.T) {
}
}
func Test_customize_type_encoder(t *testing.T) {
should := require.New(t)
RegisterTypeEncoderFunc("time.Time", func(ptr unsafe.Pointer, stream *Stream) {
t := *((*time.Time)(ptr))
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
}, nil)
defer ConfigDefault.(*frozenConfig).cleanEncoders()
val := time.Unix(0, 0)
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`"1970-01-01 00:00:00"`, str)
}
func Test_customize_byte_array_encoder(t *testing.T) {
ConfigDefault.(*frozenConfig).cleanEncoders()
should := require.New(t)
RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
t := *((*[]byte)(ptr))
stream.WriteString(string(t))
}, nil)
defer ConfigDefault.(*frozenConfig).cleanEncoders()
val := []byte("abc")
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`"abc"`, str)
}
func Test_customize_float_marshal(t *testing.T) {
should := require.New(t)
json := Config{MarshalFloatWith6Digits: true}.Froze()
str, err := json.MarshalToString(float32(1.23456789))
should.Nil(err)
should.Equal("1.234568", str)
}
type Tom struct {
field1 string
}
func Test_customize_field_decoder(t *testing.T) {
RegisterFieldDecoder("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
})
defer ClearDecoders()
defer ConfigDefault.(*frozenConfig).cleanDecoders()
tom := Tom{}
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
if err != nil {
@ -45,21 +81,236 @@ func Test_customize_field_decoder(t *testing.T) {
}
}
func Test_customize_field_decoder_factory(t *testing.T) {
RegisterFieldCustomizer(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
if (type_.String() == "jsoniter.Tom" && field.Name == "field1") {
return []string{"field-1"}, func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}
}
return nil, nil
type TestObject1 struct {
field1 string
}
type testExtension struct {
DummyExtension
}
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
if structDescriptor.Type.String() != "jsoniter.TestObject1" {
return
}
binding := structDescriptor.GetField("field1")
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
val, _ := strconv.Atoi(str)
stream.WriteInt(val)
}}
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}}
binding.ToNames = []string{"field-1"}
binding.FromNames = []string{"field-1"}
}
func Test_customize_field_by_extension(t *testing.T) {
should := require.New(t)
RegisterExtension(&testExtension{})
obj := TestObject1{}
err := UnmarshalFromString(`{"field-1": 100}`, &obj)
should.Nil(err)
should.Equal("100", obj.field1)
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field-1":100}`, str)
}
type timeImplementedMarshaler time.Time
func (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {
seconds := time.Time(obj).Unix()
return []byte(strconv.FormatInt(seconds, 10)), nil
}
func Test_marshaler(t *testing.T) {
type TestObject struct {
Field timeImplementedMarshaler
}
should := require.New(t)
val := timeImplementedMarshaler(time.Unix(123, 0))
obj := TestObject{val}
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal(`{"Field":123}`, string(bytes))
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":123}`, str)
}
func Test_marshaler_and_encoder(t *testing.T) {
type TestObject struct {
Field *timeImplementedMarshaler
}
ConfigDefault.(*frozenConfig).cleanEncoders()
should := require.New(t)
RegisterTypeEncoderFunc("jsoniter.timeImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
stream.WriteString("hello from encoder")
}, nil)
val := timeImplementedMarshaler(time.Unix(123, 0))
obj := TestObject{&val}
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal(`{"Field":123}`, string(bytes))
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":"hello from encoder"}`, str)
}
type ObjectImplementedUnmarshaler int
func (obj *ObjectImplementedUnmarshaler) UnmarshalJSON(s []byte) error {
val, _ := strconv.ParseInt(string(s[1:len(s)-1]), 10, 64)
*obj = ObjectImplementedUnmarshaler(val)
return nil
}
func Test_unmarshaler(t *testing.T) {
should := require.New(t)
var obj ObjectImplementedUnmarshaler
err := json.Unmarshal([]byte(` "100" `), &obj)
should.Nil(err)
should.Equal(100, int(obj))
iter := ParseString(ConfigDefault, ` "100" `)
iter.ReadVal(&obj)
should.Nil(err)
should.Equal(100, int(obj))
}
func Test_unmarshaler_and_decoder(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedUnmarshaler
Field2 string
}
ConfigDefault.(*frozenConfig).cleanDecoders()
should := require.New(t)
RegisterTypeDecoderFunc("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
*(*ObjectImplementedUnmarshaler)(ptr) = 10
iter.Skip()
})
tom := Tom{}
err := Unmarshal([]byte(`{"field-1": 100}`), &tom)
if err != nil {
t.Fatal(err)
obj := TestObject{}
val := ObjectImplementedUnmarshaler(0)
obj.Field = &val
err := json.Unmarshal([]byte(`{"Field":"100"}`), &obj)
should.Nil(err)
should.Equal(100, int(*obj.Field))
err = Unmarshal([]byte(`{"Field":"100"}`), &obj)
should.Nil(err)
should.Equal(10, int(*obj.Field))
}
type tmString string
type tmStruct struct {
String tmString
}
func (s tmStruct) MarshalJSON() ([]byte, error) {
var b []byte
b = append(b, '"')
b = append(b, s.String...)
b = append(b, '"')
return b, nil
}
func Test_marshaler_on_struct(t *testing.T) {
fixed := tmStruct{"hello"}
//json.Marshal(fixed)
Marshal(fixed)
}
type withChan struct {
F2 chan []byte
}
func (q withChan) MarshalJSON() ([]byte, error) {
return []byte(`""`), nil
}
func (q *withChan) UnmarshalJSON(value []byte) error {
return nil
}
func Test_marshal_json_with_chan(t *testing.T) {
type TestObject struct {
F1 withChan
}
if tom.field1 != "100" {
t.Fatal(tom.field1)
should := require.New(t)
output, err := MarshalToString(TestObject{})
should.Nil(err)
should.Equal(`{"F1":""}`, output)
}
type withTime struct {
time.Time
}
func (t *withTime) UnmarshalJSON(b []byte) error {
return nil
}
func (t withTime) MarshalJSON() ([]byte, error) {
return []byte(`"fake"`), nil
}
func Test_marshal_json_with_time(t *testing.T) {
type S1 struct {
F1 withTime
F2 *withTime
}
}
type TestObject struct {
TF1 S1
}
should := require.New(t)
obj := TestObject{
S1{
F1: withTime{
time.Unix(0, 0),
},
F2: &withTime{
time.Unix(0, 0),
},
},
}
output, err := json.Marshal(obj)
should.Nil(err)
should.Equal(`{"TF1":{"F1":"fake","F2":"fake"}}`, string(output))
output, err = Marshal(obj)
should.Nil(err)
should.Equal(`{"TF1":{"F1":"fake","F2":"fake"}}`, string(output))
obj = TestObject{}
should.Nil(json.Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
should.NotNil(obj.TF1.F2)
obj = TestObject{}
should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
should.NotNil(obj.TF1.F2)
}
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {
Field string `orm:"field"`
}
should := require.New(t)
json := Config{TagKey: "orm"}.Froze()
str, err := json.MarshalToString(TestObject{"hello"})
should.Nil(err)
should.Equal(`{"field":"hello"}`, str)
}
func Test_recursive_empty_interface_customization(t *testing.T) {
t.Skip()
var obj interface{}
RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case NumberValue:
*(*interface{})(ptr) = iter.ReadInt64()
default:
*(*interface{})(ptr) = iter.Read()
}
})
should := require.New(t)
Unmarshal([]byte("[100]"), &obj)
should.Equal([]interface{}{int64(100)}, obj)
}

View File

@ -1,56 +1,87 @@
package jsoniter
import (
"encoding/json"
"testing"
"fmt"
"github.com/stretchr/testify/require"
)
func Test_bind_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
should := require.New(t)
val := []int{}
iter.Read(&val)
fmt.Println(val[3])
}
func Test_any_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
val := iter.ReadAny()
fmt.Println(val.Get(3))
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]`)
should := require.New(t)
iter := ParseString(ConfigDefault, `[0,1,2,3]`)
total := 0
for iter.ReadArray() {
total += iter.ReadInt()
}
fmt.Println(total)
should.Equal(6, total)
}
type ABC struct {
a Any
type People struct {
Name string
Gender string
Age int
Address string
Mobile string
Country string
Height int
}
func Test_deep_nested_any_api(t *testing.T) {
iter := ParseString(`{"a": {"b": {"c": "d"}}}`)
abc := &ABC{}
iter.Read(&abc)
fmt.Println(abc.a.Get("b", "c"))
func jsoniterMarshal(p *People) error {
_, err := Marshal(p)
if nil != err {
return err
}
return nil
}
func stdMarshal(p *People) error {
_, err := json.Marshal(p)
if nil != err {
return err
}
return nil
}
type User struct {
userId int
name string
tags []string
func BenchmarkJosniterMarshal(b *testing.B) {
var p People
p.Address = "上海市徐汇区漕宝路"
p.Age = 30
p.Country = "中国"
p.Gender = "male"
p.Height = 170
p.Mobile = "18502120533"
p.Name = "Elvin"
b.ReportAllocs()
for i := 0; i < b.N; i++ {
err := jsoniterMarshal(&p)
if nil != err {
b.Error(err)
}
}
}
func Test_iterator_and_bind_api(t *testing.T) {
iter := ParseString(`[123, {"name": "taowen", "tags": ["crazy", "hacker"]}]`)
user := User{}
iter.ReadArray()
user.userId = iter.ReadInt()
iter.ReadArray()
iter.Read(&user)
iter.ReadArray() // array end
fmt.Println(user)
func BenchmarkStdMarshal(b *testing.B) {
var p People
p.Address = "上海市徐汇区漕宝路"
p.Age = 30
p.Country = "中国"
p.Gender = "male"
p.Height = 170
p.Mobile = "18502120533"
p.Name = "Elvin"
b.ReportAllocs()
for i := 0; i < b.N; i++ {
err := stdMarshal(&p)
if nil != err {
b.Error(err)
}
}
}

View File

@ -0,0 +1,42 @@
package jsoniter
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func Test_encode_interface(t *testing.T) {
should := require.New(t)
var a interface{}
a = int8(10)
str, err := MarshalToString(a)
should.Nil(err)
should.Equal(str, "10")
a = float32(3)
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, "3")
a = map[string]interface{}{"abc": 1}
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, `{"abc":1}`)
a = uintptr(1)
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, "1")
a = uint(1)
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, "1")
a = uint8(1)
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, "1")
a = json.RawMessage("abc")
MarshalToString(a)
str, err = MarshalToString(a)
should.Nil(err)
should.Equal(str, "abc")
}

View File

@ -0,0 +1,37 @@
package jsoniter
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)
func Test_encode_fixed_array(t *testing.T) {
should := require.New(t)
type FixedArray [2]float64
fixed := FixedArray{0.1, 1.0}
output, err := MarshalToString(fixed)
should.Nil(err)
should.Equal("[0.1,1]", output)
}
func Test_encode_fixed_array_of_map(t *testing.T) {
should := require.New(t)
type FixedArray [2]map[string]string
fixed := FixedArray{map[string]string{"1": "2"}, map[string]string{"3": "4"}}
output, err := MarshalToString(fixed)
should.Nil(err)
should.Equal(`[{"1":"2"},{"3":"4"}]`, output)
}
func Test_decode_fixed_array(t *testing.T) {
should := require.New(t)
type FixedArray [2]float64
var fixed FixedArray
should.Nil(json.Unmarshal([]byte("[1,2,3]"), &fixed))
should.Equal(float64(1), fixed[0])
should.Equal(float64(2), fixed[1])
should.Nil(Unmarshal([]byte("[1,2,3]"), &fixed))
should.Equal(float64(1), fixed[0])
should.Equal(float64(2), fixed[1])
}

View File

@ -1,40 +1,203 @@
// +build go1.8
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
func Test_float64_0(t *testing.T) {
iter := ParseString(`0`)
val := iter.ReadFloat64()
if val != 0 {
t.Fatal(val)
func Test_read_big_float(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `12.3`)
val := iter.ReadBigFloat()
val64, _ := val.Float64()
should.Equal(12.3, val64)
}
func Test_read_big_int(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `92233720368547758079223372036854775807`)
val := iter.ReadBigInt()
should.NotNil(val)
should.Equal(`92233720368547758079223372036854775807`, val.String())
}
func Test_read_float(t *testing.T) {
inputs := []string{
`1.1`, `1000`, `9223372036854775807`, `12.3`, `-12.3`, `720368.54775807`, `720368.547758075`,
`1e1`, `1e+1`, `1e-1`, `1E1`, `1E+1`, `1E-1`, `-1e1`, `-1e+1`, `-1e-1`,
}
for _, input := range inputs {
// non-streaming
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input+",")
expected, err := strconv.ParseFloat(input, 32)
should.Nil(err)
should.Equal(float32(expected), iter.ReadFloat32())
})
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input+",")
expected, err := strconv.ParseFloat(input, 64)
should.Nil(err)
should.Equal(expected, iter.ReadFloat64())
})
// streaming
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input+","), 2)
expected, err := strconv.ParseFloat(input, 32)
should.Nil(err)
should.Equal(float32(expected), iter.ReadFloat32())
})
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input+","), 2)
val := float64(0)
err := json.Unmarshal([]byte(input), &val)
should.Nil(err)
should.Equal(val, iter.ReadFloat64())
})
}
}
func Test_float64_1_dot_1(t *testing.T) {
iter := ParseString(`1.1`)
val := iter.ReadFloat64()
if val != 1.1 {
t.Fatal(val)
}
func Test_read_float_as_interface(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `12.3`)
should.Equal(float64(12.3), iter.Read())
}
func Test_float32_1_dot_1_comma(t *testing.T) {
iter := ParseString(`1.1,`)
val := iter.ReadFloat32()
if val != 1.1 {
fmt.Println(iter.Error)
t.Fatal(val)
func Test_wrap_float(t *testing.T) {
should := require.New(t)
str, err := MarshalToString(WrapFloat64(12.3))
should.Nil(err)
should.Equal("12.3", str)
}
func Test_write_float32(t *testing.T) {
vals := []float32{0, 1, -1, 99, 0xff, 0xfff, 0xffff, 0xfffff, 0xffffff, 0x4ffffff, 0xfffffff,
-0x4ffffff, -0xfffffff, 1.2345, 1.23456, 1.234567, 1.001}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteFloat32Lossy(val)
stream.Flush()
should.Nil(stream.Error)
output, err := json.Marshal(val)
should.Nil(err)
should.Equal(string(output), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
output, err := json.Marshal(val)
should.Nil(err)
should.Equal(string(output), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 10)
stream.WriteRaw("abcdefg")
stream.WriteFloat32Lossy(1.123456)
stream.Flush()
should.Nil(stream.Error)
should.Equal("abcdefg1.123456", buf.String())
stream = NewStream(ConfigDefault, nil, 0)
stream.WriteFloat32(float32(0.0000001))
should.Equal("1e-07", string(stream.buf))
}
func Test_write_float64(t *testing.T) {
vals := []float64{0, 1, -1, 99, 0xff, 0xfff, 0xffff, 0xfffff, 0xffffff, 0x4ffffff, 0xfffffff,
-0x4ffffff, -0xfffffff, 1.2345, 1.23456, 1.234567, 1.001}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteFloat64Lossy(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatFloat(val, 'f', -1, 64), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatFloat(val, 'f', -1, 64), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 10)
stream.WriteRaw("abcdefg")
stream.WriteFloat64Lossy(1.123456)
stream.Flush()
should.Nil(stream.Error)
should.Equal("abcdefg1.123456", buf.String())
stream = NewStream(ConfigDefault, nil, 0)
stream.WriteFloat64(float64(0.0000001))
should.Equal("1e-07", string(stream.buf))
}
func Test_read_float64_cursor(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, "[1.23456789\n,2,3]")
should.True(iter.ReadArray())
should.Equal(1.23456789, iter.Read())
should.True(iter.ReadArray())
should.Equal(float64(2), iter.Read())
}
func Test_read_float_scientific(t *testing.T) {
should := require.New(t)
var obj interface{}
should.Nil(UnmarshalFromString(`1e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(json.Unmarshal([]byte(`1e1`), &obj))
should.Equal(float64(10), obj)
should.Nil(UnmarshalFromString(`1.0e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(json.Unmarshal([]byte(`1.0e1`), &obj))
should.Equal(float64(10), obj)
}
func Test_lossy_float_marshal(t *testing.T) {
should := require.New(t)
api := Config{MarshalFloatWith6Digits: true}.Froze()
output, err := api.MarshalToString(float64(0.1234567))
should.Nil(err)
should.Equal("0.123457", output)
output, err = api.MarshalToString(float32(0.1234567))
should.Nil(err)
should.Equal("0.123457", output)
}
func Benchmark_jsoniter_float(b *testing.B) {
b.ReportAllocs()
input := []byte(`1.1123,`)
iter := NewIterator(ConfigDefault)
for n := 0; n < b.N; n++ {
iter := ParseString(`1.1111111111`)
iter.ResetBytes(input)
iter.ReadFloat64()
}
}
@ -44,4 +207,4 @@ func Benchmark_json_float(b *testing.B) {
result := float64(0)
json.Unmarshal([]byte(`1.1`), &result)
}
}
}

View File

@ -1,76 +1,492 @@
// +build go1.8
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
func Test_uint64_0(t *testing.T) {
iter := Parse(bytes.NewBufferString("0"), 4096)
val := iter.ReadUint64()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != 0 {
t.Fatal(val)
}
}
func Test_uint64_1(t *testing.T) {
iter := Parse(bytes.NewBufferString("1"), 4096)
val := iter.ReadUint64()
if val != 1 {
t.Fatal(val)
}
}
func Test_uint64_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("100"), 4096)
val := iter.ReadUint64()
if val != 100 {
t.Fatal(val)
}
}
func Test_uint64_100_comma(t *testing.T) {
iter := Parse(bytes.NewBufferString("100,"), 4096)
val := iter.ReadUint64()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != 100 {
t.Fatal(val)
}
}
func Test_uint64_invalid(t *testing.T) {
iter := Parse(bytes.NewBufferString(","), 4096)
func Test_read_uint64_invalid(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, ",")
iter.ReadUint64()
if iter.Error == nil {
t.FailNow()
should.NotNil(iter.Error)
}
func Test_read_int8(t *testing.T) {
inputs := []string{`127`, `-128`}
for _, input := range inputs {
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
expected, err := strconv.ParseInt(input, 10, 8)
should.Nil(err)
should.Equal(int8(expected), iter.ReadInt8())
})
}
}
func Test_int64_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("100"), 4096)
val := iter.ReadInt64()
if val != 100 {
t.Fatal(val)
func Test_read_int16(t *testing.T) {
inputs := []string{`32767`, `-32768`}
for _, input := range inputs {
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
expected, err := strconv.ParseInt(input, 10, 16)
should.Nil(err)
should.Equal(int16(expected), iter.ReadInt16())
})
}
}
func Test_int64_minus_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("-100"), 4096)
val := iter.ReadInt64()
if val != -100 {
t.Fatal(val)
func Test_read_int32(t *testing.T) {
inputs := []string{`1`, `12`, `123`, `1234`, `12345`, `123456`, `2147483647`, `-2147483648`}
for _, input := range inputs {
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
expected, err := strconv.ParseInt(input, 10, 32)
should.Nil(err)
should.Equal(int32(expected), iter.ReadInt32())
})
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
expected, err := strconv.ParseInt(input, 10, 32)
should.Nil(err)
should.Equal(int32(expected), iter.ReadInt32())
})
}
}
func Test_read_int32_array(t *testing.T) {
should := require.New(t)
input := `[123,456,789]`
val := make([]int32, 0)
UnmarshalFromString(input, &val)
should.Equal(3, len(val))
}
func Test_read_int64_array(t *testing.T) {
should := require.New(t)
input := `[123,456,789]`
val := make([]int64, 0)
UnmarshalFromString(input, &val)
should.Equal(3, len(val))
}
func Test_read_int_overflow(t *testing.T) {
should := require.New(t)
inputArr := []string{"123451", "-123451"}
for _, s := range inputArr {
iter := ParseString(ConfigDefault, s)
iter.ReadInt8()
should.NotNil(iter.Error)
iterU := ParseString(ConfigDefault, s)
iterU.ReadUint8()
should.NotNil(iterU.Error)
}
inputArr = []string{"12345678912", "-12345678912"}
for _, s := range inputArr {
iter := ParseString(ConfigDefault, s)
iter.ReadInt16()
should.NotNil(iter.Error)
iterUint := ParseString(ConfigDefault, s)
iterUint.ReadUint16()
should.NotNil(iterUint.Error)
}
inputArr = []string{"3111111111", "-3111111111", "1234232323232323235678912", "-1234567892323232323212"}
for _, s := range inputArr {
iter := ParseString(ConfigDefault, s)
iter.ReadInt32()
should.NotNil(iter.Error)
iterUint := ParseString(ConfigDefault, s)
iterUint.ReadUint32()
should.NotNil(iterUint.Error)
}
inputArr = []string{"9223372036854775811", "-9523372036854775807", "1234232323232323235678912", "-1234567892323232323212"}
for _, s := range inputArr {
iter := ParseString(ConfigDefault, s)
iter.ReadInt64()
should.NotNil(iter.Error)
iterUint := ParseString(ConfigDefault, s)
iterUint.ReadUint64()
should.NotNil(iterUint.Error)
}
}
func Test_read_int64(t *testing.T) {
inputs := []string{`1`, `12`, `123`, `1234`, `12345`, `123456`, `9223372036854775807`, `-9223372036854775808`}
for _, input := range inputs {
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
expected, err := strconv.ParseInt(input, 10, 64)
should.Nil(err)
should.Equal(expected, iter.ReadInt64())
})
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
expected, err := strconv.ParseInt(input, 10, 64)
should.Nil(err)
should.Equal(expected, iter.ReadInt64())
})
}
}
func Test_read_int64_overflow(t *testing.T) {
should := require.New(t)
input := "123456789123456789123456789123456789,"
iter := ParseString(ConfigDefault, input)
iter.ReadInt64()
should.NotNil(iter.Error)
}
func Test_wrap_int(t *testing.T) {
should := require.New(t)
str, err := MarshalToString(WrapInt64(100))
should.Nil(err)
should.Equal("100", str)
}
func Test_write_uint8(t *testing.T) {
vals := []uint8{0, 1, 11, 111, 255}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteUint8(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 3)
stream.WriteRaw("a")
stream.WriteUint8(100) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a100", buf.String())
}
func Test_write_int8(t *testing.T) {
vals := []int8{0, 1, -1, 99, 0x7f, -0x80}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteInt8(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4)
stream.WriteRaw("a")
stream.WriteInt8(-100) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a-100", buf.String())
}
func Test_write_uint16(t *testing.T) {
vals := []uint16{0, 1, 11, 111, 255, 0xfff, 0xffff}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteUint16(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 5)
stream.WriteRaw("a")
stream.WriteUint16(10000) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a10000", buf.String())
}
func Test_write_int16(t *testing.T) {
vals := []int16{0, 1, 11, 111, 255, 0xfff, 0x7fff, -0x8000}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteInt16(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 6)
stream.WriteRaw("a")
stream.WriteInt16(-10000) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a-10000", buf.String())
}
func Test_write_uint32(t *testing.T) {
vals := []uint32{0, 1, 11, 111, 255, 999999, 0xfff, 0xffff, 0xfffff, 0xffffff, 0xfffffff, 0xffffffff}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteUint32(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 10)
stream.WriteRaw("a")
stream.WriteUint32(0xffffffff) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a4294967295", buf.String())
}
func Test_write_int32(t *testing.T) {
vals := []int32{0, 1, 11, 111, 255, 999999, 0xfff, 0xffff, 0xfffff, 0xffffff, 0xfffffff, 0x7fffffff, -0x80000000}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteInt32(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(int64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 11)
stream.WriteRaw("a")
stream.WriteInt32(-0x7fffffff) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a-2147483647", buf.String())
}
func Test_write_uint64(t *testing.T) {
vals := []uint64{0, 1, 11, 111, 255, 999999, 0xfff, 0xffff, 0xfffff, 0xffffff, 0xfffffff, 0xffffffff,
0xfffffffff, 0xffffffffff, 0xfffffffffff, 0xffffffffffff, 0xfffffffffffff, 0xffffffffffffff,
0xfffffffffffffff, 0xffffffffffffffff}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteUint64(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatUint(uint64(val), 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 10)
stream.WriteRaw("a")
stream.WriteUint64(0xffffffff) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a4294967295", buf.String())
}
func Test_write_int64(t *testing.T) {
vals := []int64{0, 1, 11, 111, 255, 999999, 0xfff, 0xffff, 0xfffff, 0xffffff, 0xfffffff, 0xffffffff,
0xfffffffff, 0xffffffffff, 0xfffffffffff, 0xffffffffffff, 0xfffffffffffff, 0xffffffffffffff,
0xfffffffffffffff, 0x7fffffffffffffff, -0x8000000000000000}
for _, val := range vals {
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteInt64(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(val, 10), buf.String())
})
t.Run(fmt.Sprintf("%v", val), func(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatInt(val, 10), buf.String())
})
}
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 10)
stream.WriteRaw("a")
stream.WriteInt64(0xffffffff) // should clear buffer
stream.Flush()
should.Nil(stream.Error)
should.Equal("a4294967295", buf.String())
}
func Test_write_val_int(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal(1001)
stream.Flush()
should.Nil(stream.Error)
should.Equal("1001", buf.String())
}
func Test_write_val_int_ptr(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
val := 1001
stream.WriteVal(&val)
stream.Flush()
should.Nil(stream.Error)
should.Equal("1001", buf.String())
}
func Test_json_number(t *testing.T) {
should := require.New(t)
var arr []json.Number
err := Unmarshal([]byte(`[1]`), &arr)
should.Nil(err)
should.Equal(json.Number("1"), arr[0])
str, err := MarshalToString(arr)
should.Nil(err)
should.Equal(`[1]`, str)
}
func Test_jsoniter_number(t *testing.T) {
should := require.New(t)
var arr []Number
err := Unmarshal([]byte(`[1]`), &arr)
should.Nil(err)
should.Equal(Number("1"), arr[0])
str, isNumber := CastJsonNumber(arr[0])
should.True(isNumber)
should.Equal("1", str)
}
func Benchmark_jsoniter_encode_int(b *testing.B) {
stream := NewStream(ConfigDefault, ioutil.Discard, 64)
for n := 0; n < b.N; n++ {
stream.n = 0
stream.WriteUint64(0xffffffff)
}
}
func Benchmark_itoa(b *testing.B) {
for n := 0; n < b.N; n++ {
strconv.FormatInt(0xffffffff, 10)
}
}
func Benchmark_jsoniter_int(b *testing.B) {
iter := NewIterator(ConfigDefault)
input := []byte(`100`)
for n := 0; n < b.N; n++ {
iter := ParseString(`-100`)
iter.ResetBytes(input)
iter.ReadInt64()
}
}
@ -80,4 +496,4 @@ func Benchmark_json_int(b *testing.B) {
result := int64(0)
json.Unmarshal([]byte(`-100`), &result)
}
}
}

299
jsoniter_interface_test.go Normal file
View File

@ -0,0 +1,299 @@
package jsoniter
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
"unsafe"
)
func Test_write_array_of_interface(t *testing.T) {
should := require.New(t)
array := []interface{}{"hello"}
str, err := MarshalToString(array)
should.Nil(err)
should.Equal(`["hello"]`, str)
}
func Test_write_map_of_interface(t *testing.T) {
should := require.New(t)
val := map[string]interface{}{"hello": "world"}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"hello":"world"}`, str)
}
func Test_write_map_of_interface_in_struct(t *testing.T) {
type TestObject struct {
Field map[string]interface{}
}
should := require.New(t)
val := TestObject{map[string]interface{}{"hello": "world"}}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"Field":{"hello":"world"}}`, str)
}
func Test_write_map_of_interface_in_struct_with_two_fields(t *testing.T) {
type TestObject struct {
Field map[string]interface{}
Field2 string
}
should := require.New(t)
val := TestObject{map[string]interface{}{"hello": "world"}, ""}
str, err := MarshalToString(val)
should.Nil(err)
should.Contains(str, `"Field":{"hello":"world"}`)
}
type MyInterface interface {
Hello() string
}
type MyString string
func (ms MyString) Hello() string {
return string(ms)
}
func Test_write_map_of_custom_interface(t *testing.T) {
should := require.New(t)
myStr := MyString("world")
should.Equal("world", myStr.Hello())
val := map[string]MyInterface{"hello": myStr}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"hello":"world"}`, str)
}
func Test_write_interface(t *testing.T) {
should := require.New(t)
var val interface{}
val = "hello"
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`"hello"`, str)
}
func Test_read_interface(t *testing.T) {
should := require.New(t)
var val interface{}
err := UnmarshalFromString(`"hello"`, &val)
should.Nil(err)
should.Equal("hello", val)
err = UnmarshalFromString(`1e1`, &val)
should.Nil(err)
should.Equal(float64(10), val)
err = UnmarshalFromString(`1.0e1`, &val)
should.Nil(err)
should.Equal(float64(10), val)
err = json.Unmarshal([]byte(`1.0e1`), &val)
should.Nil(err)
should.Equal(float64(10), val)
}
func Test_read_custom_interface(t *testing.T) {
should := require.New(t)
var val MyInterface
RegisterTypeDecoderFunc("jsoniter.MyInterface", func(ptr unsafe.Pointer, iter *Iterator) {
*((*MyInterface)(ptr)) = MyString(iter.ReadString())
})
err := UnmarshalFromString(`"hello"`, &val)
should.Nil(err)
should.Equal("hello", val.Hello())
}
func Test_decode_object_contain_empty_interface(t *testing.T) {
type TestObject struct {
Field interface{}
}
should := require.New(t)
obj := TestObject{}
obj.Field = 1024
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal("hello", obj.Field)
}
func Test_decode_object_contain_non_empty_interface(t *testing.T) {
type TestObject struct {
Field MyInterface
}
should := require.New(t)
obj := TestObject{}
obj.Field = MyString("abc")
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal(MyString("hello"), obj.Field)
}
func Test_encode_object_contain_empty_interface(t *testing.T) {
type TestObject struct {
Field interface{}
}
should := require.New(t)
obj := TestObject{}
obj.Field = 1024
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":1024}`, str)
}
func Test_encode_object_contain_non_empty_interface(t *testing.T) {
type TestObject struct {
Field MyInterface
}
should := require.New(t)
obj := TestObject{}
obj.Field = MyString("hello")
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":"hello"}`, str)
}
func Test_nil_non_empty_interface(t *testing.T) {
ConfigDefault.(*frozenConfig).cleanEncoders()
ConfigDefault.(*frozenConfig).cleanDecoders()
type TestObject struct {
Field []MyInterface
}
should := require.New(t)
obj := TestObject{}
b := []byte(`{"Field":["AAA"]}`)
should.NotNil(json.Unmarshal(b, &obj))
should.NotNil(Unmarshal(b, &obj))
}
func Test_read_large_number_as_interface(t *testing.T) {
should := require.New(t)
var val interface{}
err := Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
should.Nil(err)
output, err := MarshalToString(val)
should.Nil(err)
should.Equal(`123456789123456789123456789`, output)
}
func Test_nested_one_field_struct(t *testing.T) {
should := require.New(t)
type YetYetAnotherObject struct {
Field string
}
type YetAnotherObject struct {
Field *YetYetAnotherObject
}
type AnotherObject struct {
Field *YetAnotherObject
}
type TestObject struct {
Me *AnotherObject
}
obj := TestObject{&AnotherObject{&YetAnotherObject{&YetYetAnotherObject{"abc"}}}}
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
str, err = MarshalToString(&obj)
should.Nil(err)
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
}
func Test_struct_with_embedded_ptr_with_tag(t *testing.T) {
type O1 struct {
O1F string
}
type Option struct {
O1 *O1
}
type T struct {
Option `json:","`
}
var obj T
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"O1":null}`, output)
}
func Test_struct_with_one_nil(t *testing.T) {
type TestObject struct {
F *float64
}
var obj TestObject
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"F":null}`, output)
}
func Test_struct_with_one_nil_embedded(t *testing.T) {
type Parent struct {
Field1 string
Field2 string
}
type TestObject struct {
*Parent
}
obj := TestObject{}
should := require.New(t)
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal("{}", string(bytes))
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{}`, output)
}
func Test_struct_with_not_nil_embedded(t *testing.T) {
type Parent struct {
Field0 string
Field1 []string
Field2 map[string]interface{}
}
type TestObject struct {
*Parent
}
should := require.New(t)
var obj TestObject
err := UnmarshalFromString(`{"Field0":"1","Field1":null,"Field2":{"K":"V"}}`, &obj)
should.Nil(err)
should.Nil(obj.Field1)
should.Equal(map[string]interface{}{"K": "V"}, obj.Field2)
should.Equal("1", obj.Field0)
}
func Test_array_with_one_nil_ptr(t *testing.T) {
obj := [1]*float64{nil}
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`[null]`, output)
}
func Test_array_with_one_not_nil_ptr(t *testing.T) {
two := float64(2)
obj := [1]*float64{&two}
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`[2]`, output)
}
func Test_embedded_array_with_one_nil(t *testing.T) {
type TestObject struct {
Field1 int
Field2 [1]*float64
}
var obj TestObject
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Contains(output, `"Field2":[null]`)
}
func Test_array_with_nothing(t *testing.T) {
var obj [2]*float64
should := require.New(t)
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`[null,null]`, output)
}

132
jsoniter_invalid_test.go Normal file
View File

@ -0,0 +1,132 @@
package jsoniter
import (
"encoding/json"
"github.com/stretchr/testify/require"
"io"
"testing"
"bytes"
)
func Test_missing_object_end(t *testing.T) {
should := require.New(t)
type TestObject struct {
Metric string `json:"metric"`
Tags map[string]interface{} `json:"tags"`
}
obj := TestObject{}
should.NotNil(UnmarshalFromString(`{"metric": "sys.777","tags": {"a":"123"}`, &obj))
}
func Test_missing_array_end(t *testing.T) {
should := require.New(t)
should.NotNil(UnmarshalFromString(`[1,2,3`, &[]int{}))
}
func Test_invalid_any(t *testing.T) {
should := require.New(t)
any := Get([]byte("[]"))
should.Equal(InvalidValue, any.Get(0.3).ValueType())
// is nil correct ?
should.Equal(nil, any.Get(0.3).GetInterface())
any = any.Get(0.3)
should.Equal(false, any.ToBool())
should.Equal(int(0), any.ToInt())
should.Equal(int32(0), any.ToInt32())
should.Equal(int64(0), any.ToInt64())
should.Equal(uint(0), any.ToUint())
should.Equal(uint32(0), any.ToUint32())
should.Equal(uint64(0), any.ToUint64())
should.Equal(float32(0), any.ToFloat32())
should.Equal(float64(0), any.ToFloat64())
should.Equal("", any.ToString())
should.Equal(InvalidValue, any.Get(0.1).Get(1).ValueType())
}
func Test_invalid_struct_input(t *testing.T) {
should := require.New(t)
type TestObject struct{}
input := []byte{54, 141, 30}
obj := TestObject{}
should.NotNil(Unmarshal(input, &obj))
}
func Test_invalid_slice_input(t *testing.T) {
should := require.New(t)
type TestObject struct{}
input := []byte{93}
obj := []string{}
should.NotNil(Unmarshal(input, &obj))
}
func Test_invalid_array_input(t *testing.T) {
should := require.New(t)
type TestObject struct{}
input := []byte{93}
obj := [0]string{}
should.NotNil(Unmarshal(input, &obj))
}
func Test_invalid_float(t *testing.T) {
inputs := []string{
`1.e1`, // dot without following digit
`1.`, // dot can not be the last char
``, // empty number
`01`, // extra leading zero
`-`, // negative without digit
`--`, // double negative
`--2`, // double negative
}
for _, input := range inputs {
t.Run(input, func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input+",")
iter.Skip()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
v := float64(0)
should.NotNil(json.Unmarshal([]byte(input), &v))
iter = ParseString(ConfigDefault, input+",")
iter.ReadFloat64()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
iter = ParseString(ConfigDefault, input+",")
iter.ReadFloat32()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
})
}
}
func Test_chan(t *testing.T) {
t.Skip("do not support chan")
type TestObject struct {
MyChan chan bool
MyField int
}
should := require.New(t)
obj := TestObject{}
str, err := json.Marshal(obj)
should.Nil(err)
should.Equal(``, str)
}
func Test_invalid_number(t *testing.T) {
type Message struct {
Number int `json:"number"`
}
obj := Message{}
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(bytes.NewBufferString(`{"number":"5"}`))
err := decoder.Decode(&obj)
invalidStr := err.Error()
result, err := ConfigCompatibleWithStandardLibrary.Marshal(invalidStr)
should := require.New(t)
should.Nil(err)
result2, err := json.Marshal(invalidStr)
should.Nil(err)
should.Equal(string(result2), string(result))
}

View File

@ -1,13 +1,14 @@
package jsoniter
import (
"testing"
"bytes"
"github.com/stretchr/testify/require"
"io"
"testing"
)
func Test_read_by_one(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 1)
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 1)
b := iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
@ -19,11 +20,6 @@ func Test_read_by_one(t *testing.T) {
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)
@ -34,40 +30,25 @@ func Test_read_by_one(t *testing.T) {
}
func Test_read_by_two(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 2)
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 2)
b := iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
should.Nil(iter.Error)
should.Equal(byte('a'), b)
b = iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'b' {
t.Fatal(b)
}
should.Nil(iter.Error)
should.Equal(byte('b'), b)
iter.unreadByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
should.Nil(iter.Error)
iter.unreadByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
should.Nil(iter.Error)
b = iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
should.Nil(iter.Error)
should.Equal(byte('a'), b)
}
func Test_read_until_eof(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 2)
iter := Parse(ConfigDefault, bytes.NewBufferString("abc"), 2)
iter.readByte()
iter.readByte()
b := iter.readByte()
@ -81,4 +62,4 @@ func Test_read_until_eof(t *testing.T) {
if iter.Error != io.EOF {
t.Fatal(iter.Error)
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,40 +1,144 @@
package jsoniter
import (
"testing"
"os"
"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 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 := Parse(file, 4096)
iter := Parse(ConfigDefault, file, 4096)
count := 0
for iter.ReadArray() {
iter.ReadArrayCB(func(iter *Iterator) bool {
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
iter.Skip()
count++
}
return true
})
file.Close()
if iter.Error != nil {
b.Error(iter.Error)
}
}
}
@ -45,6 +149,9 @@ func Benchmark_json_large_file(b *testing.B) {
bytes, _ := ioutil.ReadAll(file)
file.Close()
result := []struct{}{}
json.Unmarshal(bytes, &result)
err := json.Unmarshal(bytes, &result)
if err != nil {
b.Error(err)
}
}
}

View File

@ -1,38 +1,160 @@
package jsoniter
import (
"encoding/json"
"math/big"
"testing"
"reflect"
"fmt"
"github.com/stretchr/testify/require"
"strings"
)
func Test_read_map(t *testing.T) {
iter := ParseString(`{"hello": "world"}`)
should := require.New(t)
iter := ParseString(ConfigDefault, `{"hello": "world"}`)
m := map[string]string{"1": "2"}
iter.Read(&m)
copy(iter.buf, []byte{0,0,0,0,0,0})
if !reflect.DeepEqual(map[string]string{"1": "2", "hello": "world"}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
}
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) {
iter := ParseString(`{"hello": "world"}`)
should := require.New(t)
iter := ParseString(ConfigDefault, `{"hello": "world"}`)
m := map[string]interface{}{"1": "2"}
iter.Read(&m)
if !reflect.DeepEqual(map[string]interface{}{"1": "2", "hello": "world"}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
iter.ReadVal(&m)
should.Equal(map[string]interface{}{"1": "2", "hello": "world"}, m)
iter = ParseString(ConfigDefault, `{"hello": "world"}`)
should.Equal(map[string]interface{}{"hello": "world"}, iter.Read())
}
func Test_map_wrapper_any_get_all(t *testing.T) {
should := require.New(t)
any := Wrap(map[string][]int{"Field1": {1, 2}})
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
should.Contains(any.Keys(), "Field1")
// map write to
stream := NewStream(ConfigDefault, nil, 0)
any.WriteTo(stream)
// TODO cannot pass
//should.Equal(string(stream.buf), "")
}
func Test_write_val_map(t *testing.T) {
should := require.New(t)
val := map[string]string{"1": "2"}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_slice_of_map(t *testing.T) {
should := require.New(t)
val := []map[string]string{{"1": "2"}}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`[{"1":"2"}]`, str)
val = []map[string]string{}
should.Nil(UnmarshalFromString(str, &val))
should.Equal("2", val[0]["1"])
}
func Test_encode_int_key_map(t *testing.T) {
should := require.New(t)
val := map[int]string{1: "2"}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_decode_int_key_map(t *testing.T) {
should := require.New(t)
var val map[int]string
should.Nil(UnmarshalFromString(`{"1":"2"}`, &val))
should.Equal(map[int]string{1: "2"}, val)
}
func Test_encode_TextMarshaler_key_map(t *testing.T) {
should := require.New(t)
f, _, _ := big.ParseFloat("1", 10, 64, big.ToZero)
val := map[*big.Float]string{f: "2"}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_decode_TextMarshaler_key_map(t *testing.T) {
should := require.New(t)
var val map[*big.Float]string
should.Nil(UnmarshalFromString(`{"1":"2"}`, &val))
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_map_key_with_escaped_char(t *testing.T) {
type Ttest struct {
Map map[string]string
}
var jsonBytes = []byte(`
{
"Map":{
"k\"ey": "val"
}
}`)
should := require.New(t)
{
var obj Ttest
should.Nil(json.Unmarshal(jsonBytes, &obj))
should.Equal(map[string]string{"k\"ey": "val"}, obj.Map)
}
{
var obj Ttest
should.Nil(Unmarshal(jsonBytes, &obj))
should.Equal(map[string]string{"k\"ey": "val"}, obj.Map)
}
}
func Test_read_map_of_any(t *testing.T) {
iter := ParseString(`{"hello": "world"}`)
m := map[string]Any{"1": *MakeAny("2")}
iter.Read(&m)
if !reflect.DeepEqual(map[string]Any{"1": *MakeAny("2"), "hello": *MakeAny("world")}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
func Test_encode_map_with_sorted_keys(t *testing.T) {
should := require.New(t)
m := map[string]interface{}{
"3": 3,
"1": 1,
"2": 2,
}
bytes, err := json.Marshal(m)
should.Nil(err)
output, err := ConfigCompatibleWithStandardLibrary.MarshalToString(m)
should.Nil(err)
should.Equal(string(bytes), output)
}
func Test_encode_map_uint_keys(t *testing.T) {
should := require.New(t)
m := map[uint64]interface{}{
uint64(1): "a",
uint64(2): "a",
uint64(4): "a",
}
bytes, err := json.Marshal(m)
should.Nil(err)
output, err := ConfigCompatibleWithStandardLibrary.MarshalToString(m)
should.Nil(err)
should.Equal(string(bytes), output)
}
func Test_read_map_with_reader(t *testing.T) {
should := require.New(t)
input := `{"branch":"beta","change_log":"add the rows{10}","channel":"fros","create_time":"2017-06-13 16:39:08","firmware_list":"","md5":"80dee2bf7305bcf179582088e29fd7b9","note":{"CoreServices":{"md5":"d26975c0a8c7369f70ed699f2855cc2e","package_name":"CoreServices","version_code":"76","version_name":"1.0.76"},"FrDaemon":{"md5":"6b1f0626673200bc2157422cd2103f5d","package_name":"FrDaemon","version_code":"390","version_name":"1.0.390"},"FrGallery":{"md5":"90d767f0f31bcd3c1d27281ec979ba65","package_name":"FrGallery","version_code":"349","version_name":"1.0.349"},"FrLocal":{"md5":"f15a215b2c070a80a01f07bde4f219eb","package_name":"FrLocal","version_code":"791","version_name":"1.0.791"}},"pack_region_urls":{"CN":"https://s3.cn-north-1.amazonaws.com.cn/xxx-os/ttt_xxx_android_1.5.3.344.393.zip","default":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip","local":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip"},"pack_version":"1.5.3.344.393","pack_version_code":393,"region":"all","release_flag":0,"revision":62,"size":38966875,"status":3}`
reader := strings.NewReader(input)
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
m1 := map[string]interface{}{}
should.Nil(decoder.Decode(&m1))
m2 := map[string]interface{}{}
should.Nil(json.Unmarshal([]byte(input), &m2))
should.Equal(m2, m1)
should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"])
}

View File

@ -0,0 +1,71 @@
package jsoniter
import (
"testing"
"github.com/stretchr/testify/require"
)
// if must be valid is useless, just drop this test
func Test_must_be_valid(t *testing.T) {
should := require.New(t)
any := Get([]byte("123"))
should.Equal(any.MustBeValid().ToInt(), 123)
any = Wrap(int8(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int16(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int32(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(int64(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint8(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint16(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint32(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(uint64(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = Wrap(float32(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = Wrap(float64(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = Wrap(true)
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
any = Wrap(false)
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap(nil)
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap(struct{ age int }{age: 1})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap("abc")
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap([]int{})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = Wrap([]int{1, 2})
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
}

View File

@ -1,9 +1,9 @@
package jsoniter
import (
"testing"
"reflect"
"encoding/json"
"reflect"
"testing"
)
type Level1 struct {
@ -15,7 +15,7 @@ type Level2 struct {
}
func Test_nested(t *testing.T) {
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
@ -28,14 +28,14 @@ func Test_nested(t *testing.T) {
case "world":
l2.World = iter.ReadString()
default:
iter.ReportError("bind l2", "unexpected field: " + l2Field)
iter.ReportError("bind l2", "unexpected field: "+l2Field)
}
}
l2Array = append(l2Array, l2)
}
l1.Hello = l2Array
default:
iter.ReportError("bind l1", "unexpected field: " + l1Field)
iter.ReportError("bind l1", "unexpected field: "+l1Field)
}
}
if !reflect.DeepEqual(l1, Level1{
@ -50,7 +50,7 @@ func Test_nested(t *testing.T) {
func Benchmark_jsoniter_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
iter := ParseString(ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
@ -85,4 +85,4 @@ func Benchmark_json_nested(b *testing.B) {
l1 := Level1{}
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
}
}
}

View File

@ -1,18 +1,47 @@
package jsoniter
import (
"bytes"
"encoding/json"
"github.com/stretchr/testify/require"
"io"
"testing"
)
func Test_null(t *testing.T) {
iter := ParseString(`null`)
if iter.ReadNull() != true {
t.FailNow()
}
func Test_read_null(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `null`)
should.True(iter.ReadNil())
iter = ParseString(ConfigDefault, `null`)
should.Nil(iter.Read())
iter = ParseString(ConfigDefault, `navy`)
iter.Read()
should.True(iter.Error != nil && iter.Error != io.EOF)
iter = ParseString(ConfigDefault, `navy`)
iter.ReadNil()
should.True(iter.Error != nil && iter.Error != io.EOF)
}
func Test_null_object(t *testing.T) {
iter := ParseString(`[null,"a"]`)
func Test_write_null(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteNil()
stream.Flush()
should.Nil(stream.Error)
should.Equal("null", buf.String())
}
func Test_encode_null(t *testing.T) {
should := require.New(t)
str, err := MarshalToString(nil)
should.Nil(err)
should.Equal("null", str)
}
func Test_decode_null_object_field(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `[null,"a"]`)
iter.ReadArray()
if iter.ReadObject() != "" {
t.FailNow()
@ -21,38 +50,88 @@ func Test_null_object(t *testing.T) {
if iter.ReadString() != "a" {
t.FailNow()
}
type TestObject struct {
Field string
}
objs := []TestObject{}
should.Nil(UnmarshalFromString("[null]", &objs))
should.Len(objs, 1)
}
func Test_null_array(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadArray() != false {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
func Test_decode_null_array_element(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `[null,"a"]`)
should.True(iter.ReadArray())
should.True(iter.ReadNil())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_null_string(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadString() != "" {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
func Test_decode_null_array(t *testing.T) {
should := require.New(t)
arr := []string{}
should.Nil(UnmarshalFromString("null", &arr))
should.Nil(arr)
}
func Test_null_skip(t *testing.T) {
iter := ParseString(`[null,"a"]`)
func Test_decode_null_map(t *testing.T) {
should := require.New(t)
arr := map[string]string{}
should.Nil(UnmarshalFromString("null", &arr))
should.Nil(arr)
}
func Test_decode_null_string(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `[null,"a"]`)
should.True(iter.ReadArray())
should.Equal("", iter.ReadString())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_decode_null_skip(t *testing.T) {
iter := ParseString(ConfigDefault, `[null,"a"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}
}
func Test_encode_nil_map(t *testing.T) {
should := require.New(t)
type Ttest map[string]string
var obj1 Ttest
output, err := json.Marshal(obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = json.Marshal(&obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = Marshal(obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = Marshal(&obj1)
should.Nil(err)
should.Equal("null", string(output))
}
func Test_encode_nil_array(t *testing.T) {
should := require.New(t)
type Ttest []string
var obj1 Ttest
output, err := json.Marshal(obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = json.Marshal(&obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = Marshal(obj1)
should.Nil(err)
should.Equal("null", string(output))
output, err = Marshal(&obj1)
should.Nil(err)
should.Equal("null", string(output))
}

View File

@ -1,59 +1,57 @@
package jsoniter
import (
"testing"
"encoding/json"
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func Test_empty_object(t *testing.T) {
iter := ParseString(`{}`)
should := require.New(t)
iter := ParseString(ConfigDefault, `{}`)
field := iter.ReadObject()
if field != "" {
t.Fatal(field)
}
should.Equal("", field)
iter = ParseString(ConfigDefault, `{}`)
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
should.FailNow("should not call")
return true
})
}
func Test_one_field(t *testing.T) {
iter := ParseString(`{"a": "b"}`)
should := require.New(t)
iter := ParseString(ConfigDefault, `{"a": "stream"}`)
field := iter.ReadObject()
if field != "a" {
fmt.Println(iter.Error)
t.Fatal(field)
}
should.Equal("a", field)
value := iter.ReadString()
if value != "b" {
t.Fatal(field)
}
should.Equal("stream", value)
field = iter.ReadObject()
if field != "" {
t.Fatal(field)
}
should.Equal("", field)
iter = ParseString(ConfigDefault, `{"a": "stream"}`)
should.True(iter.ReadObjectCB(func(iter *Iterator, field string) bool {
should.Equal("a", field)
iter.Skip()
return true
}))
}
func Test_two_field(t *testing.T) {
iter := ParseString(`{ "a": "b" , "c": "d" }`)
should := require.New(t)
iter := ParseString(ConfigDefault, `{ "a": "stream" , "c": "d" }`)
field := iter.ReadObject()
if field != "a" {
t.Fatal(field)
}
should.Equal("a", field)
value := iter.ReadString()
if value != "b" {
t.Fatal(field)
}
should.Equal("stream", value)
field = iter.ReadObject()
if field != "c" {
t.Fatal(field)
}
should.Equal("c", field)
value = iter.ReadString()
if value != "d" {
t.Fatal(field)
}
should.Equal("d", value)
field = iter.ReadObject()
if field != "" {
t.Fatal(field)
}
iter = ParseString(`{"field1": "1", "field2": 2}`)
should.Equal("", field)
iter = ParseString(ConfigDefault, `{"field1": "1", "field2": 2}`)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
@ -66,31 +64,267 @@ func Test_two_field(t *testing.T) {
}
}
type TestObj struct {
Field1 string
Field2 uint64
func Test_object_wrapper_any_get_all(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 []int
Field2 []int
}
any := Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
should.Contains(any.Keys(), "Field1")
should.Contains(any.Keys(), "Field2")
should.NotContains(any.Keys(), "Field3")
//should.Contains(any.GetObject()["Field1"].GetArray()[0], 1)
}
func Benchmark_jsoniter_object(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`{"field1": "1", "field2": 2}`)
obj := TestObj{}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
obj.Field1 = iter.ReadString()
case "field2":
obj.Field2 = iter.ReadUint64()
default:
iter.ReportError("bind object", "unexpected field")
}
}
}
func Test_write_object(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096)
stream.WriteObjectStart()
stream.WriteObjectField("hello")
stream.WriteInt(1)
stream.WriteMore()
stream.WriteObjectField("world")
stream.WriteInt(2)
stream.WriteObjectEnd()
stream.Flush()
should.Nil(stream.Error)
should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String())
}
func Benchmark_json_object(b *testing.B) {
for n := 0; n < b.N; n++ {
result := TestObj{}
json.Unmarshal([]byte(`{"field1": "1", "field2": 2}`), &result)
func Test_write_val_zero_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
}
obj := TestObject{}
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{}`, str)
}
func Test_write_val_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1"`
}
obj := TestObject{"hello"}
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field-1":"hello"}`, str)
}
func Test_mixed(t *testing.T) {
should := require.New(t)
type AA struct {
ID int `json:"id"`
Payload map[string]interface{} `json:"payload"`
buf *bytes.Buffer
}
aa := AA{}
err := UnmarshalFromString(` {"id":1, "payload":{"account":"123","password":"456"}}`, &aa)
should.Nil(err)
should.Equal(1, aa.ID)
should.Equal("123", aa.Payload["account"])
}
func Test_omit_empty(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1,omitempty"`
Field2 string `json:"field-2,omitempty"`
Field3 string `json:"field-3,omitempty"`
}
obj := TestObject{}
obj.Field2 = "hello"
str, err := MarshalToString(&obj)
should.Nil(err)
should.Equal(`{"field-2":"hello"}`, str)
}
func Test_ignore_field_on_not_valid_type(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1,omitempty"`
Field2 func() `json:"-"`
}
obj := TestObject{}
obj.Field1 = "hello world"
obj.Field2 = func() {}
str, err := MarshalToString(&obj)
should.Nil(err)
should.Equal(`{"field-1":"hello world"}`, str)
}
func Test_recursive_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Me *TestObject
}
obj := TestObject{}
str, err := MarshalToString(obj)
should.Nil(err)
should.Contains(str, `"Field1":""`)
should.Contains(str, `"Me":null`)
err = UnmarshalFromString(str, &obj)
should.Nil(err)
}
func Test_encode_anonymous_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field string
}
str, err := MarshalToString(struct {
TestObject
Field int
}{
Field: 100,
})
should.Nil(err)
should.Equal(`{"Field":100}`, str)
}
func Test_decode_anonymous_struct(t *testing.T) {
should := require.New(t)
type Inner struct {
Key string `json:"key"`
}
type Outer struct {
Inner
}
var outer Outer
j := []byte("{\"key\":\"value\"}")
should.Nil(Unmarshal(j, &outer))
should.Equal("value", outer.Key)
}
func Test_multiple_level_anonymous_struct(t *testing.T) {
type Level1 struct {
Field1 string
}
type Level2 struct {
Level1
Field2 string
}
type Level3 struct {
Level2
Field3 string
}
should := require.New(t)
obj := Level3{Level2{Level1{"1"}, "2"}, "3"}
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field1":"1","Field2":"2","Field3":"3"}`, output)
}
func Test_multiple_level_anonymous_struct_with_ptr(t *testing.T) {
type Level1 struct {
Field1 string
Field2 string
Field4 string
}
type Level2 struct {
*Level1
Field2 string
Field3 string
}
type Level3 struct {
*Level2
Field3 string
}
should := require.New(t)
obj := Level3{&Level2{&Level1{"1", "", "4"}, "2", ""}, "3"}
output, err := MarshalToString(obj)
should.Nil(err)
should.Contains(output, `"Field1":"1"`)
should.Contains(output, `"Field2":"2"`)
should.Contains(output, `"Field3":"3"`)
should.Contains(output, `"Field4":"4"`)
}
func Test_shadow_struct_field(t *testing.T) {
should := require.New(t)
type omit *struct{}
type CacheItem struct {
Key string `json:"key"`
MaxAge int `json:"cacheAge"`
}
output, err := MarshalToString(struct {
*CacheItem
// Omit bad keys
OmitMaxAge omit `json:"cacheAge,omitempty"`
// Add nice keys
MaxAge int `json:"max_age"`
}{
CacheItem: &CacheItem{
Key: "value",
MaxAge: 100,
},
MaxAge: 20,
})
should.Nil(err)
should.Contains(output, `"key":"value"`)
should.Contains(output, `"max_age":20`)
}
func Test_embedded_order(t *testing.T) {
type A struct {
Field2 string
}
type C struct {
Field5 string
}
type B struct {
Field4 string
C
Field6 string
}
type TestObject struct {
Field1 string
A
Field3 string
B
Field7 string
}
should := require.New(t)
s := TestObject{}
output, err := MarshalToString(s)
should.Nil(err)
should.Equal(`{"Field1":"","Field2":"","Field3":"","Field4":"","Field5":"","Field6":"","Field7":""}`, output)
}
func Test_decode_nested(t *testing.T) {
type StructOfString struct {
Field1 string
Field2 string
}
iter := ParseString(ConfigDefault, `[{"field1": "hello"}, null, {"field2": "world"}]`)
slice := []*StructOfString{}
iter.ReadVal(&slice)
if len(slice) != 3 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0].Field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[1] != nil {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
if slice[2].Field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(slice[2])
}
}

46
jsoniter_optional_test.go Normal file
View File

@ -0,0 +1,46 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_encode_optional_int_pointer(t *testing.T) {
should := require.New(t)
var ptr *int
str, err := MarshalToString(ptr)
should.Nil(err)
should.Equal("null", str)
val := 100
ptr = &val
str, err = MarshalToString(ptr)
should.Nil(err)
should.Equal("100", str)
}
func Test_decode_struct_with_optional_field(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 *string
Field2 *string
}
obj := TestObject{}
UnmarshalFromString(`{"field1": null, "field2": "world"}`, &obj)
should.Nil(obj.Field1)
should.Equal("world", *obj.Field2)
}
func Test_encode_struct_with_optional_field(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 *string
Field2 *string
}
obj := TestObject{}
world := "world"
obj.Field2 = &world
str, err := MarshalToString(obj)
should.Nil(err)
should.Contains(str, `"Field1":null`)
should.Contains(str, `"Field2":"world"`)
}

View File

@ -0,0 +1,74 @@
package jsoniter
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)
func Test_json_RawMessage(t *testing.T) {
should := require.New(t)
var data json.RawMessage
should.Nil(Unmarshal([]byte(`[1,2,3]`), &data))
should.Equal(`[1,2,3]`, string(data))
str, err := MarshalToString(data)
should.Nil(err)
should.Equal(`[1,2,3]`, str)
}
func Test_jsoniter_RawMessage(t *testing.T) {
should := require.New(t)
var data RawMessage
should.Nil(Unmarshal([]byte(`[1,2,3]`), &data))
should.Equal(`[1,2,3]`, string(data))
str, err := MarshalToString(data)
should.Nil(err)
should.Equal(`[1,2,3]`, str)
}
func Test_json_RawMessage_in_struct(t *testing.T) {
type TestObject struct {
Field1 string
Field2 json.RawMessage
}
should := require.New(t)
var data TestObject
should.Nil(Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data))
should.Equal(` [1,2,3]`, string(data.Field2))
should.Equal(`hello`, data.Field1)
}
func Test_decode_map_of_raw_message(t *testing.T) {
should := require.New(t)
type RawMap map[string]*json.RawMessage
b := []byte("{\"test\":[{\"key\":\"value\"}]}")
var rawMap RawMap
should.Nil(Unmarshal(b, &rawMap))
should.Equal(`[{"key":"value"}]`, string(*rawMap["test"]))
type Inner struct {
Key string `json:"key"`
}
var inner []Inner
Unmarshal(*rawMap["test"], &inner)
should.Equal("value", inner[0].Key)
}
func Test_encode_map_of_raw_message(t *testing.T) {
should := require.New(t)
type RawMap map[string]*json.RawMessage
value := json.RawMessage("[]")
rawMap := RawMap{"hello": &value}
output, err := MarshalToString(rawMap)
should.Nil(err)
should.Equal(`{"hello":[]}`, output)
}
func Test_encode_map_of_jsoniter_raw_message(t *testing.T) {
should := require.New(t)
type RawMap map[string]*RawMessage
value := RawMessage("[]")
rawMap := RawMap{"hello": &value}
output, err := MarshalToString(rawMap)
should.Nil(err)
should.Equal(`{"hello":[]}`, output)
}

57
jsoniter_reader_test.go Normal file
View File

@ -0,0 +1,57 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"strings"
"testing"
"time"
)
func Test_reader_and_load_more(t *testing.T) {
should := require.New(t)
type TestObject struct {
CreatedAt time.Time
}
reader := strings.NewReader(`
{
"agency": null,
"candidateId": 0,
"candidate": "Blah Blah",
"bookingId": 0,
"shiftId": 1,
"shiftTypeId": 0,
"shift": "Standard",
"bonus": 0,
"bonusNI": 0,
"days": [],
"totalHours": 27,
"expenses": [],
"weekEndingDateSystem": "2016-10-09",
"weekEndingDateClient": "2016-10-09",
"submittedAt": null,
"submittedById": null,
"approvedAt": "2016-10-10T18:38:04Z",
"approvedById": 0,
"authorisedAt": "2016-10-10T18:38:04Z",
"authorisedById": 0,
"invoicedAt": "2016-10-10T20:00:00Z",
"revokedAt": null,
"revokedById": null,
"revokeReason": null,
"rejectedAt": null,
"rejectedById": null,
"rejectReasonCode": null,
"rejectReason": null,
"createdAt": "2016-10-03T00:00:00Z",
"updatedAt": "2016-11-09T10:26:13Z",
"updatedById": null,
"overrides": [],
"bookingApproverId": null,
"bookingApprover": null,
"status": "approved"
}
`)
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
obj := TestObject{}
should.Nil(decoder.Decode(&obj))
}

View File

@ -1,819 +0,0 @@
package jsoniter
import (
"reflect"
"errors"
"fmt"
"unsafe"
"sync/atomic"
"strings"
"io"
"strconv"
)
type Decoder interface {
decode(ptr unsafe.Pointer, iter *Iterator)
}
type stringDecoder struct {
}
func (decoder *stringDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = iter.ReadString()
}
type intDecoder struct {
}
func (decoder *intDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int)(ptr)) = iter.ReadInt()
}
type int8Decoder struct {
}
func (decoder *int8Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int8)(ptr)) = iter.ReadInt8()
}
type int16Decoder struct {
}
func (decoder *int16Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int16)(ptr)) = iter.ReadInt16()
}
type int32Decoder struct {
}
func (decoder *int32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int32)(ptr)) = iter.ReadInt32()
}
type int64Decoder struct {
}
func (decoder *int64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int64)(ptr)) = iter.ReadInt64()
}
type uintDecoder struct {
}
func (decoder *uintDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint)(ptr)) = iter.ReadUint()
}
type uint8Decoder struct {
}
func (decoder *uint8Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint8)(ptr)) = iter.ReadUint8()
}
type uint16Decoder struct {
}
func (decoder *uint16Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint16)(ptr)) = iter.ReadUint16()
}
type uint32Decoder struct {
}
func (decoder *uint32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint32)(ptr)) = iter.ReadUint32()
}
type uint64Decoder struct {
}
func (decoder *uint64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint64)(ptr)) = iter.ReadUint64()
}
type float32Decoder struct {
}
func (decoder *float32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float32)(ptr)) = iter.ReadFloat32()
}
type float64Decoder struct {
}
func (decoder *float64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float64)(ptr)) = iter.ReadFloat64()
}
type boolDecoder struct {
}
func (decoder *boolDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*bool)(ptr)) = iter.ReadBool()
}
type interfaceDecoder struct {
}
func (decoder *interfaceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.ReadAny().Get()
}
type anyDecoder struct {
}
func (decoder *anyDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Any)(ptr)) = *iter.ReadAny()
}
type stringNumberDecoder struct {
elemDecoder Decoder
}
func (decoder *stringNumberDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
c := iter.readByte()
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 optionalDecoder struct {
valueType reflect.Type
valueDecoder Decoder
}
func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNull() {
*((*unsafe.Pointer)(ptr)) = nil
} else {
value := reflect.New(decoder.valueType)
decoder.valueDecoder.decode(unsafe.Pointer(value.Pointer()), iter)
*((*uintptr)(ptr)) = value.Pointer()
}
}
type generalStructDecoder struct {
type_ reflect.Type
fields map[string]*structFieldDecoder
}
func (decoder *generalStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
fieldDecoder := decoder.fields[field]
if fieldDecoder == nil {
iter.Skip()
} else {
fieldDecoder.decode(ptr, iter)
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type skipDecoder struct {
type_ reflect.Type
}
func (decoder *skipDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
iter.Skip()
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type oneFieldStructDecoder struct {
type_ reflect.Type
fieldName string
fieldDecoder *structFieldDecoder
}
func (decoder *oneFieldStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
if field == decoder.fieldName {
decoder.fieldDecoder.decode(ptr, iter)
} else {
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type twoFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
}
func (decoder *twoFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type threeFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
fieldName3 string
fieldDecoder3 *structFieldDecoder
}
func (decoder *threeFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
case decoder.fieldName3:
decoder.fieldDecoder3.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type fourFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
fieldName3 string
fieldDecoder3 *structFieldDecoder
fieldName4 string
fieldDecoder4 *structFieldDecoder
}
func (decoder *fourFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
case decoder.fieldName3:
decoder.fieldDecoder3.decode(ptr, iter)
case decoder.fieldName4:
decoder.fieldDecoder4.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type structFieldDecoder struct {
field *reflect.StructField
fieldDecoder Decoder
}
func (decoder *structFieldDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
fieldPtr := uintptr(ptr) + decoder.field.Offset
decoder.fieldDecoder.decode(unsafe.Pointer(fieldPtr), iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%s: %s", decoder.field.Name, iter.Error.Error())
}
}
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()
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
elem := reflect.New(decoder.elemType)
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
}
}
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())
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
}
var DECODERS unsafe.Pointer
func addDecoderToCache(cacheKey reflect.Type, decoder Decoder) {
retry := true
for retry {
ptr := atomic.LoadPointer(&DECODERS)
cache := *(*map[reflect.Type]Decoder)(ptr)
copy := map[reflect.Type]Decoder{}
for k, v := range cache {
copy[k] = v
}
copy[cacheKey] = decoder
retry = !atomic.CompareAndSwapPointer(&DECODERS, ptr, unsafe.Pointer(&copy))
}
}
func getDecoderFromCache(cacheKey reflect.Type) Decoder {
ptr := atomic.LoadPointer(&DECODERS)
cache := *(*map[reflect.Type]Decoder)(ptr)
return cache[cacheKey]
}
var typeDecoders map[string]Decoder
var fieldDecoders map[string]Decoder
var fieldCustomizers []FieldCustomizerFunc
func init() {
typeDecoders = map[string]Decoder{}
fieldDecoders = map[string]Decoder{}
fieldCustomizers = []FieldCustomizerFunc{}
atomic.StorePointer(&DECODERS, unsafe.Pointer(&map[string]Decoder{}))
}
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
type FieldCustomizerFunc func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc)
type funcDecoder struct {
func_ DecoderFunc
}
func (decoder *funcDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.func_(ptr, iter)
}
func RegisterTypeDecoder(type_ string, func_ DecoderFunc) {
typeDecoders[type_] = &funcDecoder{func_}
}
func RegisterFieldDecoder(type_ string, field string, func_ DecoderFunc) {
fieldDecoders[fmt.Sprintf("%s/%s", type_, field)] = &funcDecoder{func_}
}
func RegisterFieldCustomizer(func_ FieldCustomizerFunc) {
fieldCustomizers = append(fieldCustomizers, func_)
}
func ClearDecoders() {
typeDecoders = map[string]Decoder{}
fieldDecoders = map[string]Decoder{}
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *struct{}
word unsafe.Pointer
}
func (iter *Iterator) ReadAny() (ret *Any) {
valueType := iter.WhatIsNext()
switch valueType {
case String:
return MakeAny(iter.ReadString())
case Number:
return iter.readNumber()
case Null:
return MakeAny(nil)
case Bool:
return MakeAny(iter.ReadBool())
case Array:
val := []interface{}{}
for (iter.ReadArray()) {
element := iter.ReadAny()
if iter.Error != nil {
return
}
val = append(val, element.val)
}
return MakeAny(val)
case Object:
val := map[string]interface{}{}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
element := iter.ReadAny()
if iter.Error != nil {
return
}
val[string([]byte(field))] = element.val
}
return MakeAny(val)
default:
iter.ReportError("ReadAny", fmt.Sprintf("unexpected value type: %v", valueType))
return MakeAny(nil)
}
}
func (iter *Iterator) readNumber() (ret *Any) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
foundFloat := false
foundNegative := false
for(hasMore) {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '-':
foundNegative = true
str = append(str, c)
continue
case '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
case '.', 'e', 'E':
foundFloat = true
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
number := *(*string)(unsafe.Pointer(&str))
if foundFloat {
val, err := strconv.ParseFloat(number, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
if foundNegative {
val, err := strconv.ParseInt(number, 10, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
val, err := strconv.ParseUint(number, 10, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
func (iter *Iterator) Read(obj interface{}) {
type_ := reflect.TypeOf(obj)
cacheKey := type_.Elem()
cachedDecoder := getDecoderFromCache(cacheKey)
if cachedDecoder == nil {
decoder, err := decoderOfType(type_)
if err != nil {
iter.Error = err
return
}
cachedDecoder = decoder
addDecoderToCache(cacheKey, decoder)
}
e := (*emptyInterface)(unsafe.Pointer(&obj))
cachedDecoder.decode(e.word, iter)
}
type prefix string
func (p prefix) addTo(decoder Decoder, err error) (Decoder, error) {
if err != nil {
return nil, fmt.Errorf("%s: %s", p, err.Error())
}
return decoder, err
}
func decoderOfType(type_ reflect.Type) (Decoder, error) {
switch type_.Kind() {
case reflect.Ptr:
return prefix("ptr").addTo(decoderOfPtr(type_.Elem()))
default:
return nil, errors.New("expect ptr")
}
}
func decoderOfPtr(type_ reflect.Type) (Decoder, error) {
typeName := type_.String()
if typeName == "jsoniter.Any" {
return &anyDecoder{}, nil
}
typeDecoder := typeDecoders[typeName]
if typeDecoder != nil {
return typeDecoder, nil
}
switch type_.Kind() {
case reflect.String:
return &stringDecoder{}, nil
case reflect.Int:
return &intDecoder{}, nil
case reflect.Int8:
return &int8Decoder{}, nil
case reflect.Int16:
return &int16Decoder{}, nil
case reflect.Int32:
return &int32Decoder{}, nil
case reflect.Int64:
return &int64Decoder{}, nil
case reflect.Uint:
return &uintDecoder{}, nil
case reflect.Uint8:
return &uint8Decoder{}, nil
case reflect.Uint16:
return &uint16Decoder{}, nil
case reflect.Uint32:
return &uint32Decoder{}, nil
case reflect.Uint64:
return &uint64Decoder{}, nil
case reflect.Float32:
return &float32Decoder{}, nil
case reflect.Float64:
return &float64Decoder{}, nil
case reflect.Bool:
return &boolDecoder{}, nil
case reflect.Interface:
return &interfaceDecoder{}, nil
case reflect.Struct:
return decoderOfStruct(type_)
case reflect.Slice:
return prefix("[slice]").addTo(decoderOfSlice(type_))
case reflect.Map:
return prefix("[map]").addTo(decoderOfMap(type_))
case reflect.Ptr:
return prefix("[optional]").addTo(decoderOfOptional(type_.Elem()))
default:
return nil, fmt.Errorf("unsupported type: %v", type_)
}
}
func decoderOfOptional(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_)
if err != nil {
return nil, err
}
return &optionalDecoder{type_, decoder}, nil
}
func decoderOfStruct(type_ reflect.Type) (Decoder, error) {
fields := map[string]*structFieldDecoder{}
for i := 0; i < type_.NumField(); i++ {
field := type_.Field(i)
fieldDecoderKey := fmt.Sprintf("%s/%s", type_.String(), field.Name)
var fieldNames []string
for _, customizer := range fieldCustomizers {
alternativeFieldNames, func_ := customizer(type_, &field)
if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames
}
if func_ != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{func_}
}
}
decoder := fieldDecoders[fieldDecoderKey]
tagParts := strings.Split(field.Tag.Get("json"), ",")
if fieldNames == nil {
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
}
}
if decoder == nil {
var err error
decoder, err = decoderOfPtr(field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addTo(decoder, err)
}
}
if len(tagParts) > 1 && tagParts[1] == "string" {
decoder = &stringNumberDecoder{decoder}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldDecoder{&field, decoder}
}
}
switch len(fields) {
case 0:
return &skipDecoder{type_}, nil
case 1:
for fieldName, fieldDecoder := range fields {
return &oneFieldStructDecoder{type_, fieldName, fieldDecoder}, nil
}
case 2:
var fieldName1 string
var fieldName2 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
}
}
return &twoFieldsStructDecoder{type_, fieldName1, fieldDecoder1, fieldName2, fieldDecoder2}, nil
case 3:
var fieldName1 string
var fieldName2 string
var fieldName3 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else if fieldName2 == "" {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
} else {
fieldName3 = fieldName
fieldDecoder3 = fieldDecoder
}
}
return &threeFieldsStructDecoder{type_,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
case 4:
var fieldName1 string
var fieldName2 string
var fieldName3 string
var fieldName4 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else if fieldName2 == "" {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
} else if fieldName3 == "" {
fieldName3 = fieldName
fieldDecoder3 = fieldDecoder
} else {
fieldName4 = fieldName
fieldDecoder4 = fieldDecoder
}
}
return &fourFieldsStructDecoder{type_,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
}
return &generalStructDecoder{type_, fields}, nil
}
func decoderOfSlice(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_.Elem())
if err != nil {
return nil, err
}
return &sliceDecoder{type_, type_.Elem(), decoder}, nil
}
func decoderOfMap(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_.Elem())
if err != nil {
return nil, err
}
mapInterface := reflect.New(type_).Interface()
return &mapDecoder{type_, type_.Elem(), decoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
}

View File

@ -0,0 +1,154 @@
package jsoniter
import (
"fmt"
"testing"
)
func Test_reflect_str(t *testing.T) {
iter := ParseString(ConfigDefault, `"hello"`)
str := ""
iter.ReadVal(&str)
if str != "hello" {
fmt.Println(iter.Error)
t.Fatal(str)
}
}
func Test_reflect_ptr_str(t *testing.T) {
iter := ParseString(ConfigDefault, `"hello"`)
var str *string
iter.ReadVal(&str)
if *str != "hello" {
t.Fatal(str)
}
}
func Test_reflect_int(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := int(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int8(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := int8(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int16(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := int16(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int32(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := int32(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int64(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := int64(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := uint(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint8(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := uint8(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint16(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := uint16(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint32(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := uint32(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint64(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := uint64(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_byte(t *testing.T) {
iter := ParseString(ConfigDefault, `123`)
val := byte(0)
iter.ReadVal(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_float32(t *testing.T) {
iter := ParseString(ConfigDefault, `1.23`)
val := float32(0)
iter.ReadVal(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_float64(t *testing.T) {
iter := ParseString(ConfigDefault, `1.23`)
val := float64(0)
iter.ReadVal(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_bool(t *testing.T) {
iter := ParseString(ConfigDefault, `true`)
val := false
iter.ReadVal(&val)
if val != true {
fmt.Println(iter.Error)
t.Fatal(val)
}
}

View File

@ -1,341 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
"encoding/json"
"unsafe"
)
func Test_reflect_str(t *testing.T) {
iter := ParseString(`"hello"`)
str := ""
iter.Read(&str)
if str != "hello" {
fmt.Println(iter.Error)
t.Fatal(str)
}
}
func Test_reflect_ptr_str(t *testing.T) {
iter := ParseString(`"hello"`)
var str *string
iter.Read(&str)
if *str != "hello" {
t.Fatal(str)
}
}
func Test_reflect_int(t *testing.T) {
iter := ParseString(`123`)
val := int(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int8(t *testing.T) {
iter := ParseString(`123`)
val := int8(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int16(t *testing.T) {
iter := ParseString(`123`)
val := int16(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int32(t *testing.T) {
iter := ParseString(`123`)
val := int32(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int64(t *testing.T) {
iter := ParseString(`123`)
val := int64(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint(t *testing.T) {
iter := ParseString(`123`)
val := uint(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint8(t *testing.T) {
iter := ParseString(`123`)
val := uint8(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint16(t *testing.T) {
iter := ParseString(`123`)
val := uint16(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint32(t *testing.T) {
iter := ParseString(`123`)
val := uint32(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint64(t *testing.T) {
iter := ParseString(`123`)
val := uint64(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_byte(t *testing.T) {
iter := ParseString(`123`)
val := byte(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_float32(t *testing.T) {
iter := ParseString(`1.23`)
val := float32(0)
iter.Read(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_float64(t *testing.T) {
iter := ParseString(`1.23`)
val := float64(0)
iter.Read(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_bool(t *testing.T) {
iter := ParseString(`true`)
val := false
iter.Read(&val)
if val != true {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
type StructOfString struct {
field1 string
field2 string
}
func Test_reflect_struct_string(t *testing.T) {
iter := ParseString(`{"field1": "hello", "field2": "world"}`)
struct_ := StructOfString{}
iter.Read(&struct_)
if struct_.field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
}
type StructOfStringPtr struct {
field1 *string
field2 *string
}
func Test_reflect_struct_string_ptr(t *testing.T) {
iter := ParseString(`{"field1": null, "field2": "world"}`)
struct_ := StructOfStringPtr{}
iter.Read(&struct_)
if struct_.field1 != nil {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if *struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field2)
}
}
type StructOfTag struct {
field1 string `json:"field-1"`
field2 string `json:"-"`
field3 int `json:",string"`
}
func Test_reflect_struct_tag_field(t *testing.T) {
iter := ParseString(`{"field-1": "hello", "field2": "", "field3": "100"}`)
struct_ := StructOfTag{field2: "world"}
iter.Read(&struct_)
if struct_.field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field2)
}
if struct_.field3 != 100 {
fmt.Println(iter.Error)
t.Fatal(struct_.field3)
}
}
func Test_reflect_slice(t *testing.T) {
iter := ParseString(`["hello", "world"]`)
slice := make([]string, 0, 5)
iter.Read(&slice)
if len(slice) != 2 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0] != "hello" {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[1] != "world" {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_large_slice(t *testing.T) {
iter := ParseString(`[1,2,3,4,5,6,7,8,9]`)
slice := make([]int, 0, 1)
iter.Read(&slice)
if len(slice) != 9 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0] != 1 {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[8] != 9 {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_nested(t *testing.T) {
iter := ParseString(`[{"field1": "hello"}, null, {"field2": "world"}]`)
slice := []*StructOfString{}
iter.Read(&slice)
if len(slice) != 3 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0].field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[1] != nil {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
if slice[2].field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_base64(t *testing.T) {
iter := ParseString(`"YWJj"`)
val := []byte{}
RegisterTypeDecoder("[]uint8", func(ptr unsafe.Pointer, iter *Iterator) {
*((*[]byte)(ptr)) = iter.ReadBase64()
})
defer ClearDecoders()
iter.Read(&val)
if "abc" != string(val) {
t.Fatal(string(val))
}
}
type StructOfTagOne struct {
field1 string `json:"field1"`
field2 string `json:"field2"`
field3 int `json:"field3,string"`
field4 int `json:"field4,string"`
}
func Benchmark_jsoniter_reflect(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
iter := ParseString(`{"field3": "100"}`)
struct_ := StructOfTagOne{}
iter.Read(&struct_)
//iter := ParseString(`[1,2,3]`)
//var array []int
//iter.Read(&array)
}
}
func Benchmark_jsoniter_direct(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
//iter := ParseString(`{"field1": "hello", "field2": "world"}`)
//struct_ := StructOfString{}
//for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
// switch field {
// case "field1":
// struct_.field1 = iter.ReadString()
// case "field2":
// struct_.field2 = iter.ReadString()
// default:
// iter.Skip()
// }
//}
iter := ParseString(`["hello", "world"]`)
array := make([]string, 0, 2)
for iter.ReadArray() {
array = append(array, iter.ReadString())
}
}
}
func Benchmark_json_reflect(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
struct_ := StructOfTagOne{}
json.Unmarshal([]byte(`{"field3": "100"}`), &struct_)
//array := make([]string, 0, 2)
//json.Unmarshal([]byte(`["hello", "world"]`), &array)
}
}

View File

@ -1,81 +1,109 @@
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func Test_skip_number(t *testing.T) {
iter := ParseString(`[-0.12, "b"]`)
func Test_skip_number_in_array(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `[-0.12, "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
should.Nil(iter.Error)
should.Equal("stream", iter.ReadString())
}
func Test_skip_string_in_array(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `["hello", "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
should.Nil(iter.Error)
should.Equal("stream", iter.ReadString())
}
func Test_skip_null(t *testing.T) {
iter := ParseString(`[null , "b"]`)
iter := ParseString(ConfigDefault, `[null , "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_true(t *testing.T) {
iter := ParseString(`[true , "b"]`)
iter := ParseString(ConfigDefault, `[true , "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_false(t *testing.T) {
iter := ParseString(`[false , "b"]`)
iter := ParseString(ConfigDefault, `[false , "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_array(t *testing.T) {
iter := ParseString(`[[1, [2, [3], 4]], "b"]`)
iter := ParseString(ConfigDefault, `[[1, [2, [3], 4]], "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_empty_array(t *testing.T) {
iter := ParseString(`[ [ ], "b"]`)
iter := ParseString(ConfigDefault, `[ [ ], "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_nested(t *testing.T) {
iter := ParseString(`[ {"a" : [{"b": "c"}], "d": 102 }, "b"]`)
iter := ParseString(ConfigDefault, `[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
if iter.ReadString() != "stream" {
t.FailNow()
}
}
func Test_skip_and_return_bytes(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`)
iter.ReadArray()
skipped := iter.SkipAndReturnBytes()
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
}
func Test_skip_and_return_bytes_with_reader(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(`[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`), 4)
iter.ReadArray()
skipped := iter.SkipAndReturnBytes()
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
}
type TestResp struct {
Code uint64
}
@ -107,7 +135,7 @@ func Benchmark_jsoniter_skip(b *testing.B) {
}`)
for n := 0; n < b.N; n++ {
result := TestResp{}
iter := ParseBytes(input)
iter := ParseBytes(ConfigDefault, input)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "code":
@ -148,4 +176,4 @@ func Benchmark_json_skip(b *testing.B) {
result := TestResp{}
json.Unmarshal(input, &result)
}
}
}

View File

@ -1,61 +1,64 @@
//+build jsoniter-sloppy
package jsoniter
import (
"testing"
"github.com/stretchr/testify/require"
"io"
"testing"
)
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)
}
@ -89,66 +92,55 @@ func (reader *StagedReader) Read(p []byte) (n int, err error) {
}
func Test_skip_string(t *testing.T) {
iter := ParseString(`"abc`)
should := require.New(t)
iter := ParseString(ConfigDefault, `"abc`)
iter.skipString()
if iter.head != 1 {
t.Fatal(iter.head)
}
iter = ParseString(`\""abc`)
should.Equal(1, iter.head)
iter = ParseString(ConfigDefault, `\""abc`)
iter.skipString()
if iter.head != 3 {
t.Fatal(iter.head)
}
should.Equal(3, iter.head)
reader := &StagedReader{
r1: `abc`,
r2: `"`,
}
iter = Parse(reader, 4096)
iter = Parse(ConfigDefault, reader, 4096)
iter.skipString()
if iter.head != 1 {
t.Fatal(iter.head)
}
should.Equal(1, iter.head)
reader = &StagedReader{
r1: `abc`,
r2: `1"`,
}
iter = Parse(reader, 4096)
iter = Parse(ConfigDefault, reader, 4096)
iter.skipString()
if iter.head != 2 {
t.Fatal(iter.head)
}
should.Equal(2, iter.head)
reader = &StagedReader{
r1: `abc\`,
r2: `"`,
}
iter = Parse(reader, 4096)
iter = Parse(ConfigDefault, reader, 4096)
iter.skipString()
if iter.Error != io.EOF {
t.Fatal(iter.Error)
}
should.NotNil(iter.Error)
reader = &StagedReader{
r1: `abc\`,
r2: `""`,
}
iter = Parse(reader, 4096)
iter = Parse(ConfigDefault, reader, 4096)
iter.skipString()
if iter.head != 2 {
t.Fatal(iter.head)
}
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)
@ -157,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)

53
jsoniter_stream_test.go Normal file
View File

@ -0,0 +1,53 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_writeByte_should_grow_buffer(t *testing.T) {
should := require.New(t)
stream := NewStream(ConfigDefault, nil, 1)
stream.writeByte('1')
should.Equal("1", string(stream.Buffer()))
should.Equal(1, len(stream.buf))
stream.writeByte('2')
should.Equal("12", string(stream.Buffer()))
should.Equal(2, len(stream.buf))
stream.writeThreeBytes('3', '4', '5')
should.Equal("12345", string(stream.Buffer()))
}
func Test_writeBytes_should_grow_buffer(t *testing.T) {
should := require.New(t)
stream := NewStream(ConfigDefault, nil, 1)
stream.Write([]byte{'1', '2'})
should.Equal("12", string(stream.Buffer()))
should.Equal(3, len(stream.buf))
stream.Write([]byte{'3', '4', '5', '6', '7'})
should.Equal("1234567", string(stream.Buffer()))
should.Equal(8, len(stream.buf))
}
func Test_writeIndention_should_grow_buffer(t *testing.T) {
should := require.New(t)
stream := NewStream(Config{IndentionStep: 2}.Froze(), nil, 1)
stream.WriteVal([]int{1, 2, 3})
should.Equal("[\n 1,\n 2,\n 3\n]", string(stream.Buffer()))
}
func Test_writeRaw_should_grow_buffer(t *testing.T) {
should := require.New(t)
stream := NewStream(ConfigDefault, nil, 1)
stream.WriteRaw("123")
should.Nil(stream.Error)
should.Equal("123", string(stream.Buffer()))
}
func Test_writeString_should_grow_buffer(t *testing.T) {
should := require.New(t)
stream := NewStream(ConfigDefault, nil, 0)
stream.WriteString("123")
should.Nil(stream.Error)
should.Equal(`"123"`, string(stream.Buffer()))
}

View File

@ -1,111 +1,248 @@
// +build go1.8
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
"fmt"
"testing"
"unicode/utf8"
"github.com/stretchr/testify/require"
)
func Test_string_empty(t *testing.T) {
iter := Parse(bytes.NewBufferString(`""`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
func Test_read_string(t *testing.T) {
badInputs := []string{
``,
`"`,
`"\"`,
`"\\\"`,
"\"\n\"",
`"\U0001f64f"`,
`"\uD83D\u00"`,
}
if val != "" {
t.Fatal(val)
for i := 0; i < 32; i++ {
// control characters are invalid
badInputs = append(badInputs, string([]byte{'"', byte(i), '"'}))
}
for _, input := range badInputs {
testReadString(t, input, "", true, "json.Unmarshal", json.Unmarshal)
testReadString(t, input, "", true, "jsoniter.Unmarshal", Unmarshal)
testReadString(t, input, "", true, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", ConfigCompatibleWithStandardLibrary.Unmarshal)
}
goodInputs := []struct {
input string
expectValue string
}{
{`""`, ""},
{`"a"`, "a"},
{`null`, ""},
{`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"},
{`"\uD83D"`, string([]byte{239, 191, 189})},
{`"\uD83D\\"`, string([]byte{239, 191, 189, '\\'})},
{`"\uD83D\ub000"`, string([]byte{239, 191, 189, 235, 128, 128})},
{`"\uD83D\ude04"`, "😄"},
{`"\uDEADBEEF"`, string([]byte{239, 191, 189, 66, 69, 69, 70})},
}
for _, tc := range goodInputs {
testReadString(t, tc.input, tc.expectValue, false, "json.Unmarshal", json.Unmarshal)
testReadString(t, tc.input, tc.expectValue, false, "jsoniter.Unmarshal", Unmarshal)
testReadString(t, tc.input, tc.expectValue, false, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", ConfigCompatibleWithStandardLibrary.Unmarshal)
}
}
func Test_string_hello(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hello"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
func testReadString(t *testing.T, input string, expectValue string, expectError bool, marshalerName string, marshaler func([]byte, interface{}) error) {
var value string
err := marshaler([]byte(input), &value)
if expectError != (err != nil) {
t.Errorf("%q: %s: expected error %v, got %v", input, marshalerName, expectError, err)
return
}
if val != "hello" {
t.Fatal(val)
if value != expectValue {
t.Errorf("%q: %s: expected %q, got %q", input, marshalerName, expectValue, value)
return
}
}
func Test_string_escape_quote(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hel\"lo"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
func Test_read_normal_string(t *testing.T) {
cases := map[string]string{
`"0123456789012345678901234567890123456789"`: `0123456789012345678901234567890123456789`,
`""`: ``,
`"hello"`: `hello`,
}
if val != `hel"lo` {
t.Fatal(val)
for input, output := range cases {
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
should.Equal(output, iter.ReadString())
})
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
should.Equal(output, iter.ReadString())
})
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
should.Equal(output, string(iter.ReadStringAsSlice()))
})
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
should.Equal(output, string(iter.ReadStringAsSlice()))
})
}
}
func Test_string_escape_newline(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hel\nlo"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
func Test_read_exotic_string(t *testing.T) {
cases := map[string]string{
`"hel\"lo"`: `hel"lo`,
`"hel\\\/lo"`: `hel\/lo`,
`"hel\\blo"`: `hel\blo`,
`"hel\\\blo"`: "hel\\\blo",
`"hel\\nlo"`: `hel\nlo`,
`"hel\\\nlo"`: "hel\\\nlo",
`"hel\\tlo"`: `hel\tlo`,
`"hel\\flo"`: `hel\flo`,
`"hel\\\flo"`: "hel\\\flo",
`"hel\\\rlo"`: "hel\\\rlo",
`"hel\\\tlo"`: "hel\\\tlo",
`"\u4e2d\u6587"`: "中文",
`"\ud83d\udc4a"`: "\xf0\x9f\x91\x8a", // surrogate
}
if val != "hel\nlo" {
t.Fatal(val)
for input, output := range cases {
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, input)
var v string
should.Nil(json.Unmarshal([]byte(input), &v))
should.Equal(v, iter.ReadString())
})
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t)
iter := Parse(ConfigDefault, bytes.NewBufferString(input), 2)
should.Equal(output, iter.ReadString())
})
}
}
func Test_string_escape_unicode(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"\u4e2d\u6587"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "中文" {
t.Fatal(val)
func Test_read_string_as_interface(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `"hello"`)
should.Equal("hello", iter.Read())
}
func Test_write_string(t *testing.T) {
should := require.New(t)
str, err := MarshalToString("hello")
should.Equal(`"hello"`, str)
should.Nil(err)
str, err = MarshalToString(`hel"lo`)
should.Equal(`"hel\"lo"`, str)
should.Nil(err)
}
func Test_write_val_string(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(ConfigDefault, buf, 4096)
stream.WriteVal("hello")
stream.Flush()
should.Nil(stream.Error)
should.Equal(`"hello"`, buf.String())
}
func Test_decode_slash(t *testing.T) {
should := require.New(t)
var obj interface{}
should.NotNil(json.Unmarshal([]byte("\\"), &obj))
should.NotNil(UnmarshalFromString("\\", &obj))
}
func Test_html_escape(t *testing.T) {
should := require.New(t)
output, err := json.Marshal(`>`)
should.Nil(err)
should.Equal(`"\u003e"`, string(output))
output, err = ConfigCompatibleWithStandardLibrary.Marshal(`>`)
should.Nil(err)
should.Equal(`"\u003e"`, string(output))
type MyString string
output, err = ConfigCompatibleWithStandardLibrary.Marshal(MyString(`>`))
should.Nil(err)
should.Equal(`"\u003e"`, string(output))
}
func Test_string_encode_with_std(t *testing.T) {
should := require.New(t)
for i := 0; i < utf8.RuneSelf; i++ {
input := string([]byte{byte(i)})
stdOutputBytes, err := json.Marshal(input)
should.Nil(err)
stdOutput := string(stdOutputBytes)
jsoniterOutputBytes, err := ConfigCompatibleWithStandardLibrary.Marshal(input)
should.Nil(err)
jsoniterOutput := string(jsoniterOutputBytes)
should.Equal(stdOutput, jsoniterOutput)
}
}
func Test_string_escape_unicode_with_surrogate(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"\ud83d\udc4a"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "\xf0\x9f\x91\x8a" {
t.Fatal(val)
}
func Test_unicode(t *testing.T) {
should := require.New(t)
output, _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
should.Equal(`{"a":"数字山谷"}`, output)
output, _ = Config{EscapeHTML: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
should.Equal(`{"a":"数字山谷"}`, output)
}
func Test_string_as_bytes(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hello""world"`), 4096)
val := string(iter.readStringAsBytes())
if val != "hello" {
t.Fatal(val)
}
val = string(iter.readStringAsBytes())
if val != "world" {
t.Fatal(val)
}
func Test_unicode_and_escape(t *testing.T) {
should := require.New(t)
output, err := MarshalToString(`"数字山谷"`)
should.Nil(err)
should.Equal(`"\"数字山谷\""`, output)
output, err = ConfigFastest.MarshalToString(`"数字山谷"`)
should.Nil(err)
should.Equal(`"\"数字山谷\""`, output)
}
func Test_unsafe_unicode(t *testing.T) {
ConfigDefault.(*frozenConfig).cleanEncoders()
should := require.New(t)
output, err := ConfigDefault.MarshalToString("he\u2029\u2028he")
should.Nil(err)
should.Equal(`"he\u2029\u2028he"`, output)
output, err = ConfigFastest.MarshalToString("he\u2029\u2028he")
should.Nil(err)
should.Equal("\"he\u2029\u2028he\"", output)
}
func Benchmark_jsoniter_unicode(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`"\ud83d\udc4a"`)
iter := ParseString(ConfigDefault, `"\ud83d\udc4a"`)
iter.ReadString()
}
}
func Benchmark_jsoniter_ascii(b *testing.B) {
iter := ParseString(`"hello, world!"`)
iter := NewIterator(ConfigDefault)
input := []byte(`"hello, world! hello, world!"`)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(iter.buf)
iter.ResetBytes(input)
iter.ReadString()
}
}
func Benchmark_jsoniter_string_as_bytes(b *testing.B) {
iter := ParseString(`"hello, world!"`)
iter := ParseString(ConfigDefault, `"hello, world!"`)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(iter.buf)
iter.readStringAsBytes()
iter.ReadStringAsSlice()
}
}
@ -121,4 +258,4 @@ func Benchmark_json_ascii(b *testing.B) {
result := ""
json.Unmarshal([]byte(`"hello"`), &result)
}
}
}

View File

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

118
jsoniter_wrap_test.go Normal file
View File

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

View File

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

View File

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

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