├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── benchmark ├── Makefile ├── codec_test.go ├── data.go ├── data_codec.go ├── data_ffjson.go ├── data_var.go ├── default_test.go ├── dummy_test.go ├── easyjson_test.go ├── example.json ├── ffjson_test.go ├── go.mod ├── go.sum ├── jsoniter_test.go ├── tools.go └── ujson.sh ├── bootstrap └── bootstrap.go ├── buffer ├── buffers.go ├── pool.go └── pool_test.go ├── gen ├── decoder.go ├── encoder.go ├── generator.go └── generator_test.go ├── go.mod ├── go.sum ├── helpers.go ├── helpers_test.go ├── jlexer ├── bytestostr.go ├── bytestostr_nounsafe.go ├── error.go ├── lexer.go ├── lexer_test.go └── scanner.go ├── jwriter └── writer.go ├── opt ├── gotemplate_Bool.go ├── gotemplate_Int.go ├── gotemplate_Int16.go ├── gotemplate_Int32.go ├── gotemplate_Int64.go ├── gotemplate_Int8.go ├── gotemplate_String.go ├── gotemplate_Uint.go ├── gotemplate_Uint16.go ├── gotemplate_Uint32.go ├── gotemplate_Uint64.go ├── gotemplate_Uint8.go ├── optional │ └── opt.go └── opts.go ├── parser ├── modulepath.go ├── parser.go ├── pkgpath.go ├── pkgpath_test.go └── testdata │ ├── comments.go.mod │ ├── comments_deps.go.mod │ ├── default.go.mod │ └── missing_module.go.mod ├── raw.go ├── tests ├── basic_test.go ├── custom_map_key_type.go ├── data.go ├── disallow_unknown.go ├── embedded_type.go ├── errors.go ├── errors_test.go ├── escaping.go ├── escaping_test.go ├── html.go ├── html_test.go ├── intern.go ├── intern_test.go ├── key_marshaler_map.go ├── members_escaped.go ├── members_escaping_test.go ├── members_unescaped.go ├── named_type.go ├── nested_easy.go ├── nested_marshaler.go ├── nocopy.go ├── nocopy_test.go ├── nothing.go ├── omitempty.go ├── opt_test.go ├── reference_to_pointer.go ├── required_test.go ├── snake.go ├── type_declaration.go ├── type_declaration_skip.go ├── unknown_fields.go └── unknown_fields_test.go ├── tiny-tests ├── benchmark_test.go ├── cosmwasm.go ├── cosmwasm_test.go └── generate_test.go ├── tinyjson └── main.go └── unknown_fields.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference 2 | version: 2.1 3 | jobs: 4 | build: 5 | working_directory: ~/repo 6 | docker: 7 | - image: cimg/go:1.16 8 | steps: 9 | - checkout 10 | - restore_cache: 11 | keys: 12 | - go-mod-v5-{{ checksum "go.sum" }} 13 | - run: 14 | name: Install Dependencies 15 | command: go mod download 16 | - save_cache: 17 | key: go-mod-v5-{{ checksum "go.sum" }} 18 | paths: 19 | - "/go/pkg/mod" 20 | - run: 21 | name: Cleanup 22 | command: make clean 23 | - run: 24 | name: Generate files 25 | command: make generate && make tiny-generate 26 | - run: 27 | name: Run tests 28 | command: | 29 | mkdir -p /tmp/test-reports 30 | gotestsum --junitfile /tmp/test-reports/unit-tests.xml 31 | - store_test_results: 32 | path: /tmp/test-reports 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .root 2 | *_tinyjson.go 3 | *.iml 4 | .idea 5 | *.swp 6 | bin/* 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Mail.Ru Group 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | clean: 4 | rm -rf bin 5 | rm -rf tests/*_tinyjson.go 6 | rm -rf benchmark/*_tinyjson.go 7 | 8 | build: 9 | go build -o ./bin/tinyjson ./tinyjson 10 | 11 | generate: build 12 | bin/tinyjson -stubs \ 13 | ./tests/snake.go \ 14 | ./tests/data.go \ 15 | ./tests/omitempty.go \ 16 | ./tests/nothing.go \ 17 | ./tests/named_type.go \ 18 | ./tests/custom_map_key_type.go \ 19 | ./tests/embedded_type.go \ 20 | ./tests/reference_to_pointer.go \ 21 | ./tests/html.go \ 22 | ./tests/unknown_fields.go \ 23 | ./tests/type_declaration.go \ 24 | ./tests/type_declaration_skip.go \ 25 | ./tests/members_escaped.go \ 26 | ./tests/members_unescaped.go \ 27 | ./tests/intern.go \ 28 | ./tests/nocopy.go \ 29 | ./tests/escaping.go 30 | bin/tinyjson -all \ 31 | ./tests/data.go \ 32 | ./tests/nothing.go \ 33 | ./tests/errors.go \ 34 | ./tests/html.go \ 35 | ./tests/type_declaration_skip.go 36 | bin/tinyjson \ 37 | ./tests/nested_easy.go \ 38 | ./tests/named_type.go \ 39 | ./tests/custom_map_key_type.go \ 40 | ./tests/embedded_type.go \ 41 | ./tests/reference_to_pointer.go \ 42 | ./tests/key_marshaler_map.go \ 43 | ./tests/unknown_fields.go \ 44 | ./tests/type_declaration.go \ 45 | ./tests/members_escaped.go \ 46 | ./tests/intern.go \ 47 | ./tests/nocopy.go \ 48 | ./tests/escaping.go \ 49 | ./tests/nested_marshaler.go 50 | bin/tinyjson -snake_case ./tests/snake.go 51 | bin/tinyjson -omit_empty ./tests/omitempty.go 52 | bin/tinyjson -build_tags=use_tinyjson -disable_members_unescape ./benchmark/data.go 53 | bin/tinyjson -disallow_unknown_fields ./tests/disallow_unknown.go 54 | bin/tinyjson -disable_members_unescape ./tests/members_unescaped.go 55 | 56 | test: generate 57 | go test \ 58 | ./tests \ 59 | ./jlexer \ 60 | ./gen \ 61 | ./buffer 62 | golint -set_exit_status ./tests/*_tinyjson.go 63 | # TODO: fix benchmarks to not need float 64 | # cd benchmark && go test -benchmem -tags use_tinyjson -bench . 65 | 66 | tiny-generate: build 67 | bin/tinyjson -all -snake_case \ 68 | ./tiny-tests/cosmwasm.go 69 | 70 | tiny-test: tiny-generate 71 | # look into nounsafe later, this uses reflect, so I remove it just in case 72 | go test -v -tags tinyjson_nounsafe ./tiny-tests 73 | @ golint -set_exit_status ./tiny-tests/*_tinyjson.go 74 | @ echo "No files should be listed below:" 75 | @ grep -l encoding/json ./tiny-tests/*.go || true 76 | 77 | tiny-bench: 78 | cd tiny-tests && go test -benchmem -bench . 79 | 80 | bench-other: generate 81 | cd benchmark && make 82 | 83 | bench-python: 84 | benchmark/ujson.sh 85 | 86 | 87 | .PHONY: clean generate test build 88 | -------------------------------------------------------------------------------- /benchmark/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go test -benchmem -bench . 3 | go test -benchmem -tags use_ffjson -bench . 4 | go test -benchmem -tags use_jsoniter -bench . 5 | go test -benchmem -tags use_codec -bench . 6 | -------------------------------------------------------------------------------- /benchmark/codec_test.go: -------------------------------------------------------------------------------- 1 | // +build use_codec 2 | 3 | package benchmark 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/ugorji/go/codec" 9 | ) 10 | 11 | func BenchmarkCodec_Unmarshal_M(b *testing.B) { 12 | var h codec.Handle = new(codec.JsonHandle) 13 | dec := codec.NewDecoderBytes(nil, h) 14 | 15 | b.SetBytes(int64(len(largeStructText))) 16 | for i := 0; i < b.N; i++ { 17 | var s LargeStruct 18 | dec.ResetBytes(largeStructText) 19 | if err := dec.Decode(&s); err != nil { 20 | b.Error(err) 21 | } 22 | } 23 | } 24 | 25 | func BenchmarkCodec_Unmarshal_S(b *testing.B) { 26 | var h codec.Handle = new(codec.JsonHandle) 27 | dec := codec.NewDecoderBytes(nil, h) 28 | 29 | b.SetBytes(int64(len(smallStructText))) 30 | for i := 0; i < b.N; i++ { 31 | var s LargeStruct 32 | dec.ResetBytes(smallStructText) 33 | if err := dec.Decode(&s); err != nil { 34 | b.Error(err) 35 | } 36 | } 37 | } 38 | 39 | func BenchmarkCodec_Marshal_S(b *testing.B) { 40 | var h codec.Handle = new(codec.JsonHandle) 41 | 42 | var out []byte 43 | enc := codec.NewEncoderBytes(&out, h) 44 | 45 | var l int64 46 | for i := 0; i < b.N; i++ { 47 | enc.ResetBytes(&out) 48 | if err := enc.Encode(&smallStructData); err != nil { 49 | b.Error(err) 50 | } 51 | l = int64(len(out)) 52 | out = nil 53 | } 54 | 55 | b.SetBytes(l) 56 | } 57 | 58 | func BenchmarkCodec_Marshal_M(b *testing.B) { 59 | var h codec.Handle = new(codec.JsonHandle) 60 | 61 | var out []byte 62 | enc := codec.NewEncoderBytes(&out, h) 63 | 64 | var l int64 65 | for i := 0; i < b.N; i++ { 66 | enc.ResetBytes(&out) 67 | if err := enc.Encode(&largeStructData); err != nil { 68 | b.Error(err) 69 | } 70 | l = int64(len(out)) 71 | out = nil 72 | } 73 | 74 | b.SetBytes(l) 75 | } 76 | 77 | func BenchmarkCodec_Marshal_L(b *testing.B) { 78 | var h codec.Handle = new(codec.JsonHandle) 79 | 80 | var out []byte 81 | enc := codec.NewEncoderBytes(&out, h) 82 | 83 | var l int64 84 | for i := 0; i < b.N; i++ { 85 | enc.ResetBytes(&out) 86 | if err := enc.Encode(&xlStructData); err != nil { 87 | b.Error(err) 88 | } 89 | l = int64(len(out)) 90 | out = nil 91 | } 92 | 93 | b.SetBytes(l) 94 | } 95 | 96 | func BenchmarkCodec_Marshal_S_Reuse(b *testing.B) { 97 | var h codec.Handle = new(codec.JsonHandle) 98 | 99 | var out []byte 100 | enc := codec.NewEncoderBytes(&out, h) 101 | 102 | var l int64 103 | for i := 0; i < b.N; i++ { 104 | enc.ResetBytes(&out) 105 | if err := enc.Encode(&smallStructData); err != nil { 106 | b.Error(err) 107 | } 108 | l = int64(len(out)) 109 | out = out[:0] 110 | } 111 | 112 | b.SetBytes(l) 113 | } 114 | 115 | func BenchmarkCodec_Marshal_M_Reuse(b *testing.B) { 116 | var h codec.Handle = new(codec.JsonHandle) 117 | 118 | var out []byte 119 | enc := codec.NewEncoderBytes(&out, h) 120 | 121 | var l int64 122 | for i := 0; i < b.N; i++ { 123 | enc.ResetBytes(&out) 124 | if err := enc.Encode(&largeStructData); err != nil { 125 | b.Error(err) 126 | } 127 | l = int64(len(out)) 128 | out = out[:0] 129 | } 130 | 131 | b.SetBytes(l) 132 | } 133 | 134 | func BenchmarkCodec_Marshal_L_Reuse(b *testing.B) { 135 | var h codec.Handle = new(codec.JsonHandle) 136 | 137 | var out []byte 138 | enc := codec.NewEncoderBytes(&out, h) 139 | 140 | var l int64 141 | for i := 0; i < b.N; i++ { 142 | enc.ResetBytes(&out) 143 | if err := enc.Encode(&xlStructData); err != nil { 144 | b.Error(err) 145 | } 146 | l = int64(len(out)) 147 | out = out[:0] 148 | } 149 | 150 | b.SetBytes(l) 151 | } 152 | 153 | func BenchmarkCodec_Marshal_S_Parallel(b *testing.B) { 154 | var l int64 155 | 156 | b.RunParallel(func(pb *testing.PB) { 157 | var out []byte 158 | 159 | var h codec.Handle = new(codec.JsonHandle) 160 | enc := codec.NewEncoderBytes(&out, h) 161 | 162 | for pb.Next() { 163 | enc.ResetBytes(&out) 164 | if err := enc.Encode(&smallStructData); err != nil { 165 | b.Error(err) 166 | } 167 | l = int64(len(out)) 168 | out = nil 169 | } 170 | }) 171 | 172 | b.SetBytes(l) 173 | } 174 | 175 | func BenchmarkCodec_Marshal_M_Parallel(b *testing.B) { 176 | var l int64 177 | 178 | b.RunParallel(func(pb *testing.PB) { 179 | var h codec.Handle = new(codec.JsonHandle) 180 | 181 | var out []byte 182 | enc := codec.NewEncoderBytes(&out, h) 183 | 184 | for pb.Next() { 185 | enc.ResetBytes(&out) 186 | if err := enc.Encode(&largeStructData); err != nil { 187 | b.Error(err) 188 | } 189 | l = int64(len(out)) 190 | out = nil 191 | } 192 | }) 193 | b.SetBytes(l) 194 | } 195 | 196 | func BenchmarkCodec_Marshal_L_Parallel(b *testing.B) { 197 | var l int64 198 | 199 | b.RunParallel(func(pb *testing.PB) { 200 | var h codec.Handle = new(codec.JsonHandle) 201 | 202 | var out []byte 203 | enc := codec.NewEncoderBytes(&out, h) 204 | 205 | for pb.Next() { 206 | enc.ResetBytes(&out) 207 | if err := enc.Encode(&xlStructData); err != nil { 208 | b.Error(err) 209 | } 210 | l = int64(len(out)) 211 | out = nil 212 | } 213 | }) 214 | b.SetBytes(l) 215 | } 216 | 217 | func BenchmarkCodec_Marshal_S_Parallel_Reuse(b *testing.B) { 218 | var l int64 219 | 220 | b.RunParallel(func(pb *testing.PB) { 221 | var out []byte 222 | 223 | var h codec.Handle = new(codec.JsonHandle) 224 | enc := codec.NewEncoderBytes(&out, h) 225 | 226 | for pb.Next() { 227 | enc.ResetBytes(&out) 228 | if err := enc.Encode(&smallStructData); err != nil { 229 | b.Error(err) 230 | } 231 | l = int64(len(out)) 232 | out = out[:0] 233 | } 234 | }) 235 | 236 | b.SetBytes(l) 237 | } 238 | 239 | func BenchmarkCodec_Marshal_M_Parallel_Reuse(b *testing.B) { 240 | var l int64 241 | 242 | b.RunParallel(func(pb *testing.PB) { 243 | var h codec.Handle = new(codec.JsonHandle) 244 | 245 | var out []byte 246 | enc := codec.NewEncoderBytes(&out, h) 247 | 248 | for pb.Next() { 249 | enc.ResetBytes(&out) 250 | if err := enc.Encode(&largeStructData); err != nil { 251 | b.Error(err) 252 | } 253 | l = int64(len(out)) 254 | out = out[:0] 255 | } 256 | }) 257 | b.SetBytes(l) 258 | } 259 | 260 | func BenchmarkCodec_Marshal_L_Parallel_Reuse(b *testing.B) { 261 | var l int64 262 | 263 | b.RunParallel(func(pb *testing.PB) { 264 | var h codec.Handle = new(codec.JsonHandle) 265 | 266 | var out []byte 267 | enc := codec.NewEncoderBytes(&out, h) 268 | 269 | for pb.Next() { 270 | enc.ResetBytes(&out) 271 | if err := enc.Encode(&xlStructData); err != nil { 272 | b.Error(err) 273 | } 274 | l = int64(len(out)) 275 | out = out[:0] 276 | } 277 | }) 278 | b.SetBytes(l) 279 | } 280 | -------------------------------------------------------------------------------- /benchmark/data.go: -------------------------------------------------------------------------------- 1 | // Package benchmark provides a simple benchmark for tinyjson against default serialization and ffjson. 2 | // The data example is taken from https://dev.twitter.com/rest/reference/get/search/tweets 3 | package benchmark 4 | 5 | import ( 6 | "io/ioutil" 7 | ) 8 | 9 | var largeStructText, _ = ioutil.ReadFile("example.json") 10 | var xlStructData XLStruct 11 | 12 | func init() { 13 | for i := 0; i < 50; i++ { 14 | xlStructData.Data = append(xlStructData.Data, largeStructData) 15 | } 16 | } 17 | 18 | var smallStructText = []byte(`{"hashtags":[{"indices":[5, 10],"text":"some-text"}],"urls":[],"user_mentions":[]}`) 19 | var smallStructData = Entities{ 20 | Hashtags: []Hashtag{{Indices: []int{5, 10}, Text: "some-text"}}, 21 | Urls: []*string{}, 22 | UserMentions: []*string{}, 23 | } 24 | 25 | type SearchMetadata struct { 26 | CompletedIn float64 `json:"completed_in"` 27 | Count int `json:"count"` 28 | MaxID int64 `json:"max_id"` 29 | MaxIDStr string `json:"max_id_str"` 30 | NextResults string `json:"next_results"` 31 | Query string `json:"query"` 32 | RefreshURL string `json:"refresh_url"` 33 | SinceID int64 `json:"since_id"` 34 | SinceIDStr string `json:"since_id_str"` 35 | } 36 | 37 | type Hashtag struct { 38 | Indices []int `json:"indices"` 39 | Text string `json:"text"` 40 | } 41 | 42 | //tinyjson:json 43 | type Entities struct { 44 | Hashtags []Hashtag `json:"hashtags"` 45 | Urls []*string `json:"urls"` 46 | UserMentions []*string `json:"user_mentions"` 47 | } 48 | 49 | type UserEntityDescription struct { 50 | Urls []*string `json:"urls"` 51 | } 52 | 53 | type URL struct { 54 | ExpandedURL *string `json:"expanded_url"` 55 | Indices []int `json:"indices"` 56 | URL string `json:"url"` 57 | } 58 | 59 | type UserEntityURL struct { 60 | Urls []URL `json:"urls"` 61 | } 62 | 63 | type UserEntities struct { 64 | Description UserEntityDescription `json:"description"` 65 | URL UserEntityURL `json:"url"` 66 | } 67 | 68 | type User struct { 69 | ContributorsEnabled bool `json:"contributors_enabled"` 70 | CreatedAt string `json:"created_at"` 71 | DefaultProfile bool `json:"default_profile"` 72 | DefaultProfileImage bool `json:"default_profile_image"` 73 | Description string `json:"description"` 74 | Entities UserEntities `json:"entities"` 75 | FavouritesCount int `json:"favourites_count"` 76 | FollowRequestSent *string `json:"follow_request_sent"` 77 | FollowersCount int `json:"followers_count"` 78 | Following *string `json:"following"` 79 | FriendsCount int `json:"friends_count"` 80 | GeoEnabled bool `json:"geo_enabled"` 81 | ID int `json:"id"` 82 | IDStr string `json:"id_str"` 83 | IsTranslator bool `json:"is_translator"` 84 | Lang string `json:"lang"` 85 | ListedCount int `json:"listed_count"` 86 | Location string `json:"location"` 87 | Name string `json:"name"` 88 | Notifications *string `json:"notifications"` 89 | ProfileBackgroundColor string `json:"profile_background_color"` 90 | ProfileBackgroundImageURL string `json:"profile_background_image_url"` 91 | ProfileBackgroundImageURLHTTPS string `json:"profile_background_image_url_https"` 92 | ProfileBackgroundTile bool `json:"profile_background_tile"` 93 | ProfileImageURL string `json:"profile_image_url"` 94 | ProfileImageURLHTTPS string `json:"profile_image_url_https"` 95 | ProfileLinkColor string `json:"profile_link_color"` 96 | ProfileSidebarBorderColor string `json:"profile_sidebar_border_color"` 97 | ProfileSidebarFillColor string `json:"profile_sidebar_fill_color"` 98 | ProfileTextColor string `json:"profile_text_color"` 99 | ProfileUseBackgroundImage bool `json:"profile_use_background_image"` 100 | Protected bool `json:"protected"` 101 | ScreenName string `json:"screen_name"` 102 | ShowAllInlineMedia bool `json:"show_all_inline_media"` 103 | StatusesCount int `json:"statuses_count"` 104 | TimeZone string `json:"time_zone"` 105 | URL *string `json:"url"` 106 | UtcOffset int `json:"utc_offset"` 107 | Verified bool `json:"verified"` 108 | } 109 | 110 | type StatusMetadata struct { 111 | IsoLanguageCode string `json:"iso_language_code"` 112 | ResultType string `json:"result_type"` 113 | } 114 | 115 | type Status struct { 116 | Contributors *string `json:"contributors"` 117 | Coordinates *string `json:"coordinates"` 118 | CreatedAt string `json:"created_at"` 119 | Entities Entities `json:"entities"` 120 | Favorited bool `json:"favorited"` 121 | Geo *string `json:"geo"` 122 | ID int64 `json:"id"` 123 | IDStr string `json:"id_str"` 124 | InReplyToScreenName *string `json:"in_reply_to_screen_name"` 125 | InReplyToStatusID *string `json:"in_reply_to_status_id"` 126 | InReplyToStatusIDStr *string `json:"in_reply_to_status_id_str"` 127 | InReplyToUserID *string `json:"in_reply_to_user_id"` 128 | InReplyToUserIDStr *string `json:"in_reply_to_user_id_str"` 129 | Metadata StatusMetadata `json:"metadata"` 130 | Place *string `json:"place"` 131 | RetweetCount int `json:"retweet_count"` 132 | Retweeted bool `json:"retweeted"` 133 | Source string `json:"source"` 134 | Text string `json:"text"` 135 | Truncated bool `json:"truncated"` 136 | User User `json:"user"` 137 | } 138 | 139 | //tinyjson:json 140 | type LargeStruct struct { 141 | SearchMetadata SearchMetadata `json:"search_metadata"` 142 | Statuses []Status `json:"statuses"` 143 | } 144 | 145 | //tinyjson:json 146 | type XLStruct struct { 147 | Data []LargeStruct 148 | } 149 | -------------------------------------------------------------------------------- /benchmark/data_var.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | var largeStructData = LargeStruct{ 4 | SearchMetadata: SearchMetadata{ 5 | CompletedIn: 0.035, 6 | Count: 4, 7 | MaxID: 250126199840518145, 8 | MaxIDStr: "250126199840518145", 9 | NextResults: "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed", 10 | Query: "%23freebandnames", 11 | RefreshURL: "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1", 12 | SinceID: 24012619984051000, 13 | SinceIDStr: "24012619984051000", 14 | }, 15 | Statuses: []Status{ 16 | { 17 | Contributors: nil, 18 | Coordinates: nil, 19 | CreatedAt: "Mon Sep 24 03:35:21 +0000 2012", 20 | Entities: Entities{ 21 | Hashtags: []Hashtag{{ 22 | Indices: []int{20, 34}, 23 | Text: "freebandnames"}, 24 | }, 25 | Urls: []*string{}, 26 | UserMentions: []*string{}, 27 | }, 28 | Favorited: false, 29 | Geo: nil, 30 | ID: 250075927172759552, 31 | IDStr: "250075927172759552", 32 | InReplyToScreenName: nil, 33 | InReplyToStatusID: nil, 34 | InReplyToStatusIDStr: nil, 35 | InReplyToUserID: nil, 36 | InReplyToUserIDStr: nil, 37 | Metadata: StatusMetadata{ 38 | IsoLanguageCode: "en", 39 | ResultType: "recent", 40 | }, 41 | Place: nil, 42 | RetweetCount: 0, 43 | Retweeted: false, 44 | Source: "Twitter for Mac", 45 | Text: "Aggressive Ponytail #freebandnames", 46 | Truncated: false, 47 | User: User{ 48 | ContributorsEnabled: false, 49 | CreatedAt: "Mon Apr 26 06:01:55 +0000 2010", 50 | DefaultProfile: true, 51 | DefaultProfileImage: false, 52 | Description: "Born 330 Live 310", 53 | Entities: UserEntities{ 54 | Description: UserEntityDescription{ 55 | Urls: []*string{}, 56 | }, 57 | URL: UserEntityURL{ 58 | Urls: []URL{{ 59 | ExpandedURL: nil, 60 | Indices: []int{0, 0}, 61 | URL: "", 62 | }}, 63 | }, 64 | }, 65 | FavouritesCount: 0, 66 | FollowRequestSent: nil, 67 | FollowersCount: 70, 68 | Following: nil, 69 | FriendsCount: 110, 70 | GeoEnabled: true, 71 | ID: 137238150, 72 | IDStr: "137238150", 73 | IsTranslator: false, 74 | Lang: "en", 75 | ListedCount: 2, 76 | Location: "LA, CA", 77 | Name: "Sean Cummings", 78 | Notifications: nil, 79 | ProfileBackgroundColor: "C0DEED", 80 | ProfileBackgroundImageURL: "http://a0.twimg.com/images/themes/theme1/bg.png", 81 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/images/themes/theme1/bg.png", 82 | ProfileBackgroundTile: false, 83 | ProfileImageURL: "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", 84 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", 85 | ProfileLinkColor: "0084B4", 86 | ProfileSidebarBorderColor: "C0DEED", 87 | ProfileSidebarFillColor: "DDEEF6", 88 | ProfileTextColor: "333333", 89 | ProfileUseBackgroundImage: true, 90 | Protected: false, 91 | ScreenName: "sean_cummings", 92 | ShowAllInlineMedia: false, 93 | StatusesCount: 579, 94 | TimeZone: "Pacific Time (US & Canada)", 95 | URL: nil, 96 | UtcOffset: -28800, 97 | Verified: false, 98 | }, 99 | }, 100 | { 101 | Contributors: nil, 102 | Coordinates: nil, 103 | CreatedAt: "Fri Sep 21 23:40:54 +0000 2012", 104 | Entities: Entities{ 105 | Hashtags: []Hashtag{{ 106 | Indices: []int{20, 34}, 107 | Text: "FreeBandNames", 108 | }}, 109 | Urls: []*string{}, 110 | UserMentions: []*string{}, 111 | }, 112 | Favorited: false, 113 | Geo: nil, 114 | ID: 249292149810667520, 115 | IDStr: "249292149810667520", 116 | InReplyToScreenName: nil, 117 | InReplyToStatusID: nil, 118 | InReplyToStatusIDStr: nil, 119 | InReplyToUserID: nil, 120 | InReplyToUserIDStr: nil, 121 | Metadata: StatusMetadata{ 122 | IsoLanguageCode: "pl", 123 | ResultType: "recent", 124 | }, 125 | Place: nil, 126 | RetweetCount: 0, 127 | Retweeted: false, 128 | Source: "web", 129 | Text: "Thee Namaste Nerdz. #FreeBandNames", 130 | Truncated: false, 131 | User: User{ 132 | ContributorsEnabled: false, 133 | CreatedAt: "Tue Apr 07 19:05:07 +0000 2009", 134 | DefaultProfile: false, 135 | DefaultProfileImage: false, 136 | Description: "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.", 137 | Entities: UserEntities{ 138 | Description: UserEntityDescription{Urls: []*string{}}, 139 | URL: UserEntityURL{ 140 | Urls: []URL{{ 141 | ExpandedURL: nil, 142 | Indices: []int{0, 32}, 143 | URL: "http://bullcityrecords.com/wnng/"}}, 144 | }, 145 | }, 146 | FavouritesCount: 8, 147 | FollowRequestSent: nil, 148 | FollowersCount: 2052, 149 | Following: nil, 150 | FriendsCount: 348, 151 | GeoEnabled: false, 152 | ID: 29516238, 153 | IDStr: "29516238", 154 | IsTranslator: false, 155 | Lang: "en", 156 | ListedCount: 118, 157 | Location: "Durham, NC", 158 | Name: "Chaz Martenstein", 159 | Notifications: nil, 160 | ProfileBackgroundColor: "9AE4E8", 161 | ProfileBackgroundImageURL: "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp", 162 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp", 163 | ProfileBackgroundTile: true, 164 | ProfileImageURL: "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg", 165 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg", 166 | ProfileLinkColor: "0084B4", 167 | ProfileSidebarBorderColor: "BDDCAD", 168 | ProfileSidebarFillColor: "DDFFCC", 169 | ProfileTextColor: "333333", 170 | ProfileUseBackgroundImage: true, 171 | Protected: false, 172 | ScreenName: "bullcityrecords", 173 | ShowAllInlineMedia: true, 174 | StatusesCount: 7579, 175 | TimeZone: "Eastern Time (US & Canada)", 176 | URL: nil, 177 | UtcOffset: -18000, 178 | Verified: false, 179 | }, 180 | }, 181 | { 182 | Contributors: nil, 183 | Coordinates: nil, 184 | CreatedAt: "Fri Sep 21 23:30:20 +0000 2012", 185 | Entities: Entities{ 186 | Hashtags: []Hashtag{{ 187 | Indices: []int{29, 43}, 188 | Text: "freebandnames", 189 | }}, 190 | Urls: []*string{}, 191 | UserMentions: []*string{}, 192 | }, 193 | Favorited: false, 194 | Geo: nil, 195 | ID: 249289491129438208, 196 | IDStr: "249289491129438208", 197 | InReplyToScreenName: nil, 198 | InReplyToStatusID: nil, 199 | InReplyToStatusIDStr: nil, 200 | InReplyToUserID: nil, 201 | InReplyToUserIDStr: nil, 202 | Metadata: StatusMetadata{ 203 | IsoLanguageCode: "en", 204 | ResultType: "recent", 205 | }, 206 | Place: nil, 207 | RetweetCount: 0, 208 | Retweeted: false, 209 | Source: "web", 210 | Text: "Mexican Heaven, Mexican Hell #freebandnames", 211 | Truncated: false, 212 | User: User{ 213 | ContributorsEnabled: false, 214 | CreatedAt: "Tue Sep 01 21:21:35 +0000 2009", 215 | DefaultProfile: false, 216 | DefaultProfileImage: false, 217 | Description: "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.", 218 | Entities: UserEntities{ 219 | Description: UserEntityDescription{ 220 | Urls: nil, 221 | }, 222 | URL: UserEntityURL{ 223 | Urls: []URL{{ 224 | ExpandedURL: nil, 225 | Indices: []int{0, 0}, 226 | URL: "", 227 | }}, 228 | }, 229 | }, 230 | FavouritesCount: 19, 231 | FollowRequestSent: nil, 232 | FollowersCount: 63, 233 | Following: nil, 234 | FriendsCount: 63, 235 | GeoEnabled: false, 236 | ID: 70789458, 237 | IDStr: "70789458", 238 | IsTranslator: false, 239 | Lang: "en", 240 | ListedCount: 1, 241 | Location: "Kingston New York", 242 | Name: "Thomas John Wakeman", 243 | Notifications: nil, 244 | ProfileBackgroundColor: "352726", 245 | ProfileBackgroundImageURL: "http://a0.twimg.com/images/themes/theme5/bg.gif", 246 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/images/themes/theme5/bg.gif", 247 | ProfileBackgroundTile: false, 248 | ProfileImageURL: "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png", 249 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png", 250 | ProfileLinkColor: "D02B55", 251 | ProfileSidebarBorderColor: "829D5E", 252 | ProfileSidebarFillColor: "99CC33", 253 | ProfileTextColor: "3E4415", 254 | ProfileUseBackgroundImage: true, 255 | Protected: false, 256 | ScreenName: "MonkiesFist", 257 | ShowAllInlineMedia: false, 258 | StatusesCount: 1048, 259 | TimeZone: "Eastern Time (US & Canada)", 260 | URL: nil, 261 | UtcOffset: -18000, 262 | Verified: false, 263 | }, 264 | }, 265 | { 266 | Contributors: nil, 267 | Coordinates: nil, 268 | CreatedAt: "Fri Sep 21 22:51:18 +0000 2012", 269 | Entities: Entities{ 270 | Hashtags: []Hashtag{{ 271 | Indices: []int{20, 34}, 272 | Text: "freebandnames", 273 | }}, 274 | Urls: []*string{}, 275 | UserMentions: []*string{}, 276 | }, 277 | Favorited: false, 278 | Geo: nil, 279 | ID: 249279667666817024, 280 | IDStr: "249279667666817024", 281 | InReplyToScreenName: nil, 282 | InReplyToStatusID: nil, 283 | InReplyToStatusIDStr: nil, 284 | InReplyToUserID: nil, 285 | InReplyToUserIDStr: nil, 286 | Metadata: StatusMetadata{ 287 | IsoLanguageCode: "en", 288 | ResultType: "recent", 289 | }, 290 | Place: nil, 291 | RetweetCount: 0, 292 | Retweeted: false, 293 | Source: "Twitter for iPhone", 294 | Text: "The Foolish Mortals #freebandnames", 295 | Truncated: false, 296 | User: User{ 297 | ContributorsEnabled: false, 298 | CreatedAt: "Mon May 04 00:05:00 +0000 2009", 299 | DefaultProfile: false, 300 | DefaultProfileImage: false, 301 | Description: "Cartoonist, Illustrator, and T-Shirt connoisseur", 302 | Entities: UserEntities{ 303 | Description: UserEntityDescription{ 304 | Urls: []*string{}, 305 | }, 306 | URL: UserEntityURL{ 307 | Urls: []URL{{ 308 | ExpandedURL: nil, 309 | Indices: []int{0, 24}, 310 | URL: "http://www.omnitarian.me", 311 | }}, 312 | }, 313 | }, 314 | FavouritesCount: 647, 315 | FollowRequestSent: nil, 316 | FollowersCount: 608, 317 | Following: nil, 318 | FriendsCount: 249, 319 | GeoEnabled: false, 320 | ID: 37539828, 321 | IDStr: "37539828", 322 | IsTranslator: false, 323 | Lang: "en", 324 | ListedCount: 52, 325 | Location: "Wisconsin, USA", 326 | Name: "Marty Elmer", 327 | Notifications: nil, 328 | ProfileBackgroundColor: "EEE3C4", 329 | ProfileBackgroundImageURL: "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png", 330 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png", 331 | ProfileBackgroundTile: true, 332 | ProfileImageURL: "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png", 333 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png", 334 | ProfileLinkColor: "3B2A26", 335 | ProfileSidebarBorderColor: "615A44", 336 | ProfileSidebarFillColor: "BFAC83", 337 | ProfileTextColor: "000000", 338 | ProfileUseBackgroundImage: true, 339 | Protected: false, 340 | ScreenName: "Omnitarian", 341 | ShowAllInlineMedia: true, 342 | StatusesCount: 3575, 343 | TimeZone: "Central Time (US & Canada)", 344 | URL: nil, 345 | UtcOffset: -21600, 346 | Verified: false, 347 | }, 348 | }, 349 | }, 350 | } 351 | -------------------------------------------------------------------------------- /benchmark/default_test.go: -------------------------------------------------------------------------------- 1 | // +build !use_tinyjson,!use_ffjson,!use_codec,!use_jsoniter 2 | 3 | package benchmark 4 | 5 | import ( 6 | "encoding/json" 7 | "testing" 8 | ) 9 | 10 | func BenchmarkStd_Unmarshal_M(b *testing.B) { 11 | b.SetBytes(int64(len(largeStructText))) 12 | for i := 0; i < b.N; i++ { 13 | var s LargeStruct 14 | err := json.Unmarshal(largeStructText, &s) 15 | if err != nil { 16 | b.Error(err) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkStd_Unmarshal_S(b *testing.B) { 22 | for i := 0; i < b.N; i++ { 23 | var s Entities 24 | err := json.Unmarshal(smallStructText, &s) 25 | if err != nil { 26 | b.Error(err) 27 | } 28 | } 29 | b.SetBytes(int64(len(smallStructText))) 30 | } 31 | 32 | func BenchmarkStd_Marshal_M(b *testing.B) { 33 | var l int64 34 | for i := 0; i < b.N; i++ { 35 | data, err := json.Marshal(&largeStructData) 36 | if err != nil { 37 | b.Error(err) 38 | } 39 | l = int64(len(data)) 40 | } 41 | b.SetBytes(l) 42 | } 43 | 44 | func BenchmarkStd_Marshal_L(b *testing.B) { 45 | var l int64 46 | for i := 0; i < b.N; i++ { 47 | data, err := json.Marshal(&xlStructData) 48 | if err != nil { 49 | b.Error(err) 50 | } 51 | l = int64(len(data)) 52 | } 53 | b.SetBytes(l) 54 | } 55 | 56 | func BenchmarkStd_Marshal_M_Parallel(b *testing.B) { 57 | var l int64 58 | b.RunParallel(func(pb *testing.PB) { 59 | for pb.Next() { 60 | data, err := json.Marshal(&largeStructData) 61 | if err != nil { 62 | b.Error(err) 63 | } 64 | l = int64(len(data)) 65 | } 66 | }) 67 | b.SetBytes(l) 68 | } 69 | 70 | func BenchmarkStd_Marshal_L_Parallel(b *testing.B) { 71 | var l int64 72 | b.RunParallel(func(pb *testing.PB) { 73 | for pb.Next() { 74 | data, err := json.Marshal(&xlStructData) 75 | if err != nil { 76 | b.Error(err) 77 | } 78 | l = int64(len(data)) 79 | } 80 | }) 81 | b.SetBytes(l) 82 | } 83 | 84 | func BenchmarkStd_Marshal_S(b *testing.B) { 85 | var l int64 86 | for i := 0; i < b.N; i++ { 87 | data, err := json.Marshal(&smallStructData) 88 | if err != nil { 89 | b.Error(err) 90 | } 91 | l = int64(len(data)) 92 | } 93 | b.SetBytes(l) 94 | } 95 | 96 | func BenchmarkStd_Marshal_S_Parallel(b *testing.B) { 97 | var l int64 98 | b.RunParallel(func(pb *testing.PB) { 99 | for pb.Next() { 100 | data, err := json.Marshal(&smallStructData) 101 | if err != nil { 102 | b.Error(err) 103 | } 104 | l = int64(len(data)) 105 | } 106 | }) 107 | b.SetBytes(l) 108 | } 109 | 110 | func BenchmarkStd_Marshal_M_ToWriter(b *testing.B) { 111 | enc := json.NewEncoder(&DummyWriter{}) 112 | for i := 0; i < b.N; i++ { 113 | err := enc.Encode(&largeStructData) 114 | if err != nil { 115 | b.Error(err) 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /benchmark/dummy_test.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type DummyWriter struct{} 8 | 9 | func (w DummyWriter) Write(data []byte) (int, error) { return len(data), nil } 10 | 11 | func TestToSuppressNoTestsWarning(t *testing.T) {} 12 | -------------------------------------------------------------------------------- /benchmark/easyjson_test.go: -------------------------------------------------------------------------------- 1 | // +build use_tinyjson 2 | 3 | package benchmark 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/CosmWasm/tinyjson" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | func BenchmarkEJ_Unmarshal_M(b *testing.B) { 13 | b.SetBytes(int64(len(largeStructText))) 14 | for i := 0; i < b.N; i++ { 15 | var s LargeStruct 16 | err := s.UnmarshalJSON(largeStructText) 17 | if err != nil { 18 | b.Error(err) 19 | } 20 | } 21 | } 22 | 23 | func BenchmarkEJ_Unmarshal_S(b *testing.B) { 24 | b.SetBytes(int64(len(smallStructText))) 25 | 26 | for i := 0; i < b.N; i++ { 27 | var s Entities 28 | err := s.UnmarshalJSON(smallStructText) 29 | if err != nil { 30 | b.Error(err) 31 | } 32 | } 33 | } 34 | 35 | func BenchmarkEJ_Marshal_M(b *testing.B) { 36 | var l int64 37 | for i := 0; i < b.N; i++ { 38 | data, err := tinyjson.Marshal(&largeStructData) 39 | if err != nil { 40 | b.Error(err) 41 | } 42 | l = int64(len(data)) 43 | } 44 | b.SetBytes(l) 45 | } 46 | 47 | func BenchmarkEJ_Marshal_L(b *testing.B) { 48 | var l int64 49 | for i := 0; i < b.N; i++ { 50 | data, err := tinyjson.Marshal(&xlStructData) 51 | if err != nil { 52 | b.Error(err) 53 | } 54 | l = int64(len(data)) 55 | } 56 | b.SetBytes(l) 57 | } 58 | 59 | func BenchmarkEJ_Marshal_L_ToWriter(b *testing.B) { 60 | var l int64 61 | out := &DummyWriter{} 62 | for i := 0; i < b.N; i++ { 63 | w := jwriter.Writer{} 64 | xlStructData.MarshalTinyJSON(&w) 65 | if w.Error != nil { 66 | b.Error(w.Error) 67 | } 68 | 69 | l = int64(w.Size()) 70 | w.DumpTo(out) 71 | } 72 | b.SetBytes(l) 73 | 74 | } 75 | func BenchmarkEJ_Marshal_M_Parallel(b *testing.B) { 76 | b.SetBytes(int64(len(largeStructText))) 77 | 78 | b.RunParallel(func(pb *testing.PB) { 79 | for pb.Next() { 80 | _, err := largeStructData.MarshalJSON() 81 | if err != nil { 82 | b.Error(err) 83 | } 84 | } 85 | }) 86 | } 87 | 88 | func BenchmarkEJ_Marshal_M_ToWriter(b *testing.B) { 89 | var l int64 90 | out := &DummyWriter{} 91 | for i := 0; i < b.N; i++ { 92 | w := jwriter.Writer{} 93 | largeStructData.MarshalTinyJSON(&w) 94 | if w.Error != nil { 95 | b.Error(w.Error) 96 | } 97 | 98 | l = int64(w.Size()) 99 | w.DumpTo(out) 100 | } 101 | b.SetBytes(l) 102 | 103 | } 104 | func BenchmarkEJ_Marshal_M_ToWriter_Parallel(b *testing.B) { 105 | out := &DummyWriter{} 106 | 107 | b.RunParallel(func(pb *testing.PB) { 108 | var l int64 109 | for pb.Next() { 110 | w := jwriter.Writer{} 111 | largeStructData.MarshalTinyJSON(&w) 112 | if w.Error != nil { 113 | b.Error(w.Error) 114 | } 115 | 116 | l = int64(w.Size()) 117 | w.DumpTo(out) 118 | } 119 | if l > 0 { 120 | b.SetBytes(l) 121 | } 122 | }) 123 | 124 | } 125 | 126 | func BenchmarkEJ_Marshal_L_Parallel(b *testing.B) { 127 | var l int64 128 | b.RunParallel(func(pb *testing.PB) { 129 | for pb.Next() { 130 | data, err := xlStructData.MarshalJSON() 131 | if err != nil { 132 | b.Error(err) 133 | } 134 | l = int64(len(data)) 135 | } 136 | }) 137 | b.SetBytes(l) 138 | } 139 | 140 | func BenchmarkEJ_Marshal_L_ToWriter_Parallel(b *testing.B) { 141 | out := &DummyWriter{} 142 | b.RunParallel(func(pb *testing.PB) { 143 | var l int64 144 | for pb.Next() { 145 | w := jwriter.Writer{} 146 | 147 | xlStructData.MarshalTinyJSON(&w) 148 | if w.Error != nil { 149 | b.Error(w.Error) 150 | } 151 | l = int64(w.Size()) 152 | w.DumpTo(out) 153 | } 154 | if l > 0 { 155 | b.SetBytes(l) 156 | } 157 | }) 158 | } 159 | 160 | func BenchmarkEJ_Marshal_S(b *testing.B) { 161 | var l int64 162 | for i := 0; i < b.N; i++ { 163 | data, err := smallStructData.MarshalJSON() 164 | if err != nil { 165 | b.Error(err) 166 | } 167 | l = int64(len(data)) 168 | } 169 | b.SetBytes(l) 170 | } 171 | 172 | func BenchmarkEJ_Marshal_S_Parallel(b *testing.B) { 173 | var l int64 174 | b.RunParallel(func(pb *testing.PB) { 175 | for pb.Next() { 176 | data, err := smallStructData.MarshalJSON() 177 | if err != nil { 178 | b.Error(err) 179 | } 180 | l = int64(len(data)) 181 | } 182 | }) 183 | b.SetBytes(l) 184 | } 185 | -------------------------------------------------------------------------------- /benchmark/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "statuses": [ 3 | { 4 | "coordinates": null, 5 | "favorited": false, 6 | "truncated": false, 7 | "created_at": "Mon Sep 24 03:35:21 +0000 2012", 8 | "id_str": "250075927172759552", 9 | "entities": { 10 | "urls": [ 11 | 12 | ], 13 | "hashtags": [ 14 | { 15 | "text": "freebandnames", 16 | "indices": [ 17 | 20, 18 | 34 19 | ] 20 | } 21 | ], 22 | "user_mentions": [ 23 | 24 | ] 25 | }, 26 | "in_reply_to_user_id_str": null, 27 | "contributors": null, 28 | "text": "Aggressive Ponytail #freebandnames", 29 | "metadata": { 30 | "iso_language_code": "en", 31 | "result_type": "recent" 32 | }, 33 | "retweet_count": 0, 34 | "in_reply_to_status_id_str": null, 35 | "id": 250075927172759552, 36 | "geo": null, 37 | "retweeted": false, 38 | "in_reply_to_user_id": null, 39 | "place": null, 40 | "user": { 41 | "profile_sidebar_fill_color": "DDEEF6", 42 | "profile_sidebar_border_color": "C0DEED", 43 | "profile_background_tile": false, 44 | "name": "Sean Cummings", 45 | "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", 46 | "created_at": "Mon Apr 26 06:01:55 +0000 2010", 47 | "location": "LA, CA", 48 | "follow_request_sent": null, 49 | "profile_link_color": "0084B4", 50 | "is_translator": false, 51 | "id_str": "137238150", 52 | "entities": { 53 | "url": { 54 | "urls": [ 55 | { 56 | "expanded_url": null, 57 | "url": "", 58 | "indices": [ 59 | 0, 60 | 0 61 | ] 62 | } 63 | ] 64 | }, 65 | "description": { 66 | "urls": [ 67 | 68 | ] 69 | } 70 | }, 71 | "default_profile": true, 72 | "contributors_enabled": false, 73 | "favourites_count": 0, 74 | "url": null, 75 | "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg", 76 | "utc_offset": -28800, 77 | "id": 137238150, 78 | "profile_use_background_image": true, 79 | "listed_count": 2, 80 | "profile_text_color": "333333", 81 | "lang": "en", 82 | "followers_count": 70, 83 | "protected": false, 84 | "notifications": null, 85 | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png", 86 | "profile_background_color": "C0DEED", 87 | "verified": false, 88 | "geo_enabled": true, 89 | "time_zone": "Pacific Time (US & Canada)", 90 | "description": "Born 330 Live 310", 91 | "default_profile_image": false, 92 | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png", 93 | "statuses_count": 579, 94 | "friends_count": 110, 95 | "following": null, 96 | "show_all_inline_media": false, 97 | "screen_name": "sean_cummings" 98 | }, 99 | "in_reply_to_screen_name": null, 100 | "source": "Twitter for Mac", 101 | "in_reply_to_status_id": null 102 | }, 103 | { 104 | "coordinates": null, 105 | "favorited": false, 106 | "truncated": false, 107 | "created_at": "Fri Sep 21 23:40:54 +0000 2012", 108 | "id_str": "249292149810667520", 109 | "entities": { 110 | "urls": [ 111 | 112 | ], 113 | "hashtags": [ 114 | { 115 | "text": "FreeBandNames", 116 | "indices": [ 117 | 20, 118 | 34 119 | ] 120 | } 121 | ], 122 | "user_mentions": [ 123 | 124 | ] 125 | }, 126 | "in_reply_to_user_id_str": null, 127 | "contributors": null, 128 | "text": "Thee Namaste Nerdz. #FreeBandNames", 129 | "metadata": { 130 | "iso_language_code": "pl", 131 | "result_type": "recent" 132 | }, 133 | "retweet_count": 0, 134 | "in_reply_to_status_id_str": null, 135 | "id": 249292149810667520, 136 | "geo": null, 137 | "retweeted": false, 138 | "in_reply_to_user_id": null, 139 | "place": null, 140 | "user": { 141 | "profile_sidebar_fill_color": "DDFFCC", 142 | "profile_sidebar_border_color": "BDDCAD", 143 | "profile_background_tile": true, 144 | "name": "Chaz Martenstein", 145 | "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg", 146 | "created_at": "Tue Apr 07 19:05:07 +0000 2009", 147 | "location": "Durham, NC", 148 | "follow_request_sent": null, 149 | "profile_link_color": "0084B4", 150 | "is_translator": false, 151 | "id_str": "29516238", 152 | "entities": { 153 | "url": { 154 | "urls": [ 155 | { 156 | "expanded_url": null, 157 | "url": "http://bullcityrecords.com/wnng/", 158 | "indices": [ 159 | 0, 160 | 32 161 | ] 162 | } 163 | ] 164 | }, 165 | "description": { 166 | "urls": [ 167 | 168 | ] 169 | } 170 | }, 171 | "default_profile": false, 172 | "contributors_enabled": false, 173 | "favourites_count": 8, 174 | "url": "http://bullcityrecords.com/wnng/", 175 | "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg", 176 | "utc_offset": -18000, 177 | "id": 29516238, 178 | "profile_use_background_image": true, 179 | "listed_count": 118, 180 | "profile_text_color": "333333", 181 | "lang": "en", 182 | "followers_count": 2052, 183 | "protected": false, 184 | "notifications": null, 185 | "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp", 186 | "profile_background_color": "9AE4E8", 187 | "verified": false, 188 | "geo_enabled": false, 189 | "time_zone": "Eastern Time (US & Canada)", 190 | "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.", 191 | "default_profile_image": false, 192 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp", 193 | "statuses_count": 7579, 194 | "friends_count": 348, 195 | "following": null, 196 | "show_all_inline_media": true, 197 | "screen_name": "bullcityrecords" 198 | }, 199 | "in_reply_to_screen_name": null, 200 | "source": "web", 201 | "in_reply_to_status_id": null 202 | }, 203 | { 204 | "coordinates": null, 205 | "favorited": false, 206 | "truncated": false, 207 | "created_at": "Fri Sep 21 23:30:20 +0000 2012", 208 | "id_str": "249289491129438208", 209 | "entities": { 210 | "urls": [ 211 | 212 | ], 213 | "hashtags": [ 214 | { 215 | "text": "freebandnames", 216 | "indices": [ 217 | 29, 218 | 43 219 | ] 220 | } 221 | ], 222 | "user_mentions": [ 223 | 224 | ] 225 | }, 226 | "in_reply_to_user_id_str": null, 227 | "contributors": null, 228 | "text": "Mexican Heaven, Mexican Hell #freebandnames", 229 | "metadata": { 230 | "iso_language_code": "en", 231 | "result_type": "recent" 232 | }, 233 | "retweet_count": 0, 234 | "in_reply_to_status_id_str": null, 235 | "id": 249289491129438208, 236 | "geo": null, 237 | "retweeted": false, 238 | "in_reply_to_user_id": null, 239 | "place": null, 240 | "user": { 241 | "profile_sidebar_fill_color": "99CC33", 242 | "profile_sidebar_border_color": "829D5E", 243 | "profile_background_tile": false, 244 | "name": "Thomas John Wakeman", 245 | "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png", 246 | "created_at": "Tue Sep 01 21:21:35 +0000 2009", 247 | "location": "Kingston New York", 248 | "follow_request_sent": null, 249 | "profile_link_color": "D02B55", 250 | "is_translator": false, 251 | "id_str": "70789458", 252 | "entities": { 253 | "url": { 254 | "urls": [ 255 | { 256 | "expanded_url": null, 257 | "url": "", 258 | "indices": [ 259 | 0, 260 | 0 261 | ] 262 | } 263 | ] 264 | }, 265 | "description": { 266 | "urls": [ 267 | 268 | ] 269 | } 270 | }, 271 | "default_profile": false, 272 | "contributors_enabled": false, 273 | "favourites_count": 19, 274 | "url": null, 275 | "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png", 276 | "utc_offset": -18000, 277 | "id": 70789458, 278 | "profile_use_background_image": true, 279 | "listed_count": 1, 280 | "profile_text_color": "3E4415", 281 | "lang": "en", 282 | "followers_count": 63, 283 | "protected": false, 284 | "notifications": null, 285 | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif", 286 | "profile_background_color": "352726", 287 | "verified": false, 288 | "geo_enabled": false, 289 | "time_zone": "Eastern Time (US & Canada)", 290 | "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.", 291 | "default_profile_image": false, 292 | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif", 293 | "statuses_count": 1048, 294 | "friends_count": 63, 295 | "following": null, 296 | "show_all_inline_media": false, 297 | "screen_name": "MonkiesFist" 298 | }, 299 | "in_reply_to_screen_name": null, 300 | "source": "web", 301 | "in_reply_to_status_id": null 302 | }, 303 | { 304 | "coordinates": null, 305 | "favorited": false, 306 | "truncated": false, 307 | "created_at": "Fri Sep 21 22:51:18 +0000 2012", 308 | "id_str": "249279667666817024", 309 | "entities": { 310 | "urls": [ 311 | 312 | ], 313 | "hashtags": [ 314 | { 315 | "text": "freebandnames", 316 | "indices": [ 317 | 20, 318 | 34 319 | ] 320 | } 321 | ], 322 | "user_mentions": [ 323 | 324 | ] 325 | }, 326 | "in_reply_to_user_id_str": null, 327 | "contributors": null, 328 | "text": "The Foolish Mortals #freebandnames", 329 | "metadata": { 330 | "iso_language_code": "en", 331 | "result_type": "recent" 332 | }, 333 | "retweet_count": 0, 334 | "in_reply_to_status_id_str": null, 335 | "id": 249279667666817024, 336 | "geo": null, 337 | "retweeted": false, 338 | "in_reply_to_user_id": null, 339 | "place": null, 340 | "user": { 341 | "profile_sidebar_fill_color": "BFAC83", 342 | "profile_sidebar_border_color": "615A44", 343 | "profile_background_tile": true, 344 | "name": "Marty Elmer", 345 | "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png", 346 | "created_at": "Mon May 04 00:05:00 +0000 2009", 347 | "location": "Wisconsin, USA", 348 | "follow_request_sent": null, 349 | "profile_link_color": "3B2A26", 350 | "is_translator": false, 351 | "id_str": "37539828", 352 | "entities": { 353 | "url": { 354 | "urls": [ 355 | { 356 | "expanded_url": null, 357 | "url": "http://www.omnitarian.me", 358 | "indices": [ 359 | 0, 360 | 24 361 | ] 362 | } 363 | ] 364 | }, 365 | "description": { 366 | "urls": [ 367 | 368 | ] 369 | } 370 | }, 371 | "default_profile": false, 372 | "contributors_enabled": false, 373 | "favourites_count": 647, 374 | "url": "http://www.omnitarian.me", 375 | "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png", 376 | "utc_offset": -21600, 377 | "id": 37539828, 378 | "profile_use_background_image": true, 379 | "listed_count": 52, 380 | "profile_text_color": "000000", 381 | "lang": "en", 382 | "followers_count": 608, 383 | "protected": false, 384 | "notifications": null, 385 | "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png", 386 | "profile_background_color": "EEE3C4", 387 | "verified": false, 388 | "geo_enabled": false, 389 | "time_zone": "Central Time (US & Canada)", 390 | "description": "Cartoonist, Illustrator, and T-Shirt connoisseur", 391 | "default_profile_image": false, 392 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png", 393 | "statuses_count": 3575, 394 | "friends_count": 249, 395 | "following": null, 396 | "show_all_inline_media": true, 397 | "screen_name": "Omnitarian" 398 | }, 399 | "in_reply_to_screen_name": null, 400 | "source": "Twitter for iPhone", 401 | "in_reply_to_status_id": null 402 | } 403 | ], 404 | "search_metadata": { 405 | "max_id": 250126199840518145, 406 | "since_id": 24012619984051000, 407 | "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1", 408 | "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed", 409 | "count": 4, 410 | "completed_in": 0.035, 411 | "since_id_str": "24012619984051000", 412 | "query": "%23freebandnames", 413 | "max_id_str": "250126199840518145" 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /benchmark/ffjson_test.go: -------------------------------------------------------------------------------- 1 | // +build use_ffjson 2 | 3 | package benchmark 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/pquerna/ffjson/ffjson" 9 | ) 10 | 11 | func BenchmarkFF_Unmarshal_M(b *testing.B) { 12 | b.SetBytes(int64(len(largeStructText))) 13 | for i := 0; i < b.N; i++ { 14 | var s LargeStruct 15 | err := ffjson.UnmarshalFast(largeStructText, &s) 16 | if err != nil { 17 | b.Error(err) 18 | } 19 | } 20 | } 21 | 22 | func BenchmarkFF_Unmarshal_S(b *testing.B) { 23 | for i := 0; i < b.N; i++ { 24 | var s Entities 25 | err := ffjson.UnmarshalFast(smallStructText, &s) 26 | if err != nil { 27 | b.Error(err) 28 | } 29 | } 30 | b.SetBytes(int64(len(smallStructText))) 31 | } 32 | 33 | func BenchmarkFF_Marshal_M(b *testing.B) { 34 | var l int64 35 | for i := 0; i < b.N; i++ { 36 | data, err := ffjson.MarshalFast(&largeStructData) 37 | if err != nil { 38 | b.Error(err) 39 | } 40 | l = int64(len(data)) 41 | } 42 | b.SetBytes(l) 43 | } 44 | 45 | func BenchmarkFF_Marshal_S(b *testing.B) { 46 | var l int64 47 | for i := 0; i < b.N; i++ { 48 | data, err := ffjson.MarshalFast(&smallStructData) 49 | if err != nil { 50 | b.Error(err) 51 | } 52 | l = int64(len(data)) 53 | } 54 | b.SetBytes(l) 55 | } 56 | 57 | func BenchmarkFF_Marshal_M_Pool(b *testing.B) { 58 | var l int64 59 | for i := 0; i < b.N; i++ { 60 | data, err := ffjson.MarshalFast(&largeStructData) 61 | if err != nil { 62 | b.Error(err) 63 | } 64 | l = int64(len(data)) 65 | ffjson.Pool(data) 66 | } 67 | b.SetBytes(l) 68 | } 69 | 70 | func BenchmarkFF_Marshal_L(b *testing.B) { 71 | var l int64 72 | for i := 0; i < b.N; i++ { 73 | data, err := ffjson.MarshalFast(&xlStructData) 74 | if err != nil { 75 | b.Error(err) 76 | } 77 | l = int64(len(data)) 78 | } 79 | b.SetBytes(l) 80 | } 81 | 82 | func BenchmarkFF_Marshal_L_Pool(b *testing.B) { 83 | var l int64 84 | for i := 0; i < b.N; i++ { 85 | data, err := ffjson.MarshalFast(&xlStructData) 86 | if err != nil { 87 | b.Error(err) 88 | } 89 | l = int64(len(data)) 90 | ffjson.Pool(data) 91 | } 92 | b.SetBytes(l) 93 | } 94 | 95 | func BenchmarkFF_Marshal_L_Pool_Parallel(b *testing.B) { 96 | var l int64 97 | for i := 0; i < b.N; i++ { 98 | data, err := ffjson.MarshalFast(&xlStructData) 99 | if err != nil { 100 | b.Error(err) 101 | } 102 | l = int64(len(data)) 103 | ffjson.Pool(data) 104 | } 105 | b.SetBytes(l) 106 | } 107 | func BenchmarkFF_Marshal_M_Pool_Parallel(b *testing.B) { 108 | var l int64 109 | b.RunParallel(func(pb *testing.PB) { 110 | for pb.Next() { 111 | data, err := ffjson.MarshalFast(&largeStructData) 112 | if err != nil { 113 | b.Error(err) 114 | } 115 | l = int64(len(data)) 116 | ffjson.Pool(data) 117 | } 118 | }) 119 | b.SetBytes(l) 120 | } 121 | 122 | func BenchmarkFF_Marshal_S_Pool(b *testing.B) { 123 | var l int64 124 | for i := 0; i < b.N; i++ { 125 | data, err := ffjson.MarshalFast(&smallStructData) 126 | if err != nil { 127 | b.Error(err) 128 | } 129 | l = int64(len(data)) 130 | ffjson.Pool(data) 131 | } 132 | b.SetBytes(l) 133 | } 134 | 135 | func BenchmarkFF_Marshal_S_Pool_Parallel(b *testing.B) { 136 | var l int64 137 | b.RunParallel(func(pb *testing.PB) { 138 | for pb.Next() { 139 | data, err := ffjson.MarshalFast(&smallStructData) 140 | if err != nil { 141 | b.Error(err) 142 | } 143 | l = int64(len(data)) 144 | ffjson.Pool(data) 145 | } 146 | }) 147 | b.SetBytes(l) 148 | } 149 | 150 | func BenchmarkFF_Marshal_S_Parallel(b *testing.B) { 151 | var l int64 152 | b.RunParallel(func(pb *testing.PB) { 153 | for pb.Next() { 154 | data, err := ffjson.MarshalFast(&smallStructData) 155 | if err != nil { 156 | b.Error(err) 157 | } 158 | l = int64(len(data)) 159 | } 160 | }) 161 | b.SetBytes(l) 162 | } 163 | 164 | func BenchmarkFF_Marshal_M_Parallel(b *testing.B) { 165 | var l int64 166 | b.RunParallel(func(pb *testing.PB) { 167 | for pb.Next() { 168 | data, err := ffjson.MarshalFast(&largeStructData) 169 | if err != nil { 170 | b.Error(err) 171 | } 172 | l = int64(len(data)) 173 | } 174 | }) 175 | b.SetBytes(l) 176 | } 177 | 178 | func BenchmarkFF_Marshal_L_Parallel(b *testing.B) { 179 | var l int64 180 | b.RunParallel(func(pb *testing.PB) { 181 | for pb.Next() { 182 | data, err := ffjson.MarshalFast(&xlStructData) 183 | if err != nil { 184 | b.Error(err) 185 | } 186 | l = int64(len(data)) 187 | } 188 | }) 189 | b.SetBytes(l) 190 | } 191 | -------------------------------------------------------------------------------- /benchmark/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/CosmWasm/tinyjson/benchmark 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/json-iterator/go v1.1.7 7 | github.com/CosmWasm/tinyjson v0.0.0 8 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9 9 | github.com/ugorji/go/codec v1.1.7 10 | github.com/ugorji/go/codec/codecgen v1.1.7 11 | ) 12 | 13 | replace github.com/CosmWasm/tinyjson => ../ 14 | -------------------------------------------------------------------------------- /benchmark/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 5 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 6 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 7 | github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= 8 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 9 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 10 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 11 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 12 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9 h1:kyf9snWXHvQc+yxE9imhdI8YAm4oKeZISlaAR+x73zs= 16 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 19 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 20 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 21 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 22 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 23 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 24 | github.com/ugorji/go/codec/codecgen v1.1.7 h1:6BU4y9NIgvVMNetSGxkH9lOOa2UB5b8sCHC6+8m5lVc= 25 | github.com/ugorji/go/codec/codecgen v1.1.7/go.mod h1:FMgcDIjFRtjQPUVM3GN592ic8epl2qsvfNPhezMgP8Y= 26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 27 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 28 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 29 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 30 | golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d h1:yqT69RdmShXXRtsT9jS6Iy0FFLWGLCe3IqGE0vsP0m4= 31 | golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 32 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 33 | -------------------------------------------------------------------------------- /benchmark/jsoniter_test.go: -------------------------------------------------------------------------------- 1 | // +build use_jsoniter 2 | 3 | package benchmark 4 | 5 | import ( 6 | "testing" 7 | 8 | jsoniter "github.com/json-iterator/go" 9 | ) 10 | 11 | func BenchmarkJI_Unmarshal_M(b *testing.B) { 12 | b.SetBytes(int64(len(largeStructText))) 13 | for i := 0; i < b.N; i++ { 14 | var s LargeStruct 15 | err := jsoniter.Unmarshal(largeStructText, &s) 16 | if err != nil { 17 | b.Error(err) 18 | } 19 | } 20 | } 21 | 22 | func BenchmarkJI_Unmarshal_S(b *testing.B) { 23 | for i := 0; i < b.N; i++ { 24 | var s Entities 25 | err := jsoniter.Unmarshal(smallStructText, &s) 26 | if err != nil { 27 | b.Error(err) 28 | } 29 | } 30 | b.SetBytes(int64(len(smallStructText))) 31 | } 32 | 33 | func BenchmarkJI_Marshal_M(b *testing.B) { 34 | var l int64 35 | for i := 0; i < b.N; i++ { 36 | data, err := jsoniter.Marshal(&largeStructData) 37 | if err != nil { 38 | b.Error(err) 39 | } 40 | l = int64(len(data)) 41 | } 42 | b.SetBytes(l) 43 | } 44 | 45 | func BenchmarkJI_Marshal_L(b *testing.B) { 46 | var l int64 47 | for i := 0; i < b.N; i++ { 48 | data, err := jsoniter.Marshal(&xlStructData) 49 | if err != nil { 50 | b.Error(err) 51 | } 52 | l = int64(len(data)) 53 | } 54 | b.SetBytes(l) 55 | } 56 | 57 | func BenchmarkJI_Marshal_M_Parallel(b *testing.B) { 58 | var l int64 59 | b.RunParallel(func(pb *testing.PB) { 60 | for pb.Next() { 61 | data, err := jsoniter.Marshal(&largeStructData) 62 | if err != nil { 63 | b.Error(err) 64 | } 65 | l = int64(len(data)) 66 | } 67 | }) 68 | b.SetBytes(l) 69 | } 70 | 71 | func BenchmarkJI_Marshal_L_Parallel(b *testing.B) { 72 | var l int64 73 | b.RunParallel(func(pb *testing.PB) { 74 | for pb.Next() { 75 | data, err := jsoniter.Marshal(&xlStructData) 76 | if err != nil { 77 | b.Error(err) 78 | } 79 | l = int64(len(data)) 80 | } 81 | }) 82 | b.SetBytes(l) 83 | } 84 | 85 | func BenchmarkJI_Marshal_S(b *testing.B) { 86 | var l int64 87 | for i := 0; i < b.N; i++ { 88 | data, err := jsoniter.Marshal(&smallStructData) 89 | if err != nil { 90 | b.Error(err) 91 | } 92 | l = int64(len(data)) 93 | } 94 | b.SetBytes(l) 95 | } 96 | 97 | func BenchmarkJI_Marshal_S_Parallel(b *testing.B) { 98 | var l int64 99 | b.RunParallel(func(pb *testing.PB) { 100 | for pb.Next() { 101 | data, err := jsoniter.Marshal(&smallStructData) 102 | if err != nil { 103 | b.Error(err) 104 | } 105 | l = int64(len(data)) 106 | } 107 | }) 108 | b.SetBytes(l) 109 | } 110 | 111 | func BenchmarkJI_Marshal_M_ToWriter(b *testing.B) { 112 | enc := jsoniter.NewEncoder(&DummyWriter{}) 113 | for i := 0; i < b.N; i++ { 114 | err := enc.Encode(&largeStructData) 115 | if err != nil { 116 | b.Error(err) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /benchmark/tools.go: -------------------------------------------------------------------------------- 1 | //+build tools 2 | 3 | // Package tools tracks dependencies on binaries not otherwise referenced in the codebase. 4 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 5 | package tools 6 | 7 | import ( 8 | _ "github.com/ugorji/go/codec/codecgen" 9 | ) 10 | -------------------------------------------------------------------------------- /benchmark/ujson.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | echo -n "Python ujson module, DECODE: " 4 | python -m timeit -s "import ujson; data = open('`dirname $0`/example.json', 'r').read()" 'ujson.loads(data)' 5 | 6 | echo -n "Python ujson module, ENCODE: " 7 | python -m timeit -s "import ujson; data = open('`dirname $0`/example.json', 'r').read(); obj = ujson.loads(data)" 'ujson.dumps(obj)' 8 | -------------------------------------------------------------------------------- /bootstrap/bootstrap.go: -------------------------------------------------------------------------------- 1 | // Package bootstrap implements the bootstrapping logic: generation of a .go file to 2 | // launch the actual generator and launching the generator itself. 3 | // 4 | // The package may be preferred to a command-line utility if generating the serializers 5 | // from golang code is required. 6 | package bootstrap 7 | 8 | import ( 9 | "fmt" 10 | "go/format" 11 | "io/ioutil" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "regexp" 16 | "sort" 17 | ) 18 | 19 | const genPackage = "github.com/CosmWasm/tinyjson/gen" 20 | const pkgWriter = "github.com/CosmWasm/tinyjson/jwriter" 21 | const pkgLexer = "github.com/CosmWasm/tinyjson/jlexer" 22 | 23 | var buildFlagsRegexp = regexp.MustCompile("'.+'|\".+\"|\\S+") 24 | 25 | type Generator struct { 26 | PkgPath, PkgName string 27 | Types []string 28 | 29 | NoStdMarshalers bool 30 | SnakeCase bool 31 | LowerCamelCase bool 32 | OmitEmpty bool 33 | DisallowUnknownFields bool 34 | SkipMemberNameUnescaping bool 35 | 36 | OutName string 37 | BuildTags string 38 | GenBuildFlags string 39 | 40 | StubsOnly bool 41 | LeaveTemps bool 42 | NoFormat bool 43 | SimpleBytes bool 44 | } 45 | 46 | // writeStub outputs an initial stub for marshalers/unmarshalers so that the package 47 | // using marshalers/unmarshales compiles correctly for boostrapping code. 48 | func (g *Generator) writeStub() error { 49 | f, err := os.Create(g.OutName) 50 | if err != nil { 51 | return err 52 | } 53 | defer f.Close() 54 | 55 | if g.BuildTags != "" { 56 | fmt.Fprintln(f, "// +build ", g.BuildTags) 57 | fmt.Fprintln(f) 58 | } 59 | fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: tinyjson stub code to make the package") 60 | fmt.Fprintln(f, "// compilable during generation.") 61 | fmt.Fprintln(f) 62 | fmt.Fprintln(f, "package ", g.PkgName) 63 | 64 | if len(g.Types) > 0 { 65 | fmt.Fprintln(f) 66 | fmt.Fprintln(f, "import (") 67 | fmt.Fprintln(f, ` "`+pkgWriter+`"`) 68 | fmt.Fprintln(f, ` "`+pkgLexer+`"`) 69 | fmt.Fprintln(f, ")") 70 | } 71 | 72 | sort.Strings(g.Types) 73 | for _, t := range g.Types { 74 | fmt.Fprintln(f) 75 | if !g.NoStdMarshalers { 76 | fmt.Fprintln(f, "func (", t, ") MarshalJSON() ([]byte, error) { return nil, nil }") 77 | fmt.Fprintln(f, "func (*", t, ") UnmarshalJSON([]byte) error { return nil }") 78 | } 79 | 80 | fmt.Fprintln(f, "func (", t, ") MarshalTinyJSON(w *jwriter.Writer) {}") 81 | fmt.Fprintln(f, "func (*", t, ") UnmarshalTinyJSON(l *jlexer.Lexer) {}") 82 | fmt.Fprintln(f) 83 | fmt.Fprintln(f, "type TinyJSON_exporter_"+t+" *"+t) 84 | } 85 | return nil 86 | } 87 | 88 | // writeMain creates a .go file that launches the generator if 'go run'. 89 | func (g *Generator) writeMain() (path string, err error) { 90 | f, err := ioutil.TempFile(filepath.Dir(g.OutName), "tinyjson-bootstrap") 91 | if err != nil { 92 | return "", err 93 | } 94 | 95 | fmt.Fprintln(f, "// +build ignore") 96 | fmt.Fprintln(f) 97 | fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: tinyjson bootstapping code to launch") 98 | fmt.Fprintln(f, "// the actual generator.") 99 | fmt.Fprintln(f) 100 | fmt.Fprintln(f, "package main") 101 | fmt.Fprintln(f) 102 | fmt.Fprintln(f, "import (") 103 | fmt.Fprintln(f, ` "fmt"`) 104 | fmt.Fprintln(f, ` "os"`) 105 | fmt.Fprintln(f) 106 | fmt.Fprintf(f, " %q\n", genPackage) 107 | if len(g.Types) > 0 { 108 | fmt.Fprintln(f) 109 | fmt.Fprintf(f, " pkg %q\n", g.PkgPath) 110 | } 111 | fmt.Fprintln(f, ")") 112 | fmt.Fprintln(f) 113 | fmt.Fprintln(f, "func main() {") 114 | fmt.Fprintf(f, " g := gen.NewGenerator(%q)\n", filepath.Base(g.OutName)) 115 | fmt.Fprintf(f, " g.SetPkg(%q, %q)\n", g.PkgName, g.PkgPath) 116 | if g.BuildTags != "" { 117 | fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags) 118 | } 119 | if g.SnakeCase { 120 | fmt.Fprintln(f, " g.UseSnakeCase()") 121 | } 122 | if g.LowerCamelCase { 123 | fmt.Fprintln(f, " g.UseLowerCamelCase()") 124 | } 125 | if g.OmitEmpty { 126 | fmt.Fprintln(f, " g.OmitEmpty()") 127 | } 128 | if g.NoStdMarshalers { 129 | fmt.Fprintln(f, " g.NoStdMarshalers()") 130 | } 131 | if g.DisallowUnknownFields { 132 | fmt.Fprintln(f, " g.DisallowUnknownFields()") 133 | } 134 | if g.SimpleBytes { 135 | fmt.Fprintln(f, " g.SimpleBytes()") 136 | } 137 | if g.SkipMemberNameUnescaping { 138 | fmt.Fprintln(f, " g.SkipMemberNameUnescaping()") 139 | } 140 | 141 | sort.Strings(g.Types) 142 | for _, v := range g.Types { 143 | fmt.Fprintln(f, " g.Add(pkg.TinyJSON_exporter_"+v+"(nil))") 144 | } 145 | 146 | fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {") 147 | fmt.Fprintln(f, " fmt.Fprintln(os.Stderr, err)") 148 | fmt.Fprintln(f, " os.Exit(1)") 149 | fmt.Fprintln(f, " }") 150 | fmt.Fprintln(f, "}") 151 | 152 | src := f.Name() 153 | if err := f.Close(); err != nil { 154 | return src, err 155 | } 156 | 157 | dest := src + ".go" 158 | return dest, os.Rename(src, dest) 159 | } 160 | 161 | func (g *Generator) Run() error { 162 | if err := g.writeStub(); err != nil { 163 | return err 164 | } 165 | if g.StubsOnly { 166 | return nil 167 | } 168 | 169 | path, err := g.writeMain() 170 | if err != nil { 171 | return err 172 | } 173 | if !g.LeaveTemps { 174 | defer os.Remove(path) 175 | } 176 | 177 | f, err := os.Create(g.OutName + ".tmp") 178 | if err != nil { 179 | return err 180 | } 181 | if !g.LeaveTemps { 182 | defer os.Remove(f.Name()) // will not remove after rename 183 | } 184 | 185 | execArgs := []string{"run"} 186 | if g.GenBuildFlags != "" { 187 | buildFlags := buildFlagsRegexp.FindAllString(g.GenBuildFlags, -1) 188 | execArgs = append(execArgs, buildFlags...) 189 | } 190 | execArgs = append(execArgs, "-tags", g.BuildTags, filepath.Base(path)) 191 | cmd := exec.Command("go", execArgs...) 192 | 193 | cmd.Stdout = f 194 | cmd.Stderr = os.Stderr 195 | cmd.Dir = filepath.Dir(path) 196 | if err = cmd.Run(); err != nil { 197 | return err 198 | } 199 | f.Close() 200 | 201 | // move unformatted file to out path 202 | if g.NoFormat { 203 | return os.Rename(f.Name(), g.OutName) 204 | } 205 | 206 | // format file and write to out path 207 | in, err := ioutil.ReadFile(f.Name()) 208 | if err != nil { 209 | return err 210 | } 211 | out, err := format.Source(in) 212 | if err != nil { 213 | return err 214 | } 215 | return ioutil.WriteFile(g.OutName, out, 0644) 216 | } 217 | -------------------------------------------------------------------------------- /buffer/buffers.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | import "io" 4 | 5 | // This code is copied from golang/net, which is not supported by TinyGo 6 | 7 | // Buffers contains zero or more runs of bytes to write. 8 | // 9 | // On certain machines, for certain types of connections, this is 10 | // optimized into an OS-specific batch write operation (such as 11 | // "writev"). 12 | type Buffers [][]byte 13 | 14 | var ( 15 | _ io.WriterTo = (*Buffers)(nil) 16 | _ io.Reader = (*Buffers)(nil) 17 | ) 18 | 19 | func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) { 20 | // Remove this as TinyGo cannot handle such reflection 21 | // if wv, ok := w.(buffersWriter); ok { 22 | // return wv.writeBuffers(v) 23 | // } 24 | for _, b := range *v { 25 | nb, err := w.Write(b) 26 | n += int64(nb) 27 | if err != nil { 28 | v.consume(n) 29 | return n, err 30 | } 31 | } 32 | v.consume(n) 33 | return n, nil 34 | } 35 | 36 | func (v *Buffers) Read(p []byte) (n int, err error) { 37 | for len(p) > 0 && len(*v) > 0 { 38 | n0 := copy(p, (*v)[0]) 39 | v.consume(int64(n0)) 40 | p = p[n0:] 41 | n += n0 42 | } 43 | if len(*v) == 0 { 44 | err = io.EOF 45 | } 46 | return 47 | } 48 | 49 | func (v *Buffers) consume(n int64) { 50 | for len(*v) > 0 { 51 | ln0 := int64(len((*v)[0])) 52 | if ln0 > n { 53 | (*v)[0] = (*v)[0][n:] 54 | return 55 | } 56 | n -= ln0 57 | (*v)[0] = nil 58 | *v = (*v)[1:] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /buffer/pool.go: -------------------------------------------------------------------------------- 1 | // Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to 2 | // reduce copying and to allow reuse of individual chunks. 3 | package buffer 4 | 5 | import ( 6 | "io" 7 | "sync" 8 | ) 9 | 10 | // PoolConfig contains configuration for the allocation and reuse strategy. 11 | type PoolConfig struct { 12 | StartSize int // Minimum chunk size that is allocated. 13 | PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead. 14 | MaxSize int // Maximum chunk size that will be allocated. 15 | } 16 | 17 | var config = PoolConfig{ 18 | StartSize: 128, 19 | PooledSize: 512, 20 | MaxSize: 32768, 21 | } 22 | 23 | // Reuse pool: chunk size -> pool. 24 | var buffers = map[int]*sync.Pool{} 25 | 26 | func initBuffers() { 27 | for l := config.PooledSize; l <= config.MaxSize; l *= 2 { 28 | buffers[l] = new(sync.Pool) 29 | } 30 | } 31 | 32 | func init() { 33 | initBuffers() 34 | } 35 | 36 | // Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done. 37 | func Init(cfg PoolConfig) { 38 | config = cfg 39 | initBuffers() 40 | } 41 | 42 | // putBuf puts a chunk to reuse pool if it can be reused. 43 | func putBuf(buf []byte) { 44 | size := cap(buf) 45 | if size < config.PooledSize { 46 | return 47 | } 48 | if c := buffers[size]; c != nil { 49 | c.Put(buf[:0]) 50 | } 51 | } 52 | 53 | // getBuf gets a chunk from reuse pool or creates a new one if reuse failed. 54 | func getBuf(size int) []byte { 55 | if size >= config.PooledSize { 56 | if c := buffers[size]; c != nil { 57 | v := c.Get() 58 | if v != nil { 59 | return v.([]byte) 60 | } 61 | } 62 | } 63 | return make([]byte, 0, size) 64 | } 65 | 66 | // Buffer is a buffer optimized for serialization without extra copying. 67 | type Buffer struct { 68 | 69 | // Buf is the current chunk that can be used for serialization. 70 | Buf []byte 71 | 72 | toPool []byte 73 | bufs [][]byte 74 | } 75 | 76 | // EnsureSpace makes sure that the current chunk contains at least s free bytes, 77 | // possibly creating a new chunk. 78 | func (b *Buffer) EnsureSpace(s int) { 79 | if cap(b.Buf)-len(b.Buf) < s { 80 | b.ensureSpaceSlow(s) 81 | } 82 | } 83 | 84 | func (b *Buffer) ensureSpaceSlow(s int) { 85 | l := len(b.Buf) 86 | if l > 0 { 87 | if cap(b.toPool) != cap(b.Buf) { 88 | // Chunk was reallocated, toPool can be pooled. 89 | putBuf(b.toPool) 90 | } 91 | if cap(b.bufs) == 0 { 92 | b.bufs = make([][]byte, 0, 8) 93 | } 94 | b.bufs = append(b.bufs, b.Buf) 95 | l = cap(b.toPool) * 2 96 | } else { 97 | l = config.StartSize 98 | } 99 | 100 | if l > config.MaxSize { 101 | l = config.MaxSize 102 | } 103 | b.Buf = getBuf(l) 104 | b.toPool = b.Buf 105 | } 106 | 107 | // AppendByte appends a single byte to buffer. 108 | func (b *Buffer) AppendByte(data byte) { 109 | b.EnsureSpace(1) 110 | b.Buf = append(b.Buf, data) 111 | } 112 | 113 | // AppendBytes appends a byte slice to buffer. 114 | func (b *Buffer) AppendBytes(data []byte) { 115 | if len(data) <= cap(b.Buf)-len(b.Buf) { 116 | b.Buf = append(b.Buf, data...) // fast path 117 | } else { 118 | b.appendBytesSlow(data) 119 | } 120 | } 121 | 122 | func (b *Buffer) appendBytesSlow(data []byte) { 123 | for len(data) > 0 { 124 | b.EnsureSpace(1) 125 | 126 | sz := cap(b.Buf) - len(b.Buf) 127 | if sz > len(data) { 128 | sz = len(data) 129 | } 130 | 131 | b.Buf = append(b.Buf, data[:sz]...) 132 | data = data[sz:] 133 | } 134 | } 135 | 136 | // AppendString appends a string to buffer. 137 | func (b *Buffer) AppendString(data string) { 138 | if len(data) <= cap(b.Buf)-len(b.Buf) { 139 | b.Buf = append(b.Buf, data...) // fast path 140 | } else { 141 | b.appendStringSlow(data) 142 | } 143 | } 144 | 145 | func (b *Buffer) appendStringSlow(data string) { 146 | for len(data) > 0 { 147 | b.EnsureSpace(1) 148 | 149 | sz := cap(b.Buf) - len(b.Buf) 150 | if sz > len(data) { 151 | sz = len(data) 152 | } 153 | 154 | b.Buf = append(b.Buf, data[:sz]...) 155 | data = data[sz:] 156 | } 157 | } 158 | 159 | // Size computes the size of a buffer by adding sizes of every chunk. 160 | func (b *Buffer) Size() int { 161 | size := len(b.Buf) 162 | for _, buf := range b.bufs { 163 | size += len(buf) 164 | } 165 | return size 166 | } 167 | 168 | // DumpTo outputs the contents of a buffer to a writer and resets the buffer. 169 | func (b *Buffer) DumpTo(w io.Writer) (written int, err error) { 170 | bufs := Buffers(b.bufs) 171 | if len(b.Buf) > 0 { 172 | bufs = append(bufs, b.Buf) 173 | } 174 | n, err := bufs.WriteTo(w) 175 | 176 | for _, buf := range b.bufs { 177 | putBuf(buf) 178 | } 179 | putBuf(b.toPool) 180 | 181 | b.bufs = nil 182 | b.Buf = nil 183 | b.toPool = nil 184 | 185 | return int(n), err 186 | } 187 | 188 | // BuildBytes creates a single byte slice with all the contents of the buffer. Data is 189 | // copied if it does not fit in a single chunk. You can optionally provide one byte 190 | // slice as argument that it will try to reuse. 191 | func (b *Buffer) BuildBytes(reuse ...[]byte) []byte { 192 | if len(b.bufs) == 0 { 193 | ret := b.Buf 194 | b.toPool = nil 195 | b.Buf = nil 196 | return ret 197 | } 198 | 199 | var ret []byte 200 | size := b.Size() 201 | 202 | // If we got a buffer as argument and it is big enough, reuse it. 203 | if len(reuse) == 1 && cap(reuse[0]) >= size { 204 | ret = reuse[0][:0] 205 | } else { 206 | ret = make([]byte, 0, size) 207 | } 208 | for _, buf := range b.bufs { 209 | ret = append(ret, buf...) 210 | putBuf(buf) 211 | } 212 | 213 | ret = append(ret, b.Buf...) 214 | putBuf(b.toPool) 215 | 216 | b.bufs = nil 217 | b.toPool = nil 218 | b.Buf = nil 219 | 220 | return ret 221 | } 222 | 223 | type readCloser struct { 224 | offset int 225 | bufs [][]byte 226 | } 227 | 228 | func (r *readCloser) Read(p []byte) (n int, err error) { 229 | for _, buf := range r.bufs { 230 | // Copy as much as we can. 231 | x := copy(p[n:], buf[r.offset:]) 232 | n += x // Increment how much we filled. 233 | 234 | // Did we empty the whole buffer? 235 | if r.offset+x == len(buf) { 236 | // On to the next buffer. 237 | r.offset = 0 238 | r.bufs = r.bufs[1:] 239 | 240 | // We can release this buffer. 241 | putBuf(buf) 242 | } else { 243 | r.offset += x 244 | } 245 | 246 | if n == len(p) { 247 | break 248 | } 249 | } 250 | // No buffers left or nothing read? 251 | if len(r.bufs) == 0 { 252 | err = io.EOF 253 | } 254 | return 255 | } 256 | 257 | func (r *readCloser) Close() error { 258 | // Release all remaining buffers. 259 | for _, buf := range r.bufs { 260 | putBuf(buf) 261 | } 262 | // In case Close gets called multiple times. 263 | r.bufs = nil 264 | 265 | return nil 266 | } 267 | 268 | // ReadCloser creates an io.ReadCloser with all the contents of the buffer. 269 | func (b *Buffer) ReadCloser() io.ReadCloser { 270 | ret := &readCloser{0, append(b.bufs, b.Buf)} 271 | 272 | b.bufs = nil 273 | b.toPool = nil 274 | b.Buf = nil 275 | 276 | return ret 277 | } 278 | -------------------------------------------------------------------------------- /buffer/pool_test.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestAppendByte(t *testing.T) { 9 | var b Buffer 10 | var want []byte 11 | 12 | for i := 0; i < 1000; i++ { 13 | b.AppendByte(1) 14 | b.AppendByte(2) 15 | want = append(want, 1, 2) 16 | } 17 | 18 | got := b.BuildBytes() 19 | if !bytes.Equal(got, want) { 20 | t.Errorf("BuildBytes() = %v; want %v", got, want) 21 | } 22 | } 23 | 24 | func TestAppendBytes(t *testing.T) { 25 | var b Buffer 26 | var want []byte 27 | 28 | for i := 0; i < 1000; i++ { 29 | b.AppendBytes([]byte{1, 2}) 30 | want = append(want, 1, 2) 31 | } 32 | 33 | got := b.BuildBytes() 34 | if !bytes.Equal(got, want) { 35 | t.Errorf("BuildBytes() = %v; want %v", got, want) 36 | } 37 | } 38 | 39 | func TestAppendString(t *testing.T) { 40 | var b Buffer 41 | var want []byte 42 | 43 | s := "test" 44 | for i := 0; i < 1000; i++ { 45 | b.AppendString(s) 46 | want = append(want, s...) 47 | } 48 | 49 | got := b.BuildBytes() 50 | if !bytes.Equal(got, want) { 51 | t.Errorf("BuildBytes() = %v; want %v", got, want) 52 | } 53 | } 54 | 55 | func TestDumpTo(t *testing.T) { 56 | var b Buffer 57 | var want []byte 58 | 59 | s := "test" 60 | for i := 0; i < 1000; i++ { 61 | b.AppendBytes([]byte(s)) 62 | want = append(want, s...) 63 | } 64 | 65 | out := &bytes.Buffer{} 66 | n, err := b.DumpTo(out) 67 | if err != nil { 68 | t.Errorf("DumpTo() error: %v", err) 69 | } 70 | 71 | got := out.Bytes() 72 | if !bytes.Equal(got, want) { 73 | t.Errorf("DumpTo(): got %v; want %v", got, want) 74 | } 75 | 76 | if n != len(want) { 77 | t.Errorf("DumpTo() = %v; want %v", n, len(want)) 78 | } 79 | } 80 | 81 | func TestReadCloser(t *testing.T) { 82 | var b Buffer 83 | var want []byte 84 | 85 | s := "test" 86 | for i := 0; i < 1000; i++ { 87 | b.AppendBytes([]byte(s)) 88 | want = append(want, s...) 89 | } 90 | 91 | out := &bytes.Buffer{} 92 | rc := b.ReadCloser() 93 | n, err := out.ReadFrom(rc) 94 | if err != nil { 95 | t.Errorf("ReadCloser() error: %v", err) 96 | } 97 | rc.Close() // Will always return nil 98 | 99 | got := out.Bytes() 100 | if !bytes.Equal(got, want) { 101 | t.Errorf("DumpTo(): got %v; want %v", got, want) 102 | } 103 | 104 | if n != int64(len(want)) { 105 | t.Errorf("DumpTo() = %v; want %v", n, len(want)) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /gen/generator.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "hash/fnv" 7 | "io" 8 | "path" 9 | "reflect" 10 | "sort" 11 | "strconv" 12 | "strings" 13 | "unicode" 14 | ) 15 | 16 | const pkgWriter = "github.com/CosmWasm/tinyjson/jwriter" 17 | const pkgLexer = "github.com/CosmWasm/tinyjson/jlexer" 18 | const pkgTinyJSON = "github.com/CosmWasm/tinyjson" 19 | 20 | // FieldNamer defines a policy for generating names for struct fields. 21 | type FieldNamer interface { 22 | GetJSONFieldName(t reflect.Type, f reflect.StructField) string 23 | } 24 | 25 | // Generator generates the requested marshaler/unmarshalers. 26 | type Generator struct { 27 | out *bytes.Buffer 28 | 29 | pkgName string 30 | pkgPath string 31 | buildTags string 32 | hashString string 33 | 34 | varCounter int 35 | 36 | noStdMarshalers bool 37 | omitEmpty bool 38 | disallowUnknownFields bool 39 | fieldNamer FieldNamer 40 | simpleBytes bool 41 | skipMemberNameUnescaping bool 42 | 43 | // package path to local alias map for tracking imports 44 | imports map[string]string 45 | 46 | // types that marshalers were requested for by user 47 | marshalers map[reflect.Type]bool 48 | 49 | // types that encoders were already generated for 50 | typesSeen map[reflect.Type]bool 51 | 52 | // types that encoders were requested for (e.g. by encoders of other types) 53 | typesUnseen []reflect.Type 54 | 55 | // function name to relevant type maps to track names of de-/encoders in 56 | // case of a name clash or unnamed structs 57 | functionNames map[string]reflect.Type 58 | } 59 | 60 | // NewGenerator initializes and returns a Generator. 61 | func NewGenerator(filename string) *Generator { 62 | ret := &Generator{ 63 | imports: map[string]string{ 64 | pkgWriter: "jwriter", 65 | pkgLexer: "jlexer", 66 | pkgTinyJSON: "tinyjson", 67 | }, 68 | fieldNamer: DefaultFieldNamer{}, 69 | marshalers: make(map[reflect.Type]bool), 70 | typesSeen: make(map[reflect.Type]bool), 71 | functionNames: make(map[string]reflect.Type), 72 | } 73 | 74 | // Use a file-unique prefix on all auxiliary funcs to avoid 75 | // name clashes. 76 | hash := fnv.New32() 77 | hash.Write([]byte(filename)) 78 | ret.hashString = fmt.Sprintf("%x", hash.Sum32()) 79 | 80 | return ret 81 | } 82 | 83 | // SetPkg sets the name and path of output package. 84 | func (g *Generator) SetPkg(name, path string) { 85 | g.pkgName = name 86 | g.pkgPath = path 87 | } 88 | 89 | // SetBuildTags sets build tags for the output file. 90 | func (g *Generator) SetBuildTags(tags string) { 91 | g.buildTags = tags 92 | } 93 | 94 | // SetFieldNamer sets field naming strategy. 95 | func (g *Generator) SetFieldNamer(n FieldNamer) { 96 | g.fieldNamer = n 97 | } 98 | 99 | // UseSnakeCase sets snake_case field naming strategy. 100 | func (g *Generator) UseSnakeCase() { 101 | g.fieldNamer = SnakeCaseFieldNamer{} 102 | } 103 | 104 | // UseLowerCamelCase sets lowerCamelCase field naming strategy. 105 | func (g *Generator) UseLowerCamelCase() { 106 | g.fieldNamer = LowerCamelCaseFieldNamer{} 107 | } 108 | 109 | // NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON 110 | // methods (only the custom interface). 111 | func (g *Generator) NoStdMarshalers() { 112 | g.noStdMarshalers = true 113 | } 114 | 115 | // DisallowUnknownFields instructs not to skip unknown fields in json and return error. 116 | func (g *Generator) DisallowUnknownFields() { 117 | g.disallowUnknownFields = true 118 | } 119 | 120 | // SkipMemberNameUnescaping instructs to skip member names unescaping to improve performance 121 | func (g *Generator) SkipMemberNameUnescaping() { 122 | g.skipMemberNameUnescaping = true 123 | } 124 | 125 | // OmitEmpty triggers `json=",omitempty"` behaviour by default. 126 | func (g *Generator) OmitEmpty() { 127 | g.omitEmpty = true 128 | } 129 | 130 | // SimpleBytes triggers generate output bytes as slice byte 131 | func (g *Generator) SimpleBytes() { 132 | g.simpleBytes = true 133 | } 134 | 135 | // addTypes requests to generate encoding/decoding funcs for the given type. 136 | func (g *Generator) addType(t reflect.Type) { 137 | if g.typesSeen[t] { 138 | return 139 | } 140 | for _, t1 := range g.typesUnseen { 141 | if t1 == t { 142 | return 143 | } 144 | } 145 | g.typesUnseen = append(g.typesUnseen, t) 146 | } 147 | 148 | // Add requests to generate marshaler/unmarshalers and encoding/decoding 149 | // funcs for the type of given object. 150 | func (g *Generator) Add(obj interface{}) { 151 | t := reflect.TypeOf(obj) 152 | if t.Kind() == reflect.Ptr { 153 | t = t.Elem() 154 | } 155 | g.addType(t) 156 | g.marshalers[t] = true 157 | } 158 | 159 | // printHeader prints package declaration and imports. 160 | func (g *Generator) printHeader() { 161 | if g.buildTags != "" { 162 | fmt.Println("// +build ", g.buildTags) 163 | fmt.Println() 164 | } 165 | fmt.Println("// Code generated by tinyjson for marshaling/unmarshaling. DO NOT EDIT.") 166 | fmt.Println() 167 | fmt.Println("package ", g.pkgName) 168 | fmt.Println() 169 | 170 | byAlias := make(map[string]string, len(g.imports)) 171 | aliases := make([]string, 0, len(g.imports)) 172 | 173 | for path, alias := range g.imports { 174 | aliases = append(aliases, alias) 175 | byAlias[alias] = path 176 | } 177 | 178 | sort.Strings(aliases) 179 | fmt.Println("import (") 180 | for _, alias := range aliases { 181 | fmt.Printf(" %s %q\n", alias, byAlias[alias]) 182 | } 183 | 184 | fmt.Println(")") 185 | fmt.Println("") 186 | fmt.Println("// suppress unused package warning") 187 | fmt.Println("var (") 188 | fmt.Println(" _ *jlexer.Lexer") 189 | fmt.Println(" _ *jwriter.Writer") 190 | fmt.Println(" _ tinyjson.Marshaler") 191 | fmt.Println(")") 192 | 193 | fmt.Println() 194 | } 195 | 196 | // Run runs the generator and outputs generated code to out. 197 | func (g *Generator) Run(out io.Writer) error { 198 | g.out = &bytes.Buffer{} 199 | 200 | for len(g.typesUnseen) > 0 { 201 | t := g.typesUnseen[len(g.typesUnseen)-1] 202 | g.typesUnseen = g.typesUnseen[:len(g.typesUnseen)-1] 203 | g.typesSeen[t] = true 204 | 205 | if err := g.genDecoder(t); err != nil { 206 | return err 207 | } 208 | if err := g.genEncoder(t); err != nil { 209 | return err 210 | } 211 | 212 | if !g.marshalers[t] { 213 | continue 214 | } 215 | 216 | if err := g.genStructMarshaler(t); err != nil { 217 | return err 218 | } 219 | if err := g.genStructUnmarshaler(t); err != nil { 220 | return err 221 | } 222 | } 223 | g.printHeader() 224 | _, err := out.Write(g.out.Bytes()) 225 | return err 226 | } 227 | 228 | // fixes vendored paths 229 | func fixPkgPathVendoring(pkgPath string) string { 230 | const vendor = "/vendor/" 231 | if i := strings.LastIndex(pkgPath, vendor); i != -1 { 232 | return pkgPath[i+len(vendor):] 233 | } 234 | return pkgPath 235 | } 236 | 237 | func fixAliasName(alias string) string { 238 | alias = strings.Replace( 239 | strings.Replace(alias, ".", "_", -1), 240 | "-", 241 | "_", 242 | -1, 243 | ) 244 | 245 | if alias[0] == 'v' { // to void conflicting with var names, say v1 246 | alias = "_" + alias 247 | } 248 | return alias 249 | } 250 | 251 | // pkgAlias creates and returns and import alias for a given package. 252 | func (g *Generator) pkgAlias(pkgPath string) string { 253 | pkgPath = fixPkgPathVendoring(pkgPath) 254 | if alias := g.imports[pkgPath]; alias != "" { 255 | return alias 256 | } 257 | 258 | for i := 0; ; i++ { 259 | alias := fixAliasName(path.Base(pkgPath)) 260 | if i > 0 { 261 | alias += fmt.Sprint(i) 262 | } 263 | 264 | exists := false 265 | for _, v := range g.imports { 266 | if v == alias { 267 | exists = true 268 | break 269 | } 270 | } 271 | 272 | if !exists { 273 | g.imports[pkgPath] = alias 274 | return alias 275 | } 276 | } 277 | } 278 | 279 | // getType return the textual type name of given type that can be used in generated code. 280 | func (g *Generator) getType(t reflect.Type) string { 281 | if t.Name() == "" { 282 | switch t.Kind() { 283 | case reflect.Ptr: 284 | return "*" + g.getType(t.Elem()) 285 | case reflect.Slice: 286 | return "[]" + g.getType(t.Elem()) 287 | case reflect.Array: 288 | return "[" + strconv.Itoa(t.Len()) + "]" + g.getType(t.Elem()) 289 | case reflect.Map: 290 | return "map[" + g.getType(t.Key()) + "]" + g.getType(t.Elem()) 291 | } 292 | } 293 | 294 | if t.Name() == "" || t.PkgPath() == "" { 295 | if t.Kind() == reflect.Struct { 296 | // the fields of an anonymous struct can have named types, 297 | // and t.String() will not be sufficient because it does not 298 | // remove the package name when it matches g.pkgPath. 299 | // so we convert by hand 300 | nf := t.NumField() 301 | lines := make([]string, 0, nf) 302 | for i := 0; i < nf; i++ { 303 | f := t.Field(i) 304 | var line string 305 | if !f.Anonymous { 306 | line = f.Name + " " 307 | } // else the field is anonymous (an embedded type) 308 | line += g.getType(f.Type) 309 | t := f.Tag 310 | if t != "" { 311 | line += " " + escapeTag(t) 312 | } 313 | lines = append(lines, line) 314 | } 315 | return strings.Join([]string{"struct { ", strings.Join(lines, "; "), " }"}, "") 316 | } 317 | return t.String() 318 | } else if t.PkgPath() == g.pkgPath { 319 | return t.Name() 320 | } 321 | return g.pkgAlias(t.PkgPath()) + "." + t.Name() 322 | } 323 | 324 | // escape a struct field tag string back to source code 325 | func escapeTag(tag reflect.StructTag) string { 326 | t := string(tag) 327 | if strings.ContainsRune(t, '`') { 328 | // there are ` in the string; we can't use ` to enclose the string 329 | return strconv.Quote(t) 330 | } 331 | return "`" + t + "`" 332 | } 333 | 334 | // uniqueVarName returns a file-unique name that can be used for generated variables. 335 | func (g *Generator) uniqueVarName() string { 336 | g.varCounter++ 337 | return fmt.Sprint("v", g.varCounter) 338 | } 339 | 340 | // safeName escapes unsafe characters in pkg/type name and returns a string that can be used 341 | // in encoder/decoder names for the type. 342 | func (g *Generator) safeName(t reflect.Type) string { 343 | name := t.PkgPath() 344 | if t.Name() == "" { 345 | name += "anonymous" 346 | } else { 347 | name += "." + t.Name() 348 | } 349 | 350 | parts := []string{} 351 | part := []rune{} 352 | for _, c := range name { 353 | if unicode.IsLetter(c) || unicode.IsDigit(c) { 354 | part = append(part, c) 355 | } else if len(part) > 0 { 356 | parts = append(parts, string(part)) 357 | part = []rune{} 358 | } 359 | } 360 | return joinFunctionNameParts(false, parts...) 361 | } 362 | 363 | // functionName returns a function name for a given type with a given prefix. If a function 364 | // with this prefix already exists for a type, it is returned. 365 | // 366 | // Method is used to track encoder/decoder names for the type. 367 | func (g *Generator) functionName(prefix string, t reflect.Type) string { 368 | prefix = joinFunctionNameParts(true, "tinyjson", g.hashString, prefix) 369 | name := joinFunctionNameParts(true, prefix, g.safeName(t)) 370 | 371 | // Most of the names will be unique, try a shortcut first. 372 | if e, ok := g.functionNames[name]; !ok || e == t { 373 | g.functionNames[name] = t 374 | return name 375 | } 376 | 377 | // Search if the function already exists. 378 | for name1, t1 := range g.functionNames { 379 | if t1 == t && strings.HasPrefix(name1, prefix) { 380 | return name1 381 | } 382 | } 383 | 384 | // Create a new name in the case of a clash. 385 | for i := 1; ; i++ { 386 | nm := fmt.Sprint(name, i) 387 | if _, ok := g.functionNames[nm]; ok { 388 | continue 389 | } 390 | g.functionNames[nm] = t 391 | return nm 392 | } 393 | } 394 | 395 | // DefaultFieldsNamer implements trivial naming policy equivalent to encoding/json. 396 | type DefaultFieldNamer struct{} 397 | 398 | func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string { 399 | jsonName := strings.Split(f.Tag.Get("json"), ",")[0] 400 | if jsonName != "" { 401 | return jsonName 402 | } 403 | 404 | return f.Name 405 | } 406 | 407 | // LowerCamelCaseFieldNamer 408 | type LowerCamelCaseFieldNamer struct{} 409 | 410 | func isLower(b byte) bool { 411 | return b <= 122 && b >= 97 412 | } 413 | 414 | func isUpper(b byte) bool { 415 | return b >= 65 && b <= 90 416 | } 417 | 418 | // convert HTTPRestClient to httpRestClient 419 | func lowerFirst(s string) string { 420 | if s == "" { 421 | return "" 422 | } 423 | 424 | str := "" 425 | strlen := len(s) 426 | 427 | /** 428 | Loop each char 429 | If is uppercase: 430 | If is first char, LOWER it 431 | If the following char is lower, LEAVE it 432 | If the following char is upper OR numeric, LOWER it 433 | If is the end of string, LEAVE it 434 | Else lowercase 435 | */ 436 | 437 | foundLower := false 438 | for i := range s { 439 | ch := s[i] 440 | if isUpper(ch) { 441 | switch { 442 | case i == 0: 443 | str += string(ch + 32) 444 | case !foundLower: // Currently just a stream of capitals, eg JSONRESTS[erver] 445 | if strlen > (i+1) && isLower(s[i+1]) { 446 | // Next char is lower, keep this a capital 447 | str += string(ch) 448 | } else { 449 | // Either at end of string or next char is capital 450 | str += string(ch + 32) 451 | } 452 | default: 453 | str += string(ch) 454 | } 455 | } else { 456 | foundLower = true 457 | str += string(ch) 458 | } 459 | } 460 | 461 | return str 462 | } 463 | 464 | func (LowerCamelCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string { 465 | jsonName := strings.Split(f.Tag.Get("json"), ",")[0] 466 | if jsonName != "" { 467 | return jsonName 468 | } 469 | 470 | return lowerFirst(f.Name) 471 | } 472 | 473 | // SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names. 474 | type SnakeCaseFieldNamer struct{} 475 | 476 | func camelToSnake(name string) string { 477 | var ret bytes.Buffer 478 | 479 | multipleUpper := false 480 | var lastUpper rune 481 | var beforeUpper rune 482 | 483 | for _, c := range name { 484 | // Non-lowercase character after uppercase is considered to be uppercase too. 485 | isUpper := (unicode.IsUpper(c) || (lastUpper != 0 && !unicode.IsLower(c))) 486 | 487 | if lastUpper != 0 { 488 | // Output a delimiter if last character was either the first uppercase character 489 | // in a row, or the last one in a row (e.g. 'S' in "HTTPServer"). 490 | // Do not output a delimiter at the beginning of the name. 491 | 492 | firstInRow := !multipleUpper 493 | lastInRow := !isUpper 494 | 495 | if ret.Len() > 0 && (firstInRow || lastInRow) && beforeUpper != '_' { 496 | ret.WriteByte('_') 497 | } 498 | ret.WriteRune(unicode.ToLower(lastUpper)) 499 | } 500 | 501 | // Buffer uppercase char, do not output it yet as a delimiter may be required if the 502 | // next character is lowercase. 503 | if isUpper { 504 | multipleUpper = (lastUpper != 0) 505 | lastUpper = c 506 | continue 507 | } 508 | 509 | ret.WriteRune(c) 510 | lastUpper = 0 511 | beforeUpper = c 512 | multipleUpper = false 513 | } 514 | 515 | if lastUpper != 0 { 516 | ret.WriteRune(unicode.ToLower(lastUpper)) 517 | } 518 | return string(ret.Bytes()) 519 | } 520 | 521 | func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string { 522 | jsonName := strings.Split(f.Tag.Get("json"), ",")[0] 523 | if jsonName != "" { 524 | return jsonName 525 | } 526 | 527 | return camelToSnake(f.Name) 528 | } 529 | 530 | func joinFunctionNameParts(keepFirst bool, parts ...string) string { 531 | buf := bytes.NewBufferString("") 532 | for i, part := range parts { 533 | if i == 0 && keepFirst { 534 | buf.WriteString(part) 535 | } else { 536 | if len(part) > 0 { 537 | buf.WriteString(strings.ToUpper(string(part[0]))) 538 | } 539 | if len(part) > 1 { 540 | buf.WriteString(part[1:]) 541 | } 542 | } 543 | } 544 | return buf.String() 545 | } 546 | -------------------------------------------------------------------------------- /gen/generator_test.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCamelToSnake(t *testing.T) { 8 | for i, test := range []struct { 9 | In, Out string 10 | }{ 11 | {"", ""}, 12 | {"A", "a"}, 13 | {"SimpleExample", "simple_example"}, 14 | {"internalField", "internal_field"}, 15 | 16 | {"SomeHTTPStuff", "some_http_stuff"}, 17 | {"WriteJSON", "write_json"}, 18 | {"HTTP2Server", "http2_server"}, 19 | {"Some_Mixed_Case", "some_mixed_case"}, 20 | {"do_nothing", "do_nothing"}, 21 | 22 | {"JSONHTTPRPCServer", "jsonhttprpc_server"}, // nothing can be done here without a dictionary 23 | } { 24 | got := camelToSnake(test.In) 25 | if got != test.Out { 26 | t.Errorf("[%d] camelToSnake(%s) = %s; want %s", i, test.In, got, test.Out) 27 | } 28 | } 29 | } 30 | 31 | func TestCamelToLowerCamel(t *testing.T) { 32 | for i, test := range []struct { 33 | In, Out string 34 | }{ 35 | {"", ""}, 36 | {"A", "a"}, 37 | {"SimpleExample", "simpleExample"}, 38 | {"internalField", "internalField"}, 39 | 40 | {"SomeHTTPStuff", "someHTTPStuff"}, 41 | {"WriteJSON", "writeJSON"}, 42 | {"HTTP2Server", "http2Server"}, 43 | 44 | {"JSONHTTPRPCServer", "jsonhttprpcServer"}, // nothing can be done here without a dictionary 45 | } { 46 | got := lowerFirst(test.In) 47 | if got != test.Out { 48 | t.Errorf("[%d] lowerFirst(%s) = %s; want %s", i, test.In, got, test.Out) 49 | } 50 | } 51 | } 52 | 53 | func TestJoinFunctionNameParts(t *testing.T) { 54 | for i, test := range []struct { 55 | keepFirst bool 56 | parts []string 57 | out string 58 | }{ 59 | {false, []string{}, ""}, 60 | {false, []string{"a"}, "A"}, 61 | {false, []string{"simple", "example"}, "SimpleExample"}, 62 | {true, []string{"first", "example"}, "firstExample"}, 63 | {false, []string{"some", "UPPER", "case"}, "SomeUPPERCase"}, 64 | {false, []string{"number", "123"}, "Number123"}, 65 | } { 66 | got := joinFunctionNameParts(test.keepFirst, test.parts...) 67 | if got != test.out { 68 | t.Errorf("[%d] joinFunctionNameParts(%v) = %s; want %s", i, test.parts, got, test.out) 69 | } 70 | } 71 | } 72 | 73 | func TestFixVendorPath(t *testing.T) { 74 | for i, test := range []struct { 75 | In, Out string 76 | }{ 77 | {"", ""}, 78 | {"time", "time"}, 79 | {"project/vendor/subpackage", "subpackage"}, 80 | } { 81 | got := fixPkgPathVendoring(test.In) 82 | if got != test.Out { 83 | t.Errorf("[%d] fixPkgPathVendoring(%s) = %s; want %s", i, test.In, got, test.Out) 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/CosmWasm/tinyjson 2 | 3 | go 1.16 4 | 5 | require github.com/josharian/intern v1.0.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 2 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 3 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | // Package tinyjson contains marshaler/unmarshaler interfaces and helper functions. 2 | package tinyjson 3 | 4 | import ( 5 | "io" 6 | "io/ioutil" 7 | "unsafe" 8 | 9 | "github.com/CosmWasm/tinyjson/jlexer" 10 | "github.com/CosmWasm/tinyjson/jwriter" 11 | ) 12 | 13 | // Marshaler is an tinyjson-compatible marshaler interface. 14 | type Marshaler interface { 15 | MarshalTinyJSON(w *jwriter.Writer) 16 | } 17 | 18 | // Marshaler is an tinyjson-compatible unmarshaler interface. 19 | type Unmarshaler interface { 20 | UnmarshalTinyJSON(w *jlexer.Lexer) 21 | } 22 | 23 | // MarshalerUnmarshaler is an tinyjson-compatible marshaler/unmarshaler interface. 24 | type MarshalerUnmarshaler interface { 25 | Marshaler 26 | Unmarshaler 27 | } 28 | 29 | // Optional defines an undefined-test method for a type to integrate with 'omitempty' logic. 30 | type Optional interface { 31 | IsDefined() bool 32 | } 33 | 34 | // UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want 35 | type UnknownsUnmarshaler interface { 36 | UnmarshalUnknown(in *jlexer.Lexer, key string) 37 | } 38 | 39 | // UnknownsMarshaler provides a method to write additional struct fields 40 | type UnknownsMarshaler interface { 41 | MarshalUnknowns(w *jwriter.Writer, first bool) 42 | } 43 | 44 | func isNilInterface(i interface{}) bool { 45 | return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0 46 | } 47 | 48 | // Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied 49 | // from a chain of smaller chunks. 50 | func Marshal(v Marshaler) ([]byte, error) { 51 | if isNilInterface(v) { 52 | return nullBytes, nil 53 | } 54 | 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.BuildBytes() 58 | } 59 | 60 | // MarshalToWriter marshals the data to an io.Writer. 61 | func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) { 62 | if isNilInterface(v) { 63 | return w.Write(nullBytes) 64 | } 65 | 66 | jw := jwriter.Writer{} 67 | v.MarshalTinyJSON(&jw) 68 | return jw.DumpTo(w) 69 | } 70 | 71 | // Unmarshal decodes the JSON in data into the object. 72 | func Unmarshal(data []byte, v Unmarshaler) error { 73 | l := jlexer.Lexer{Data: data} 74 | v.UnmarshalTinyJSON(&l) 75 | return l.Error() 76 | } 77 | 78 | // UnmarshalFromReader reads all the data in the reader and decodes as JSON into the object. 79 | func UnmarshalFromReader(r io.Reader, v Unmarshaler) error { 80 | data, err := ioutil.ReadAll(r) 81 | if err != nil { 82 | return err 83 | } 84 | l := jlexer.Lexer{Data: data} 85 | v.UnmarshalTinyJSON(&l) 86 | return l.Error() 87 | } 88 | -------------------------------------------------------------------------------- /helpers_test.go: -------------------------------------------------------------------------------- 1 | package tinyjson 2 | 3 | import "testing" 4 | 5 | func BenchmarkNilCheck(b *testing.B) { 6 | var a *int 7 | for i := 0; i < b.N; i++ { 8 | if !isNilInterface(a) { 9 | b.Fatal("expected it to be nil") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jlexer/bytestostr.go: -------------------------------------------------------------------------------- 1 | // This file will only be included to the build if neither 2 | // tinyjson_nounsafe nor appengine build tag is set. See README notes 3 | // for more details. 4 | 5 | //+build !tinyjson_nounsafe 6 | //+build !appengine 7 | 8 | package jlexer 9 | 10 | import ( 11 | "reflect" 12 | "unsafe" 13 | ) 14 | 15 | // bytesToStr creates a string pointing at the slice to avoid copying. 16 | // 17 | // Warning: the string returned by the function should be used with care, as the whole input data 18 | // chunk may be either blocked from being freed by GC because of a single string or the buffer.Data 19 | // may be garbage-collected even when the string exists. 20 | func bytesToStr(data []byte) string { 21 | h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) 22 | shdr := reflect.StringHeader{Data: h.Data, Len: h.Len} 23 | return *(*string)(unsafe.Pointer(&shdr)) 24 | } 25 | -------------------------------------------------------------------------------- /jlexer/bytestostr_nounsafe.go: -------------------------------------------------------------------------------- 1 | // This file is included to the build if any of the buildtags below 2 | // are defined. Refer to README notes for more details. 3 | 4 | //+build tinyjson_nounsafe appengine 5 | 6 | package jlexer 7 | 8 | // bytesToStr creates a string normally from []byte 9 | // 10 | // Note that this method is roughly 1.5x slower than using the 'unsafe' method. 11 | func bytesToStr(data []byte) string { 12 | return string(data) 13 | } 14 | -------------------------------------------------------------------------------- /jlexer/error.go: -------------------------------------------------------------------------------- 1 | package jlexer 2 | 3 | import "strconv" 4 | 5 | // LexerError implements the error interface and represents all possible errors that can be 6 | // generated during parsing the JSON data. 7 | type LexerError struct { 8 | Reason string 9 | Offset int 10 | Data string 11 | } 12 | 13 | func (l *LexerError) Error() string { 14 | msg := "parse error: " + l.Reason + " near offset " + strconv.Itoa(l.Offset) + "of '" + l.Data + "'" 15 | return msg 16 | } 17 | 18 | // This is a (temporary?) helper to use in place of errors.New 19 | func NewError(msg string) error { 20 | return myError{msg: msg} 21 | } 22 | 23 | type myError struct { 24 | msg string 25 | } 26 | 27 | func (m myError) Error() string { 28 | return m.msg 29 | } 30 | -------------------------------------------------------------------------------- /jlexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package jlexer 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestString(t *testing.T) { 10 | for i, test := range []struct { 11 | toParse string 12 | want string 13 | wantError bool 14 | }{ 15 | {toParse: `"simple string"`, want: "simple string"}, 16 | {toParse: " \r\r\n\t " + `"test"`, want: "test"}, 17 | {toParse: `"\n\t\"\/\\\f\r"`, want: "\n\t\"/\\\f\r"}, 18 | {toParse: `"\u0020"`, want: " "}, 19 | {toParse: `"\u0020-\t"`, want: " -\t"}, 20 | {toParse: `"\ufffd\uFFFD"`, want: "\ufffd\ufffd"}, 21 | {toParse: `"\ud83d\ude00"`, want: "😀"}, 22 | {toParse: `"\ud83d\ude08"`, want: "😈"}, 23 | {toParse: `"\ud8"`, wantError: true}, 24 | 25 | {toParse: `"test"junk`, want: "test"}, 26 | 27 | {toParse: `5`, wantError: true}, // not a string 28 | {toParse: `"\x"`, wantError: true}, // invalid escape 29 | {toParse: `"\ud800"`, want: "�"}, // invalid utf-8 char; return replacement char 30 | } { 31 | { 32 | l := Lexer{Data: []byte(test.toParse)} 33 | 34 | got := l.String() 35 | if got != test.want { 36 | t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want) 37 | } 38 | err := l.Error() 39 | if err != nil && !test.wantError { 40 | t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err) 41 | } else if err == nil && test.wantError { 42 | t.Errorf("[%d, %q] String() ok; want error", i, test.toParse) 43 | } 44 | } 45 | { 46 | l := Lexer{Data: []byte(test.toParse)} 47 | 48 | got := l.StringIntern() 49 | if got != test.want { 50 | t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want) 51 | } 52 | err := l.Error() 53 | if err != nil && !test.wantError { 54 | t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err) 55 | } else if err == nil && test.wantError { 56 | t.Errorf("[%d, %q] String() ok; want error", i, test.toParse) 57 | } 58 | } 59 | } 60 | } 61 | 62 | func TestStringIntern(t *testing.T) { 63 | data := []byte(`"string interning test"`) 64 | var l Lexer 65 | 66 | allocsPerRun := testing.AllocsPerRun(1000, func() { 67 | l = Lexer{Data: data} 68 | _ = l.StringIntern() 69 | }) 70 | if allocsPerRun != 0 { 71 | t.Fatalf("expected 0 allocs, got %f", allocsPerRun) 72 | } 73 | 74 | allocsPerRun = testing.AllocsPerRun(1000, func() { 75 | l = Lexer{Data: data} 76 | _ = l.String() 77 | }) 78 | if allocsPerRun != 1 { 79 | t.Fatalf("expected 1 allocs, got %f", allocsPerRun) 80 | } 81 | } 82 | 83 | func TestBytes(t *testing.T) { 84 | for i, test := range []struct { 85 | toParse string 86 | want string 87 | wantError bool 88 | }{ 89 | {toParse: `"c2ltcGxlIHN0cmluZw=="`, want: "simple string"}, 90 | {toParse: " \r\r\n\t " + `"dGVzdA=="`, want: "test"}, 91 | {toParse: `"c3ViamVjdHM\/X2Q9MQ=="`, want: "subjects?_d=1"}, // base64 with forward slash escaped 92 | 93 | {toParse: `5`, wantError: true}, // not a JSON string 94 | {toParse: `"foobar"`, wantError: true}, // not base64 encoded 95 | {toParse: `"c2ltcGxlIHN0cmluZw="`, wantError: true}, // invalid base64 padding 96 | } { 97 | l := Lexer{Data: []byte(test.toParse)} 98 | 99 | got := l.Bytes() 100 | if bytes.Compare(got, []byte(test.want)) != 0 { 101 | t.Errorf("[%d, %q] Bytes() = %v; want: %v", i, test.toParse, got, []byte(test.want)) 102 | } 103 | err := l.Error() 104 | if err != nil && !test.wantError { 105 | t.Errorf("[%d, %q] Bytes() error: %v", i, test.toParse, err) 106 | } else if err == nil && test.wantError { 107 | t.Errorf("[%d, %q] Bytes() ok; want error", i, test.toParse) 108 | } 109 | } 110 | } 111 | 112 | func TestNumber(t *testing.T) { 113 | for i, test := range []struct { 114 | toParse string 115 | want string 116 | wantError bool 117 | }{ 118 | {toParse: "123", want: "123"}, 119 | {toParse: "-123", want: "-123"}, 120 | {toParse: "\r\n12.35", want: "12.35"}, 121 | {toParse: "12.35e+1", want: "12.35e+1"}, 122 | {toParse: "12.35e-15", want: "12.35e-15"}, 123 | {toParse: "12.35E-15", want: "12.35E-15"}, 124 | {toParse: "12.35E15", want: "12.35E15"}, 125 | 126 | {toParse: `"a"`, wantError: true}, 127 | {toParse: "123junk", wantError: true}, 128 | {toParse: "1.2.3", wantError: true}, 129 | {toParse: "1e2e3", wantError: true}, 130 | {toParse: "1e2.3", wantError: true}, 131 | } { 132 | l := Lexer{Data: []byte(test.toParse)} 133 | 134 | got := l.number() 135 | if got != test.want { 136 | t.Errorf("[%d, %q] number() = %v; want %v", i, test.toParse, got, test.want) 137 | } 138 | err := l.Error() 139 | if err != nil && !test.wantError { 140 | t.Errorf("[%d, %q] number() error: %v", i, test.toParse, err) 141 | } else if err == nil && test.wantError { 142 | t.Errorf("[%d, %q] number() ok; want error", i, test.toParse) 143 | } 144 | } 145 | } 146 | 147 | func TestBool(t *testing.T) { 148 | for i, test := range []struct { 149 | toParse string 150 | want bool 151 | wantError bool 152 | }{ 153 | {toParse: "true", want: true}, 154 | {toParse: "false", want: false}, 155 | 156 | {toParse: "1", wantError: true}, 157 | {toParse: "truejunk", wantError: true}, 158 | {toParse: `false"junk"`, wantError: true}, 159 | {toParse: "True", wantError: true}, 160 | {toParse: "False", wantError: true}, 161 | } { 162 | l := Lexer{Data: []byte(test.toParse)} 163 | 164 | got := l.Bool() 165 | if got != test.want { 166 | t.Errorf("[%d, %q] Bool() = %v; want %v", i, test.toParse, got, test.want) 167 | } 168 | err := l.Error() 169 | if err != nil && !test.wantError { 170 | t.Errorf("[%d, %q] Bool() error: %v", i, test.toParse, err) 171 | } else if err == nil && test.wantError { 172 | t.Errorf("[%d, %q] Bool() ok; want error", i, test.toParse) 173 | } 174 | } 175 | } 176 | 177 | func TestSkipRecursive(t *testing.T) { 178 | for i, test := range []struct { 179 | toParse string 180 | left string 181 | wantError bool 182 | }{ 183 | {toParse: "5, 4", left: ", 4"}, 184 | {toParse: "[5, 6], 4", left: ", 4"}, 185 | {toParse: "[5, [7,8]]: 4", left: ": 4"}, 186 | 187 | {toParse: `{"a":1}, 4`, left: ", 4"}, 188 | {toParse: `{"a":1, "b":{"c": 5}, "e":[12,15]}, 4`, left: ", 4"}, 189 | 190 | // array start/end chars in a string 191 | {toParse: `[5, "]"], 4`, left: ", 4"}, 192 | {toParse: `[5, "\"]"], 4`, left: ", 4"}, 193 | {toParse: `[5, "["], 4`, left: ", 4"}, 194 | {toParse: `[5, "\"["], 4`, left: ", 4"}, 195 | 196 | // object start/end chars in a string 197 | {toParse: `{"a}":1}, 4`, left: ", 4"}, 198 | {toParse: `{"a\"}":1}, 4`, left: ", 4"}, 199 | {toParse: `{"a{":1}, 4`, left: ", 4"}, 200 | {toParse: `{"a\"{":1}, 4`, left: ", 4"}, 201 | 202 | // object with double slashes at the end of string 203 | {toParse: `{"a":"hey\\"}, 4`, left: ", 4"}, 204 | 205 | // make sure skipping an invalid json results in an error 206 | {toParse: `{"a": [ ##invalid json## ]}, 4`, wantError: true}, 207 | {toParse: `{"a": [ [1], [ ##invalid json## ]]}, 4`, wantError: true}, 208 | } { 209 | l := Lexer{Data: []byte(test.toParse)} 210 | 211 | l.SkipRecursive() 212 | 213 | got := string(l.Data[l.pos:]) 214 | if got != test.left { 215 | t.Errorf("[%d, %q] SkipRecursive() left = %v; want %v", i, test.toParse, got, test.left) 216 | } 217 | err := l.Error() 218 | if err != nil && !test.wantError { 219 | t.Errorf("[%d, %q] SkipRecursive() error: %v", i, test.toParse, err) 220 | } else if err == nil && test.wantError { 221 | t.Errorf("[%d, %q] SkipRecursive() ok; want error", i, test.toParse) 222 | } 223 | } 224 | } 225 | 226 | func TestInterface(t *testing.T) { 227 | for i, test := range []struct { 228 | toParse string 229 | want interface{} 230 | wantError bool 231 | }{ 232 | {toParse: "null", want: nil}, 233 | {toParse: "true", want: true}, 234 | {toParse: `"a"`, want: "a"}, 235 | {toParse: "5", want: uint64(5)}, 236 | 237 | {toParse: `{}`, want: map[string]interface{}{}}, 238 | {toParse: `[]`, want: []interface{}{}}, 239 | 240 | {toParse: `{"a": "b"}`, want: map[string]interface{}{"a": "b"}}, 241 | {toParse: `[5]`, want: []interface{}{uint64(5)}}, 242 | 243 | {toParse: `{"a":5 , "b" : "string"}`, want: map[string]interface{}{"a": uint64(5), "b": "string"}}, 244 | {toParse: `["a", 5 , null, true]`, want: []interface{}{"a", uint64(5), nil, true}}, 245 | 246 | {toParse: `{"a" "b"}`, wantError: true}, 247 | {toParse: `{"a": "b",}`, wantError: true}, 248 | {toParse: `{"a":"b","c" "b"}`, wantError: true}, 249 | {toParse: `{"a": "b","c":"d",}`, wantError: true}, 250 | {toParse: `{,}`, wantError: true}, 251 | 252 | {toParse: `[1, 2,]`, wantError: true}, 253 | {toParse: `[1 2]`, wantError: true}, 254 | {toParse: `[,]`, wantError: true}, 255 | } { 256 | l := Lexer{Data: []byte(test.toParse)} 257 | 258 | got := l.Interface() 259 | if !reflect.DeepEqual(got, test.want) { 260 | t.Errorf("[%d, %q] Interface() = %v; want %v", i, test.toParse, got, test.want) 261 | } 262 | err := l.Error() 263 | if err != nil && !test.wantError { 264 | t.Errorf("[%d, %q] Interface() error: %v", i, test.toParse, err) 265 | } else if err == nil && test.wantError { 266 | t.Errorf("[%d, %q] Interface() ok; want error", i, test.toParse) 267 | } 268 | } 269 | } 270 | 271 | func TestConsumed(t *testing.T) { 272 | for i, test := range []struct { 273 | toParse string 274 | wantError bool 275 | }{ 276 | {toParse: "", wantError: false}, 277 | {toParse: " ", wantError: false}, 278 | {toParse: "\r\n", wantError: false}, 279 | {toParse: "\t\t", wantError: false}, 280 | 281 | {toParse: "{", wantError: true}, 282 | } { 283 | l := Lexer{Data: []byte(test.toParse)} 284 | l.Consumed() 285 | 286 | err := l.Error() 287 | if err != nil && !test.wantError { 288 | t.Errorf("[%d, %q] Consumed() error: %v", i, test.toParse, err) 289 | } else if err == nil && test.wantError { 290 | t.Errorf("[%d, %q] Consumed() ok; want error", i, test.toParse) 291 | } 292 | } 293 | } 294 | 295 | func TestFetchStringUnterminatedString(t *testing.T) { 296 | for _, test := range []struct { 297 | data []byte 298 | }{ 299 | {data: []byte(`"sting without trailing quote`)}, 300 | {data: []byte(`"\"`)}, 301 | {data: []byte{'"'}}, 302 | } { 303 | l := Lexer{Data: test.data} 304 | l.fetchString() 305 | if l.pos > len(l.Data) { 306 | t.Errorf("fetchString(%s): pos=%v should not be greater than length of Data = %v", test.data, l.pos, len(l.Data)) 307 | } 308 | if l.Error() == nil { 309 | t.Errorf("fetchString(%s): should add parsing error", test.data) 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /jwriter/writer.go: -------------------------------------------------------------------------------- 1 | // Package jwriter contains a JSON writer. 2 | package jwriter 3 | 4 | import ( 5 | "io" 6 | "strconv" 7 | "unicode/utf8" 8 | 9 | "github.com/CosmWasm/tinyjson/buffer" 10 | ) 11 | 12 | // Flags describe various encoding options. The behavior may be actually implemented in the encoder, but 13 | // Flags field in Writer is used to set and pass them around. 14 | type Flags int 15 | 16 | const ( 17 | NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'. 18 | NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. 19 | ) 20 | 21 | // Writer is a JSON writer. 22 | type Writer struct { 23 | Flags Flags 24 | 25 | Error error 26 | Buffer buffer.Buffer 27 | NoEscapeHTML bool 28 | } 29 | 30 | // Size returns the size of the data that was written out. 31 | func (w *Writer) Size() int { 32 | return w.Buffer.Size() 33 | } 34 | 35 | // DumpTo outputs the data to given io.Writer, resetting the buffer. 36 | func (w *Writer) DumpTo(out io.Writer) (written int, err error) { 37 | return w.Buffer.DumpTo(out) 38 | } 39 | 40 | // BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice 41 | // as argument that it will try to reuse. 42 | func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) { 43 | if w.Error != nil { 44 | return nil, w.Error 45 | } 46 | 47 | return w.Buffer.BuildBytes(reuse...), nil 48 | } 49 | 50 | // ReadCloser returns an io.ReadCloser that can be used to read the data. 51 | // ReadCloser also resets the buffer. 52 | func (w *Writer) ReadCloser() (io.ReadCloser, error) { 53 | if w.Error != nil { 54 | return nil, w.Error 55 | } 56 | 57 | return w.Buffer.ReadCloser(), nil 58 | } 59 | 60 | // RawByte appends raw binary data to the buffer. 61 | func (w *Writer) RawByte(c byte) { 62 | w.Buffer.AppendByte(c) 63 | } 64 | 65 | // RawByte appends raw binary data to the buffer. 66 | func (w *Writer) RawString(s string) { 67 | w.Buffer.AppendString(s) 68 | } 69 | 70 | // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for 71 | // calling with results of MarshalJSON-like functions. 72 | func (w *Writer) Raw(data []byte, err error) { 73 | switch { 74 | case w.Error != nil: 75 | return 76 | case err != nil: 77 | w.Error = err 78 | case len(data) > 0: 79 | w.Buffer.AppendBytes(data) 80 | default: 81 | w.RawString("null") 82 | } 83 | } 84 | 85 | // RawText encloses raw binary data in quotes and appends in to the buffer. 86 | // Useful for calling with results of MarshalText-like functions. 87 | func (w *Writer) RawText(data []byte, err error) { 88 | switch { 89 | case w.Error != nil: 90 | return 91 | case err != nil: 92 | w.Error = err 93 | case len(data) > 0: 94 | w.String(string(data)) 95 | default: 96 | w.RawString("null") 97 | } 98 | } 99 | 100 | // Base64Bytes appends data to the buffer after base64 encoding it 101 | func (w *Writer) Base64Bytes(data []byte) { 102 | if data == nil { 103 | w.Buffer.AppendString("null") 104 | return 105 | } 106 | w.Buffer.AppendByte('"') 107 | w.base64(data) 108 | w.Buffer.AppendByte('"') 109 | } 110 | 111 | func (w *Writer) Uint8(n uint8) { 112 | w.Buffer.EnsureSpace(3) 113 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 114 | } 115 | 116 | func (w *Writer) Uint16(n uint16) { 117 | w.Buffer.EnsureSpace(5) 118 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 119 | } 120 | 121 | func (w *Writer) Uint32(n uint32) { 122 | w.Buffer.EnsureSpace(10) 123 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 124 | } 125 | 126 | func (w *Writer) Uint(n uint) { 127 | w.Buffer.EnsureSpace(20) 128 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 129 | } 130 | 131 | func (w *Writer) Uint64(n uint64) { 132 | w.Buffer.EnsureSpace(20) 133 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) 134 | } 135 | 136 | func (w *Writer) Int8(n int8) { 137 | w.Buffer.EnsureSpace(4) 138 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 139 | } 140 | 141 | func (w *Writer) Int16(n int16) { 142 | w.Buffer.EnsureSpace(6) 143 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 144 | } 145 | 146 | func (w *Writer) Int32(n int32) { 147 | w.Buffer.EnsureSpace(11) 148 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 149 | } 150 | 151 | func (w *Writer) Int(n int) { 152 | w.Buffer.EnsureSpace(21) 153 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 154 | } 155 | 156 | func (w *Writer) Int64(n int64) { 157 | w.Buffer.EnsureSpace(21) 158 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) 159 | } 160 | 161 | func (w *Writer) Uint8Str(n uint8) { 162 | w.Buffer.EnsureSpace(3) 163 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 164 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 165 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 166 | } 167 | 168 | func (w *Writer) Uint16Str(n uint16) { 169 | w.Buffer.EnsureSpace(5) 170 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 171 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 172 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 173 | } 174 | 175 | func (w *Writer) Uint32Str(n uint32) { 176 | w.Buffer.EnsureSpace(10) 177 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 178 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 179 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 180 | } 181 | 182 | func (w *Writer) UintStr(n uint) { 183 | w.Buffer.EnsureSpace(20) 184 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 185 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 186 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 187 | } 188 | 189 | func (w *Writer) Uint64Str(n uint64) { 190 | w.Buffer.EnsureSpace(20) 191 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 192 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) 193 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 194 | } 195 | 196 | func (w *Writer) UintptrStr(n uintptr) { 197 | w.Buffer.EnsureSpace(20) 198 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 199 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 200 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 201 | } 202 | 203 | func (w *Writer) Int8Str(n int8) { 204 | w.Buffer.EnsureSpace(4) 205 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 206 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 207 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 208 | } 209 | 210 | func (w *Writer) Int16Str(n int16) { 211 | w.Buffer.EnsureSpace(6) 212 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 213 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 214 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 215 | } 216 | 217 | func (w *Writer) Int32Str(n int32) { 218 | w.Buffer.EnsureSpace(11) 219 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 220 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 221 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 222 | } 223 | 224 | func (w *Writer) IntStr(n int) { 225 | w.Buffer.EnsureSpace(21) 226 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 227 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 228 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 229 | } 230 | 231 | func (w *Writer) Int64Str(n int64) { 232 | w.Buffer.EnsureSpace(21) 233 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 234 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) 235 | w.Buffer.Buf = append(w.Buffer.Buf, '"') 236 | } 237 | 238 | func (w *Writer) Bool(v bool) { 239 | w.Buffer.EnsureSpace(5) 240 | if v { 241 | w.Buffer.Buf = append(w.Buffer.Buf, "true"...) 242 | } else { 243 | w.Buffer.Buf = append(w.Buffer.Buf, "false"...) 244 | } 245 | } 246 | 247 | const chars = "0123456789abcdef" 248 | 249 | func getTable(falseValues ...int) [128]bool { 250 | table := [128]bool{} 251 | 252 | for i := 0; i < 128; i++ { 253 | table[i] = true 254 | } 255 | 256 | for _, v := range falseValues { 257 | table[v] = false 258 | } 259 | 260 | return table 261 | } 262 | 263 | var ( 264 | htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\') 265 | htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\') 266 | ) 267 | 268 | func (w *Writer) String(s string) { 269 | w.Buffer.AppendByte('"') 270 | 271 | // Portions of the string that contain no escapes are appended as 272 | // byte slices. 273 | 274 | p := 0 // last non-escape symbol 275 | 276 | escapeTable := &htmlEscapeTable 277 | if w.NoEscapeHTML { 278 | escapeTable = &htmlNoEscapeTable 279 | } 280 | 281 | for i := 0; i < len(s); { 282 | c := s[i] 283 | 284 | if c < utf8.RuneSelf { 285 | if escapeTable[c] { 286 | // single-width character, no escaping is required 287 | i++ 288 | continue 289 | } 290 | 291 | w.Buffer.AppendString(s[p:i]) 292 | switch c { 293 | case '\t': 294 | w.Buffer.AppendString(`\t`) 295 | case '\r': 296 | w.Buffer.AppendString(`\r`) 297 | case '\n': 298 | w.Buffer.AppendString(`\n`) 299 | case '\\': 300 | w.Buffer.AppendString(`\\`) 301 | case '"': 302 | w.Buffer.AppendString(`\"`) 303 | default: 304 | w.Buffer.AppendString(`\u00`) 305 | w.Buffer.AppendByte(chars[c>>4]) 306 | w.Buffer.AppendByte(chars[c&0xf]) 307 | } 308 | 309 | i++ 310 | p = i 311 | continue 312 | } 313 | 314 | // broken utf 315 | runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) 316 | if runeValue == utf8.RuneError && runeWidth == 1 { 317 | w.Buffer.AppendString(s[p:i]) 318 | w.Buffer.AppendString(`\ufffd`) 319 | i++ 320 | p = i 321 | continue 322 | } 323 | 324 | // jsonp stuff - tab separator and line separator 325 | if runeValue == '\u2028' || runeValue == '\u2029' { 326 | w.Buffer.AppendString(s[p:i]) 327 | w.Buffer.AppendString(`\u202`) 328 | w.Buffer.AppendByte(chars[runeValue&0xf]) 329 | i += runeWidth 330 | p = i 331 | continue 332 | } 333 | i += runeWidth 334 | } 335 | w.Buffer.AppendString(s[p:]) 336 | w.Buffer.AppendByte('"') 337 | } 338 | 339 | const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 340 | const padChar = '=' 341 | 342 | func (w *Writer) base64(in []byte) { 343 | 344 | if len(in) == 0 { 345 | return 346 | } 347 | 348 | w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4) 349 | 350 | si := 0 351 | n := (len(in) / 3) * 3 352 | 353 | for si < n { 354 | // Convert 3x 8bit source bytes into 4 bytes 355 | val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) 356 | 357 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) 358 | 359 | si += 3 360 | } 361 | 362 | remain := len(in) - si 363 | if remain == 0 { 364 | return 365 | } 366 | 367 | // Add the remaining small block 368 | val := uint(in[si+0]) << 16 369 | if remain == 2 { 370 | val |= uint(in[si+1]) << 8 371 | } 372 | 373 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) 374 | 375 | switch remain { 376 | case 2: 377 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) 378 | case 1: 379 | w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /opt/gotemplate_Bool.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Bool struct { 16 | V bool 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OBool(v bool) Bool { 22 | return Bool{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Bool) Get(deflt bool) bool { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Bool) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Bool(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Bool) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Bool{} 47 | } else { 48 | v.V = l.Bool() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Bool) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Bool) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Bool) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Bool) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Int.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Int struct { 16 | V int 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OInt(v int) Int { 22 | return Int{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Int) Get(deflt int) int { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Int) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Int(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Int) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Int{} 47 | } else { 48 | v.V = l.Int() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Int) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Int) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Int) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Int) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Int16.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Int16 struct { 16 | V int16 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OInt16(v int16) Int16 { 22 | return Int16{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Int16) Get(deflt int16) int16 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Int16) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Int16(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Int16) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Int16{} 47 | } else { 48 | v.V = l.Int16() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Int16) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Int16) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Int16) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Int16) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Int32.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Int32 struct { 16 | V int32 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OInt32(v int32) Int32 { 22 | return Int32{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Int32) Get(deflt int32) int32 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Int32) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Int32(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Int32) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Int32{} 47 | } else { 48 | v.V = l.Int32() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Int32) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Int32) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Int32) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Int32) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Int64.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Int64 struct { 16 | V int64 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OInt64(v int64) Int64 { 22 | return Int64{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Int64) Get(deflt int64) int64 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Int64) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Int64(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Int64) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Int64{} 47 | } else { 48 | v.V = l.Int64() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Int64) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Int64) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Int64) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Int64) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Int8.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Int8 struct { 16 | V int8 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OInt8(v int8) Int8 { 22 | return Int8{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Int8) Get(deflt int8) int8 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Int8) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Int8(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Int8) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Int8{} 47 | } else { 48 | v.V = l.Int8() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Int8) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Int8) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Int8) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Int8) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_String.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type String struct { 16 | V string 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OString(v string) String { 22 | return String{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v String) Get(deflt string) string { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v String) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.String(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *String) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = String{} 47 | } else { 48 | v.V = l.String() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v String) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *String) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v String) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v String) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Uint.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Uint struct { 16 | V uint 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OUint(v uint) Uint { 22 | return Uint{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Uint) Get(deflt uint) uint { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Uint) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Uint(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Uint) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Uint{} 47 | } else { 48 | v.V = l.Uint() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Uint) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Uint) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Uint) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Uint) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Uint16.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Uint16 struct { 16 | V uint16 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OUint16(v uint16) Uint16 { 22 | return Uint16{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Uint16) Get(deflt uint16) uint16 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Uint16) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Uint16(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Uint16) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Uint16{} 47 | } else { 48 | v.V = l.Uint16() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Uint16) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Uint16) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Uint16) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Uint16) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Uint32.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Uint32 struct { 16 | V uint32 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OUint32(v uint32) Uint32 { 22 | return Uint32{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Uint32) Get(deflt uint32) uint32 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Uint32) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Uint32(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Uint32) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Uint32{} 47 | } else { 48 | v.V = l.Uint32() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Uint32) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Uint32) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Uint32) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Uint32) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Uint64.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Uint64 struct { 16 | V uint64 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OUint64(v uint64) Uint64 { 22 | return Uint64{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Uint64) Get(deflt uint64) uint64 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Uint64) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Uint64(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Uint64) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Uint64{} 47 | } else { 48 | v.V = l.Uint64() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Uint64) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Uint64) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Uint64) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Uint64) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/gotemplate_Uint8.go: -------------------------------------------------------------------------------- 1 | // generated by gotemplate 2 | 3 | package opt 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | 14 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 15 | type Uint8 struct { 16 | V uint8 17 | Defined bool 18 | } 19 | 20 | // Creates an optional type with a given value. 21 | func OUint8(v uint8) Uint8 { 22 | return Uint8{V: v, Defined: true} 23 | } 24 | 25 | // Get returns the value or given default in the case the value is undefined. 26 | func (v Uint8) Get(deflt uint8) uint8 { 27 | if !v.Defined { 28 | return deflt 29 | } 30 | return v.V 31 | } 32 | 33 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 34 | func (v Uint8) MarshalTinyJSON(w *jwriter.Writer) { 35 | if v.Defined { 36 | w.Uint8(v.V) 37 | } else { 38 | w.RawString("null") 39 | } 40 | } 41 | 42 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 43 | func (v *Uint8) UnmarshalTinyJSON(l *jlexer.Lexer) { 44 | if l.IsNull() { 45 | l.Skip() 46 | *v = Uint8{} 47 | } else { 48 | v.V = l.Uint8() 49 | v.Defined = true 50 | } 51 | } 52 | 53 | // MarshalJSON implements a standard json marshaler interface. 54 | func (v Uint8) MarshalJSON() ([]byte, error) { 55 | w := jwriter.Writer{} 56 | v.MarshalTinyJSON(&w) 57 | return w.Buffer.BuildBytes(), w.Error 58 | } 59 | 60 | // UnmarshalJSON implements a standard json unmarshaler interface. 61 | func (v *Uint8) UnmarshalJSON(data []byte) error { 62 | l := jlexer.Lexer{Data: data} 63 | v.UnmarshalTinyJSON(&l) 64 | return l.Error() 65 | } 66 | 67 | // IsDefined returns whether the value is defined, a function is required so that it can 68 | // be used in an interface. 69 | func (v Uint8) IsDefined() bool { 70 | return v.Defined 71 | } 72 | 73 | // String implements a stringer interface using fmt.Sprint for the value. 74 | func (v Uint8) String() string { 75 | if !v.Defined { 76 | return "" 77 | } 78 | return fmt.Sprint(v.V) 79 | } 80 | -------------------------------------------------------------------------------- /opt/optional/opt.go: -------------------------------------------------------------------------------- 1 | // +build none 2 | 3 | package optional 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/CosmWasm/tinyjson/jlexer" 9 | "github.com/CosmWasm/tinyjson/jwriter" 10 | ) 11 | 12 | // template type Optional(A) 13 | type A int 14 | 15 | // A 'gotemplate'-based type for providing optional semantics without using pointers. 16 | type Optional struct { 17 | V A 18 | Defined bool 19 | } 20 | 21 | // Creates an optional type with a given value. 22 | func OOptional(v A) Optional { 23 | return Optional{V: v, Defined: true} 24 | } 25 | 26 | // Get returns the value or given default in the case the value is undefined. 27 | func (v Optional) Get(deflt A) A { 28 | if !v.Defined { 29 | return deflt 30 | } 31 | return v.V 32 | } 33 | 34 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 35 | func (v Optional) MarshalTinyJSON(w *jwriter.Writer) { 36 | if v.Defined { 37 | w.Optional(v.V) 38 | } else { 39 | w.RawString("null") 40 | } 41 | } 42 | 43 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 44 | func (v *Optional) UnmarshalTinyJSON(l *jlexer.Lexer) { 45 | if l.IsNull() { 46 | l.Skip() 47 | *v = Optional{} 48 | } else { 49 | v.V = l.Optional() 50 | v.Defined = true 51 | } 52 | } 53 | 54 | // MarshalJSON implements a standard json marshaler interface. 55 | func (v Optional) MarshalJSON() ([]byte, error) { 56 | w := jwriter.Writer{} 57 | v.MarshalTinyJSON(&w) 58 | return w.Buffer.BuildBytes(), w.Error 59 | } 60 | 61 | // UnmarshalJSON implements a standard json unmarshaler interface. 62 | func (v *Optional) UnmarshalJSON(data []byte) error { 63 | l := jlexer.Lexer{Data: data} 64 | v.UnmarshalTinyJSON(&l) 65 | return l.Error() 66 | } 67 | 68 | // IsDefined returns whether the value is defined, a function is required so that it can 69 | // be used in an interface. 70 | func (v Optional) IsDefined() bool { 71 | return v.Defined 72 | } 73 | 74 | // String implements a stringer interface using fmt.Sprint for the value. 75 | func (v Optional) String() string { 76 | if !v.Defined { 77 | return "" 78 | } 79 | return fmt.Sprint(v.V) 80 | } 81 | -------------------------------------------------------------------------------- /opt/opts.go: -------------------------------------------------------------------------------- 1 | package opt 2 | 3 | //go:generate sed -i "s/\\+build none/generated by gotemplate/" optional/opt.go 4 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Int(int) 5 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Uint(uint) 6 | 7 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Int8(int8) 8 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Int16(int16) 9 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Int32(int32) 10 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Int64(int64) 11 | 12 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Uint8(uint8) 13 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Uint16(uint16) 14 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Uint32(uint32) 15 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Uint64(uint64) 16 | 17 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Float32(float32) 18 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Float64(float64) 19 | 20 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" Bool(bool) 21 | //go:generate gotemplate "github.com/CosmWasm/tinyjson/opt/optional" String(string) 22 | //go:generate sed -i "s/generated by gotemplate/+build none/" optional/opt.go 23 | -------------------------------------------------------------------------------- /parser/modulepath.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | ) 7 | 8 | // Content of this file was copied from the package golang.org/x/mod/modfile 9 | // https://github.com/golang/mod/blob/v0.2.0/modfile/read.go#L877 10 | // Under the BSD-3-Clause licence: 11 | // golang.org/x/mod@v0.2.0/LICENSE 12 | /* 13 | Copyright (c) 2009 The Go Authors. All rights reserved. 14 | 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are 17 | met: 18 | 19 | * Redistributions of source code must retain the above copyright 20 | notice, this list of conditions and the following disclaimer. 21 | * Redistributions in binary form must reproduce the above 22 | copyright notice, this list of conditions and the following disclaimer 23 | in the documentation and/or other materials provided with the 24 | distribution. 25 | * Neither the name of Google Inc. nor the names of its 26 | contributors may be used to endorse or promote products derived from 27 | this software without specific prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | */ 41 | 42 | var ( 43 | slashSlash = []byte("//") 44 | moduleStr = []byte("module") 45 | ) 46 | 47 | // modulePath returns the module path from the gomod file text. 48 | // If it cannot find a module path, it returns an empty string. 49 | // It is tolerant of unrelated problems in the go.mod file. 50 | func modulePath(mod []byte) string { 51 | for len(mod) > 0 { 52 | line := mod 53 | mod = nil 54 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 55 | line, mod = line[:i], line[i+1:] 56 | } 57 | if i := bytes.Index(line, slashSlash); i >= 0 { 58 | line = line[:i] 59 | } 60 | line = bytes.TrimSpace(line) 61 | if !bytes.HasPrefix(line, moduleStr) { 62 | continue 63 | } 64 | line = line[len(moduleStr):] 65 | n := len(line) 66 | line = bytes.TrimSpace(line) 67 | if len(line) == n || len(line) == 0 { 68 | continue 69 | } 70 | 71 | if line[0] == '"' || line[0] == '`' { 72 | p, err := strconv.Unquote(string(line)) 73 | if err != nil { 74 | return "" // malformed quoted string or multiline module path 75 | } 76 | return p 77 | } 78 | 79 | return string(line) 80 | } 81 | return "" // missing module path 82 | } 83 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "go/ast" 5 | "go/parser" 6 | "go/token" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | structComment = "tinyjson:json" 13 | structSkipComment = "tinyjson:skip" 14 | ) 15 | 16 | type Parser struct { 17 | PkgPath string 18 | PkgName string 19 | StructNames []string 20 | AllStructs bool 21 | } 22 | 23 | type visitor struct { 24 | *Parser 25 | 26 | name string 27 | } 28 | 29 | func (p *Parser) needType(comments *ast.CommentGroup) (skip, explicit bool) { 30 | if comments == nil { 31 | return 32 | } 33 | 34 | for _, v := range comments.List { 35 | comment := v.Text 36 | 37 | if len(comment) > 2 { 38 | switch comment[1] { 39 | case '/': 40 | // -style comment (no newline at the end) 41 | comment = comment[2:] 42 | case '*': 43 | /*-style comment */ 44 | comment = comment[2 : len(comment)-2] 45 | } 46 | } 47 | 48 | for _, comment := range strings.Split(comment, "\n") { 49 | comment = strings.TrimSpace(comment) 50 | 51 | if strings.HasPrefix(comment, structSkipComment) { 52 | return true, false 53 | } 54 | if strings.HasPrefix(comment, structComment) { 55 | return false, true 56 | } 57 | } 58 | } 59 | 60 | return 61 | } 62 | 63 | func (v *visitor) Visit(n ast.Node) (w ast.Visitor) { 64 | switch n := n.(type) { 65 | case *ast.Package: 66 | return v 67 | case *ast.File: 68 | v.PkgName = n.Name.String() 69 | return v 70 | 71 | case *ast.GenDecl: 72 | skip, explicit := v.needType(n.Doc) 73 | 74 | if skip || explicit { 75 | for _, nc := range n.Specs { 76 | switch nct := nc.(type) { 77 | case *ast.TypeSpec: 78 | nct.Doc = n.Doc 79 | } 80 | } 81 | } 82 | 83 | return v 84 | case *ast.TypeSpec: 85 | skip, explicit := v.needType(n.Doc) 86 | if skip { 87 | return nil 88 | } 89 | if !explicit && !v.AllStructs { 90 | return nil 91 | } 92 | 93 | v.name = n.Name.String() 94 | 95 | // Allow to specify non-structs explicitly independent of '-all' flag. 96 | if explicit { 97 | v.StructNames = append(v.StructNames, v.name) 98 | return nil 99 | } 100 | 101 | return v 102 | case *ast.StructType: 103 | v.StructNames = append(v.StructNames, v.name) 104 | return nil 105 | } 106 | return nil 107 | } 108 | 109 | func (p *Parser) Parse(fname string, isDir bool) error { 110 | var err error 111 | if p.PkgPath, err = getPkgPath(fname, isDir); err != nil { 112 | return err 113 | } 114 | 115 | fset := token.NewFileSet() 116 | if isDir { 117 | packages, err := parser.ParseDir(fset, fname, excludeTestFiles, parser.ParseComments) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | for _, pckg := range packages { 123 | ast.Walk(&visitor{Parser: p}, pckg) 124 | } 125 | } else { 126 | f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | ast.Walk(&visitor{Parser: p}, f) 132 | } 133 | return nil 134 | } 135 | 136 | func excludeTestFiles(fi os.FileInfo) bool { 137 | return !strings.HasSuffix(fi.Name(), "_test.go") 138 | } 139 | -------------------------------------------------------------------------------- /parser/pkgpath.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/build" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "path" 11 | "path/filepath" 12 | "strings" 13 | "sync" 14 | ) 15 | 16 | func getPkgPath(fname string, isDir bool) (string, error) { 17 | if !filepath.IsAbs(fname) { 18 | pwd, err := os.Getwd() 19 | if err != nil { 20 | return "", err 21 | } 22 | fname = filepath.Join(pwd, fname) 23 | } 24 | 25 | goModPath, _ := goModPath(fname, isDir) 26 | if strings.Contains(goModPath, "go.mod") { 27 | pkgPath, err := getPkgPathFromGoMod(fname, isDir, goModPath) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | return pkgPath, nil 33 | } 34 | 35 | return getPkgPathFromGOPATH(fname, isDir) 36 | } 37 | 38 | var goModPathCache = struct { 39 | paths map[string]string 40 | sync.RWMutex 41 | }{ 42 | paths: make(map[string]string), 43 | } 44 | 45 | // empty if no go.mod, GO111MODULE=off or go without go modules support 46 | func goModPath(fname string, isDir bool) (string, error) { 47 | root := fname 48 | if !isDir { 49 | root = filepath.Dir(fname) 50 | } 51 | 52 | goModPathCache.RLock() 53 | goModPath, ok := goModPathCache.paths[root] 54 | goModPathCache.RUnlock() 55 | if ok { 56 | return goModPath, nil 57 | } 58 | 59 | defer func() { 60 | goModPathCache.Lock() 61 | goModPathCache.paths[root] = goModPath 62 | goModPathCache.Unlock() 63 | }() 64 | 65 | cmd := exec.Command("go", "env", "GOMOD") 66 | cmd.Dir = root 67 | 68 | stdout, err := cmd.Output() 69 | if err != nil { 70 | return "", err 71 | } 72 | 73 | goModPath = string(bytes.TrimSpace(stdout)) 74 | 75 | return goModPath, nil 76 | } 77 | 78 | func getPkgPathFromGoMod(fname string, isDir bool, goModPath string) (string, error) { 79 | modulePath := getModulePath(goModPath) 80 | if modulePath == "" { 81 | return "", fmt.Errorf("cannot determine module path from %s", goModPath) 82 | } 83 | 84 | rel := path.Join(modulePath, filePathToPackagePath(strings.TrimPrefix(fname, filepath.Dir(goModPath)))) 85 | 86 | if !isDir { 87 | return path.Dir(rel), nil 88 | } 89 | 90 | return path.Clean(rel), nil 91 | } 92 | 93 | var pkgPathFromGoModCache = struct { 94 | paths map[string]string 95 | sync.RWMutex 96 | }{ 97 | paths: make(map[string]string), 98 | } 99 | 100 | func getModulePath(goModPath string) string { 101 | pkgPathFromGoModCache.RLock() 102 | pkgPath, ok := pkgPathFromGoModCache.paths[goModPath] 103 | pkgPathFromGoModCache.RUnlock() 104 | if ok { 105 | return pkgPath 106 | } 107 | 108 | defer func() { 109 | pkgPathFromGoModCache.Lock() 110 | pkgPathFromGoModCache.paths[goModPath] = pkgPath 111 | pkgPathFromGoModCache.Unlock() 112 | }() 113 | 114 | data, err := ioutil.ReadFile(goModPath) 115 | if err != nil { 116 | return "" 117 | } 118 | pkgPath = modulePath(data) 119 | return pkgPath 120 | } 121 | 122 | func getPkgPathFromGOPATH(fname string, isDir bool) (string, error) { 123 | gopath := os.Getenv("GOPATH") 124 | if gopath == "" { 125 | gopath = build.Default.GOPATH 126 | } 127 | 128 | for _, p := range strings.Split(gopath, string(filepath.ListSeparator)) { 129 | prefix := filepath.Join(p, "src") + string(filepath.Separator) 130 | rel, err := filepath.Rel(prefix, fname) 131 | if err == nil && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) { 132 | if !isDir { 133 | return path.Dir(filePathToPackagePath(rel)), nil 134 | } else { 135 | return path.Clean(filePathToPackagePath(rel)), nil 136 | } 137 | } 138 | } 139 | 140 | return "", fmt.Errorf("file '%v' is not in GOPATH '%v'", fname, gopath) 141 | } 142 | 143 | func filePathToPackagePath(path string) string { 144 | return filepath.ToSlash(path) 145 | } 146 | -------------------------------------------------------------------------------- /parser/pkgpath_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import "testing" 4 | 5 | func Test_getModulePath(t *testing.T) { 6 | tests := map[string]struct { 7 | goModPath string 8 | want string 9 | }{ 10 | "valid go.mod without comments and deps": { 11 | goModPath: "./testdata/default.go.mod", 12 | want: "example.com/user/project", 13 | }, 14 | "valid go.mod with comments and without deps": { 15 | goModPath: "./testdata/comments.go.mod", 16 | want: "example.com/user/project", 17 | }, 18 | "valid go.mod with comments and deps": { 19 | goModPath: "./testdata/comments_deps.go.mod", 20 | want: "example.com/user/project", 21 | }, 22 | "actual tinyjson go.mod": { 23 | goModPath: "../go.mod", 24 | want: "github.com/CosmWasm/tinyjson", 25 | }, 26 | "invalid go.mod with missing module": { 27 | goModPath: "./testdata/missing_module.go", 28 | want: "", 29 | }, 30 | } 31 | for name := range tests { 32 | tt := tests[name] 33 | t.Run(name, func(t *testing.T) { 34 | if got := getModulePath(tt.goModPath); got != tt.want { 35 | t.Errorf("getModulePath() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /parser/testdata/comments.go.mod: -------------------------------------------------------------------------------- 1 | // first-line comment which should bresk anything 2 | module example.com/user/project // end-line comment which should not break anything 3 | 4 | go 1.13 5 | -------------------------------------------------------------------------------- /parser/testdata/comments_deps.go.mod: -------------------------------------------------------------------------------- 1 | // first-line comment which should bresk anything 2 | module example.com/user/project // end-line comment which should not break anything 3 | 4 | go 1.13 5 | 6 | require ( 7 | github.com/CosmWasm/tinyjson v0.7.0 8 | ) 9 | -------------------------------------------------------------------------------- /parser/testdata/default.go.mod: -------------------------------------------------------------------------------- 1 | module example.com/user/project 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /parser/testdata/missing_module.go.mod: -------------------------------------------------------------------------------- 1 | 2 | go 1.13 3 | 4 | require ( 5 | github.com/CosmWasm/tinyjson v0.7.0 6 | ) 7 | -------------------------------------------------------------------------------- /raw.go: -------------------------------------------------------------------------------- 1 | package tinyjson 2 | 3 | import ( 4 | "github.com/CosmWasm/tinyjson/jlexer" 5 | "github.com/CosmWasm/tinyjson/jwriter" 6 | ) 7 | 8 | // RawMessage is a raw piece of JSON (number, string, bool, object, array or 9 | // null) that is extracted without parsing and output as is during marshaling. 10 | type RawMessage []byte 11 | 12 | // MarshalTinyJSON does JSON marshaling using tinyjson interface. 13 | func (v *RawMessage) MarshalTinyJSON(w *jwriter.Writer) { 14 | if len(*v) == 0 { 15 | w.RawString("null") 16 | } else { 17 | w.Raw(*v, nil) 18 | } 19 | } 20 | 21 | // UnmarshalTinyJSON does JSON unmarshaling using tinyjson interface. 22 | func (v *RawMessage) UnmarshalTinyJSON(l *jlexer.Lexer) { 23 | *v = RawMessage(l.Raw()) 24 | } 25 | 26 | // UnmarshalJSON implements encoding/json.Unmarshaler interface. 27 | func (v *RawMessage) UnmarshalJSON(data []byte) error { 28 | *v = data 29 | return nil 30 | } 31 | 32 | var nullBytes = []byte("null") 33 | 34 | // MarshalJSON implements encoding/json.Marshaler interface. 35 | func (v RawMessage) MarshalJSON() ([]byte, error) { 36 | if len(v) == 0 { 37 | return nullBytes, nil 38 | } 39 | return v, nil 40 | } 41 | 42 | // IsDefined is required for integration with omitempty tinyjson logic. 43 | func (v *RawMessage) IsDefined() bool { 44 | return len(*v) > 0 45 | } 46 | -------------------------------------------------------------------------------- /tests/basic_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/CosmWasm/tinyjson" 10 | "github.com/CosmWasm/tinyjson/jwriter" 11 | ) 12 | 13 | type testType interface { 14 | json.Marshaler 15 | json.Unmarshaler 16 | } 17 | 18 | var testCases = []struct { 19 | Decoded testType 20 | Encoded string 21 | }{ 22 | {&primitiveTypesValue, primitiveTypesString}, 23 | {&namedPrimitiveTypesValue, namedPrimitiveTypesString}, 24 | {&structsValue, structsString}, 25 | {&omitEmptyValue, omitEmptyString}, 26 | {&snakeStructValue, snakeStructString}, 27 | {&omitEmptyDefaultValue, omitEmptyDefaultString}, 28 | {&optsValue, optsString}, 29 | {&rawValue, rawString}, 30 | {&stdMarshalerValue, stdMarshalerString}, 31 | {&unexportedStructValue, unexportedStructString}, 32 | {&excludedFieldValue, excludedFieldString}, 33 | {&sliceValue, sliceString}, 34 | {&arrayValue, arrayString}, 35 | {&mapsValue, mapsString}, 36 | {&deepNestValue, deepNestString}, 37 | {&IntsValue, IntsString}, 38 | {&mapStringStringValue, mapStringStringString}, 39 | {&namedTypeValue, namedTypeValueString}, 40 | {&customMapKeyTypeValue, customMapKeyTypeValueString}, 41 | {&embeddedTypeValue, embeddedTypeValueString}, 42 | {&mapMyIntStringValue, mapMyIntStringValueString}, 43 | {&mapIntStringValue, mapIntStringValueString}, 44 | {&mapInt32StringValue, mapInt32StringValueString}, 45 | {&mapInt64StringValue, mapInt64StringValueString}, 46 | {&mapUintStringValue, mapUintStringValueString}, 47 | {&mapUint32StringValue, mapUint32StringValueString}, 48 | {&mapUint64StringValue, mapUint64StringValueString}, 49 | {&mapUintptrStringValue, mapUintptrStringValueString}, 50 | {&intKeyedMapStructValue, intKeyedMapStructValueString}, 51 | {&intArrayStructValue, intArrayStructValueString}, 52 | {&myUInt8SliceValue, myUInt8SliceString}, 53 | {&myUInt8ArrayValue, myUInt8ArrayString}, 54 | {&mapWithEncodingMarshaler, mapWithEncodingMarshalerString}, 55 | {&myGenDeclaredValue, myGenDeclaredString}, 56 | {&myGenDeclaredWithCommentValue, myGenDeclaredWithCommentString}, 57 | {&myTypeDeclaredValue, myTypeDeclaredString}, 58 | {&myTypeNotSkippedValue, myTypeNotSkippedString}, 59 | {&intern, internString}, 60 | } 61 | 62 | func TestMarshal(t *testing.T) { 63 | for i, test := range testCases { 64 | data, err := test.Decoded.MarshalJSON() 65 | if err != nil { 66 | t.Errorf("[%d, %T] MarshalJSON() error: %v", i, test.Decoded, err) 67 | } 68 | 69 | got := string(data) 70 | if got != test.Encoded { 71 | t.Errorf("[%d, %T] MarshalJSON(): got \n%v\n\t\t want \n%v", i, test.Decoded, got, test.Encoded) 72 | } 73 | } 74 | } 75 | 76 | func TestUnmarshal(t *testing.T) { 77 | for i, test := range testCases { 78 | v1 := reflect.New(reflect.TypeOf(test.Decoded).Elem()).Interface() 79 | v := v1.(testType) 80 | 81 | err := v.UnmarshalJSON([]byte(test.Encoded)) 82 | if err != nil { 83 | t.Errorf("[%d, %T] UnmarshalJSON() error: %v", i, test.Decoded, err) 84 | } 85 | 86 | if !reflect.DeepEqual(v, test.Decoded) { 87 | t.Errorf("[%d, %T] UnmarshalJSON(): got \n%+v\n\t\t want \n%+v", i, test.Decoded, v, test.Decoded) 88 | } 89 | } 90 | } 91 | 92 | func TestRawMessageSTD(t *testing.T) { 93 | type T struct { 94 | F tinyjson.RawMessage 95 | Fnil tinyjson.RawMessage 96 | } 97 | 98 | val := T{F: tinyjson.RawMessage([]byte(`"test"`))} 99 | str := `{"F":"test","Fnil":null}` 100 | 101 | data, err := json.Marshal(val) 102 | if err != nil { 103 | t.Errorf("json.Marshal() error: %v", err) 104 | } 105 | got := string(data) 106 | if got != str { 107 | t.Errorf("json.Marshal() = %v; want %v", got, str) 108 | } 109 | 110 | wantV := T{F: tinyjson.RawMessage([]byte(`"test"`)), Fnil: tinyjson.RawMessage([]byte("null"))} 111 | var gotV T 112 | 113 | err = json.Unmarshal([]byte(str), &gotV) 114 | if err != nil { 115 | t.Errorf("json.Unmarshal() error: %v", err) 116 | } 117 | if !reflect.DeepEqual(gotV, wantV) { 118 | t.Errorf("json.Unmarshal() = %v; want %v", gotV, wantV) 119 | } 120 | } 121 | 122 | func TestParseNull(t *testing.T) { 123 | var got, want SubStruct 124 | if err := tinyjson.Unmarshal([]byte("null"), &got); err != nil { 125 | t.Errorf("Unmarshal() error: %v", err) 126 | } 127 | 128 | if !reflect.DeepEqual(got, want) { 129 | t.Errorf("Unmarshal() = %+v; want %+v", got, want) 130 | } 131 | } 132 | 133 | var testSpecialCases = []struct { 134 | EncodedString string 135 | Value string 136 | }{ 137 | {`"Username \u003cuser@example.com\u003e"`, `Username `}, 138 | {`"Username\ufffd"`, "Username\xc5"}, 139 | {`"тестzтест"`, "тестzтест"}, 140 | {`"тест\ufffdтест"`, "тест\xc5тест"}, 141 | {`"绿茶"`, "绿茶"}, 142 | {`"绿\ufffd茶"`, "绿\xc5茶"}, 143 | {`"тест\u2028"`, "тест\xE2\x80\xA8"}, 144 | {`"\\\r\n\t\""`, "\\\r\n\t\""}, 145 | {`"text\\\""`, "text\\\""}, 146 | {`"ü"`, "ü"}, 147 | } 148 | 149 | func TestSpecialCases(t *testing.T) { 150 | for i, test := range testSpecialCases { 151 | w := jwriter.Writer{} 152 | w.String(test.Value) 153 | got := string(w.Buffer.BuildBytes()) 154 | if got != test.EncodedString { 155 | t.Errorf("[%d] Encoded() = %+v; want %+v", i, got, test.EncodedString) 156 | } 157 | } 158 | } 159 | 160 | func TestOverflowArray(t *testing.T) { 161 | var a Arrays 162 | err := tinyjson.Unmarshal([]byte(arrayOverflowString), &a) 163 | if err != nil { 164 | t.Error(err) 165 | } 166 | if a != arrayValue { 167 | t.Errorf("Unmarshal(%v) = %+v; want %+v", arrayOverflowString, a, arrayValue) 168 | } 169 | } 170 | 171 | func TestUnderflowArray(t *testing.T) { 172 | var a Arrays 173 | err := tinyjson.Unmarshal([]byte(arrayUnderflowString), &a) 174 | if err != nil { 175 | t.Error(err) 176 | } 177 | if a != arrayUnderflowValue { 178 | t.Errorf("Unmarshal(%v) = %+v; want %+v", arrayUnderflowString, a, arrayUnderflowValue) 179 | } 180 | } 181 | 182 | func TestEncodingFlags(t *testing.T) { 183 | for i, test := range []struct { 184 | Flags jwriter.Flags 185 | In tinyjson.Marshaler 186 | Want string 187 | }{ 188 | {0, EncodingFlagsTestMap{}, `{"F":null}`}, 189 | {0, EncodingFlagsTestSlice{}, `{"F":null}`}, 190 | {jwriter.NilMapAsEmpty, EncodingFlagsTestMap{}, `{"F":{}}`}, 191 | {jwriter.NilSliceAsEmpty, EncodingFlagsTestSlice{}, `{"F":[]}`}, 192 | } { 193 | w := &jwriter.Writer{Flags: test.Flags} 194 | test.In.MarshalTinyJSON(w) 195 | 196 | data, err := w.BuildBytes() 197 | if err != nil { 198 | t.Errorf("[%v] tinyjson.Marshal(%+v) error: %v", i, test.In, err) 199 | } 200 | 201 | v := string(data) 202 | if v != test.Want { 203 | t.Errorf("[%v] tinyjson.Marshal(%+v) = %v; want %v", i, test.In, v, test.Want) 204 | } 205 | } 206 | 207 | } 208 | 209 | func TestNestedEasyJsonMarshal(t *testing.T) { 210 | n := map[string]*NestedEasyMarshaler{ 211 | "Value": {}, 212 | "Slice1": {}, 213 | "Slice2": {}, 214 | "Map1": {}, 215 | "Map2": {}, 216 | } 217 | 218 | ni := NestedInterfaces{ 219 | Value: n["Value"], 220 | Slice: []interface{}{n["Slice1"], n["Slice2"]}, 221 | Map: map[string]interface{}{"1": n["Map1"], "2": n["Map2"]}, 222 | } 223 | tinyjson.Marshal(ni) 224 | 225 | for k, v := range n { 226 | if !v.EasilyMarshaled { 227 | t.Errorf("Nested interface %s wasn't easily marshaled", k) 228 | } 229 | } 230 | } 231 | 232 | func TestNestedMarshaler(t *testing.T) { 233 | s := NestedMarshaler{ 234 | Value: &StructWithMarshaler{ 235 | Value: 5, 236 | }, 237 | Value2: 10, 238 | } 239 | 240 | data, err := s.MarshalJSON() 241 | if err != nil { 242 | t.Errorf("Can't marshal NestedMarshaler: %s", err) 243 | } 244 | 245 | s2 := NestedMarshaler{ 246 | Value: &StructWithMarshaler{}, 247 | } 248 | 249 | err = s2.UnmarshalJSON(data) 250 | if err != nil { 251 | t.Errorf("Can't unmarshal NestedMarshaler: %s", err) 252 | } 253 | 254 | if !reflect.DeepEqual(s2, s) { 255 | t.Errorf("tinyjson.Unmarshal() = %#v; want %#v", s2, s) 256 | } 257 | } 258 | 259 | func TestUnmarshalStructWithEmbeddedPtrStruct(t *testing.T) { 260 | var s = StructWithInterface{Field2: &EmbeddedStruct{}} 261 | var err error 262 | err = tinyjson.Unmarshal([]byte(structWithInterfaceString), &s) 263 | if err != nil { 264 | t.Errorf("tinyjson.Unmarshal() error: %v", err) 265 | } 266 | if !reflect.DeepEqual(s, structWithInterfaceValueFilled) { 267 | t.Errorf("tinyjson.Unmarshal() = %#v; want %#v", s, structWithInterfaceValueFilled) 268 | } 269 | } 270 | 271 | func TestDisallowUnknown(t *testing.T) { 272 | var d DisallowUnknown 273 | err := tinyjson.Unmarshal([]byte(disallowUnknownString), &d) 274 | if err == nil { 275 | t.Error("want error, got nil") 276 | } 277 | } 278 | 279 | var testNotGeneratedTypeCases = []interface{}{ 280 | TypeNotDeclared{}, 281 | TypeSkipped{}, 282 | } 283 | 284 | func TestMethodsNoGenerated(t *testing.T) { 285 | var ok bool 286 | for i, instance := range testNotGeneratedTypeCases { 287 | _, ok = instance.(json.Marshaler) 288 | if ok { 289 | t.Errorf("[%d, %T] Unexpected MarshalJSON()", i, instance) 290 | } 291 | 292 | _, ok = instance.(json.Unmarshaler) 293 | if ok { 294 | t.Errorf("[%d, %T] Unexpected Unmarshaler()", i, instance) 295 | } 296 | } 297 | } 298 | 299 | func TestNil(t *testing.T) { 300 | var p *PrimitiveTypes 301 | 302 | data, err := tinyjson.Marshal(p) 303 | if err != nil { 304 | t.Errorf("tinyjson.Marshal() error: %v", err) 305 | } 306 | if string(data) != "null" { 307 | t.Errorf("Wanted null, got %q", string(data)) 308 | } 309 | 310 | var b bytes.Buffer 311 | if n, err := tinyjson.MarshalToWriter(p, &b); err != nil || n != 4 { 312 | t.Errorf("tinyjson.MarshalToWriter() error: %v, written %d", err, n) 313 | } 314 | 315 | if s := b.String(); s != "null" { 316 | t.Errorf("Wanted null, got %q", s) 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /tests/custom_map_key_type.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import fmt "fmt" 4 | 5 | //tinyjson:json 6 | type CustomMapKeyType struct { 7 | Map map[customKeyType]int 8 | } 9 | 10 | type customKeyType [2]byte 11 | 12 | func (k customKeyType) MarshalJSON() ([]byte, error) { 13 | return []byte(fmt.Sprintf(`"%02x"`, k)), nil 14 | } 15 | 16 | func (k *customKeyType) UnmarshalJSON(b []byte) error { 17 | _, err := fmt.Sscanf(string(b), `"%02x%02x"`, &k[0], &k[1]) 18 | return err 19 | } 20 | 21 | var customMapKeyTypeValue CustomMapKeyType 22 | 23 | func init() { 24 | customMapKeyTypeValue.Map = map[customKeyType]int{ 25 | {0x01, 0x02}: 3, 26 | } 27 | } 28 | 29 | var customMapKeyTypeValueString = `{"Map":{"0102":3}}` 30 | -------------------------------------------------------------------------------- /tests/disallow_unknown.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type DisallowUnknown struct { 5 | FieldOne string `json:"field_one"` 6 | } 7 | 8 | var disallowUnknownString = `{"field_one": "one", "field_two": "two"}` 9 | -------------------------------------------------------------------------------- /tests/embedded_type.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type EmbeddedType struct { 5 | EmbeddedInnerType 6 | Inner struct { 7 | EmbeddedInnerType 8 | } 9 | Field2 int 10 | EmbeddedInnerType2 `json:"named"` 11 | } 12 | 13 | type EmbeddedInnerType struct { 14 | Field1 int 15 | } 16 | 17 | type EmbeddedInnerType2 struct { 18 | Field3 int 19 | } 20 | 21 | var embeddedTypeValue EmbeddedType 22 | 23 | func init() { 24 | embeddedTypeValue.Field1 = 1 25 | embeddedTypeValue.Field2 = 2 26 | embeddedTypeValue.Inner.Field1 = 3 27 | embeddedTypeValue.Field3 = 4 28 | } 29 | 30 | var embeddedTypeValueString = `{"Inner":{"Field1":3},"Field2":2,"named":{"Field3":4},"Field1":1}` 31 | -------------------------------------------------------------------------------- /tests/errors.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type ErrorIntSlice []int 5 | 6 | //tinyjson:json 7 | type ErrorBoolSlice []bool 8 | 9 | //tinyjson:json 10 | type ErrorUintSlice []uint 11 | 12 | //tinyjson:json 13 | type ErrorStruct struct { 14 | Int int `json:"int"` 15 | String string `json:"string"` 16 | Slice []int `json:"slice"` 17 | IntSlice []int `json:"int_slice"` 18 | } 19 | 20 | type ErrorNestedStruct struct { 21 | ErrorStruct ErrorStruct `json:"error_struct"` 22 | Int int `json:"int"` 23 | } 24 | 25 | //tinyjson:json 26 | type ErrorIntMap map[uint32]string 27 | -------------------------------------------------------------------------------- /tests/errors_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/CosmWasm/tinyjson/jlexer" 7 | ) 8 | 9 | func TestMultipleErrorsInt(t *testing.T) { 10 | for i, test := range []struct { 11 | Data []byte 12 | Offsets []int 13 | }{ 14 | { 15 | Data: []byte(`[1, 2, 3, "4", "5"]`), 16 | Offsets: []int{10, 15}, 17 | }, 18 | { 19 | Data: []byte(`[1, {"2":"3"}, 3, "4"]`), 20 | Offsets: []int{4, 18}, 21 | }, 22 | { 23 | Data: []byte(`[1, "2", "3", "4", "5", "6"]`), 24 | Offsets: []int{4, 9, 14, 19, 24}, 25 | }, 26 | { 27 | Data: []byte(`[1, 2, 3, 4, "5"]`), 28 | Offsets: []int{13}, 29 | }, 30 | { 31 | Data: []byte(`[{"1": "2"}]`), 32 | Offsets: []int{1}, 33 | }, 34 | } { 35 | l := jlexer.Lexer{ 36 | Data: test.Data, 37 | UseMultipleErrors: true, 38 | } 39 | 40 | var v ErrorIntSlice 41 | 42 | v.UnmarshalTinyJSON(&l) 43 | 44 | errors := l.GetNonFatalErrors() 45 | 46 | if len(errors) != len(test.Offsets) { 47 | t.Errorf("[%d] TestMultipleErrorsInt(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 48 | return 49 | } 50 | 51 | for ii, e := range errors { 52 | if e.Offset != test.Offsets[ii] { 53 | t.Errorf("[%d] TestMultipleErrorsInt(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 54 | } 55 | } 56 | } 57 | } 58 | 59 | func TestMultipleErrorsBool(t *testing.T) { 60 | for i, test := range []struct { 61 | Data []byte 62 | Offsets []int 63 | }{ 64 | { 65 | Data: []byte(`[true, false, true, false]`), 66 | }, 67 | { 68 | Data: []byte(`["test", "value", "lol", "1"]`), 69 | Offsets: []int{1, 9, 18, 25}, 70 | }, 71 | { 72 | Data: []byte(`[true, 42, {"a":"b", "c":"d"}, false]`), 73 | Offsets: []int{7, 11}, 74 | }, 75 | } { 76 | l := jlexer.Lexer{ 77 | Data: test.Data, 78 | UseMultipleErrors: true, 79 | } 80 | 81 | var v ErrorBoolSlice 82 | v.UnmarshalTinyJSON(&l) 83 | 84 | errors := l.GetNonFatalErrors() 85 | 86 | if len(errors) != len(test.Offsets) { 87 | t.Errorf("[%d] TestMultipleErrorsBool(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 88 | return 89 | } 90 | for ii, e := range errors { 91 | if e.Offset != test.Offsets[ii] { 92 | t.Errorf("[%d] TestMultipleErrorsBool(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 93 | } 94 | } 95 | } 96 | } 97 | 98 | func TestMultipleErrorsUint(t *testing.T) { 99 | for i, test := range []struct { 100 | Data []byte 101 | Offsets []int 102 | }{ 103 | { 104 | Data: []byte(`[42, 42, 42]`), 105 | }, 106 | { 107 | Data: []byte(`[17, "42", 32]`), 108 | Offsets: []int{5}, 109 | }, 110 | { 111 | Data: []byte(`["zz", "zz"]`), 112 | Offsets: []int{1, 7}, 113 | }, 114 | { 115 | Data: []byte(`[{}, 42]`), 116 | Offsets: []int{1}, 117 | }, 118 | } { 119 | l := jlexer.Lexer{ 120 | Data: test.Data, 121 | UseMultipleErrors: true, 122 | } 123 | 124 | var v ErrorUintSlice 125 | v.UnmarshalTinyJSON(&l) 126 | 127 | errors := l.GetNonFatalErrors() 128 | 129 | if len(errors) != len(test.Offsets) { 130 | t.Errorf("[%d] TestMultipleErrorsUint(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 131 | return 132 | } 133 | for ii, e := range errors { 134 | if e.Offset != test.Offsets[ii] { 135 | t.Errorf("[%d] TestMultipleErrorsUint(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 136 | } 137 | } 138 | } 139 | } 140 | 141 | func TestMultipleErrorsStruct(t *testing.T) { 142 | for i, test := range []struct { 143 | Data []byte 144 | Offsets []int 145 | }{ 146 | { 147 | Data: []byte(`{"string": "test", "slice":[42, 42, 42], "int_slice":[1, 2, 3]}`), 148 | }, 149 | { 150 | Data: []byte(`{"string": {"test": "test"}, "slice":[42, 42, 42], "int_slice":["1", 2, 3]}`), 151 | Offsets: []int{11, 64}, 152 | }, 153 | { 154 | Data: []byte(`{"slice": [42, 42], "string": {"test": "test"}, "int_slice":["1", "2", 3]}`), 155 | Offsets: []int{30, 61, 66}, 156 | }, 157 | { 158 | Data: []byte(`{"string": "test", "slice": {}}`), 159 | Offsets: []int{28}, 160 | }, 161 | { 162 | Data: []byte(`{"slice":5, "string" : "test"}`), 163 | Offsets: []int{9}, 164 | }, 165 | { 166 | Data: []byte(`{"slice" : "test", "string" : "test"}`), 167 | Offsets: []int{11}, 168 | }, 169 | { 170 | Data: []byte(`{"slice": "", "string" : {}, "int":{}}`), 171 | Offsets: []int{10, 25, 35}, 172 | }, 173 | } { 174 | l := jlexer.Lexer{ 175 | Data: test.Data, 176 | UseMultipleErrors: true, 177 | } 178 | var v ErrorStruct 179 | v.UnmarshalTinyJSON(&l) 180 | 181 | errors := l.GetNonFatalErrors() 182 | 183 | if len(errors) != len(test.Offsets) { 184 | t.Errorf("[%d] TestMultipleErrorsStruct(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 185 | return 186 | } 187 | for ii, e := range errors { 188 | if e.Offset != test.Offsets[ii] { 189 | t.Errorf("[%d] TestMultipleErrorsStruct(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 190 | } 191 | } 192 | } 193 | } 194 | 195 | func TestMultipleErrorsNestedStruct(t *testing.T) { 196 | for i, test := range []struct { 197 | Data []byte 198 | Offsets []int 199 | }{ 200 | { 201 | Data: []byte(`{"error_struct":{}}`), 202 | }, 203 | { 204 | Data: []byte(`{"error_struct":5}`), 205 | Offsets: []int{16}, 206 | }, 207 | { 208 | Data: []byte(`{"error_struct":[]}`), 209 | Offsets: []int{16}, 210 | }, 211 | { 212 | Data: []byte(`{"error_struct":{"int":{}}}`), 213 | Offsets: []int{23}, 214 | }, 215 | { 216 | Data: []byte(`{"error_struct":{"int_slice":{}}, "int":4}`), 217 | Offsets: []int{29}, 218 | }, 219 | { 220 | Data: []byte(`{"error_struct":{"int_slice":["1", 2, "3"]}, "int":[]}`), 221 | Offsets: []int{30, 38, 51}, 222 | }, 223 | } { 224 | l := jlexer.Lexer{ 225 | Data: test.Data, 226 | UseMultipleErrors: true, 227 | } 228 | var v ErrorNestedStruct 229 | v.UnmarshalTinyJSON(&l) 230 | 231 | errors := l.GetNonFatalErrors() 232 | 233 | if len(errors) != len(test.Offsets) { 234 | t.Errorf("[%d] TestMultipleErrorsNestedStruct(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 235 | return 236 | } 237 | for ii, e := range errors { 238 | if e.Offset != test.Offsets[ii] { 239 | t.Errorf("[%d] TestMultipleErrorsNestedStruct(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 240 | } 241 | } 242 | } 243 | } 244 | 245 | func TestMultipleErrorsIntMap(t *testing.T) { 246 | for i, test := range []struct { 247 | Data []byte 248 | Offsets []int 249 | }{ 250 | { 251 | Data: []byte(`{"a":"NumErr"}`), 252 | Offsets: []int{1}, 253 | }, 254 | { 255 | Data: []byte(`{"":"ErrSyntax"}`), 256 | Offsets: []int{1}, 257 | }, 258 | { 259 | Data: []byte(`{"a":"NumErr","33147483647":"ErrRange","-1":"ErrRange"}`), 260 | Offsets: []int{1, 14, 39}, 261 | }, 262 | } { 263 | l := jlexer.Lexer{ 264 | Data: test.Data, 265 | UseMultipleErrors: true, 266 | } 267 | 268 | var v ErrorIntMap 269 | 270 | v.UnmarshalTinyJSON(&l) 271 | 272 | errors := l.GetNonFatalErrors() 273 | 274 | if len(errors) != len(test.Offsets) { 275 | t.Errorf("[%d] TestMultipleErrorsInt(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) 276 | return 277 | } 278 | 279 | for ii, e := range errors { 280 | if e.Offset != test.Offsets[ii] { 281 | t.Errorf("[%d] TestMultipleErrorsInt(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) 282 | } 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /tests/escaping.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type EscStringStruct struct { 5 | A string `json:"a"` 6 | } 7 | 8 | //tinyjson:json 9 | type EscIntStruct struct { 10 | A int `json:"a,string"` 11 | } 12 | -------------------------------------------------------------------------------- /tests/escaping_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/CosmWasm/tinyjson" 8 | ) 9 | 10 | func TestStrFieldsUnescaping(t *testing.T) { 11 | cases := []struct { 12 | data string 13 | exp EscStringStruct 14 | }{ 15 | { 16 | data: `{}`, 17 | exp: EscStringStruct{}, 18 | }, 19 | { 20 | data: `{"a": "\""}`, 21 | exp: EscStringStruct{A: `"`}, 22 | }, 23 | { 24 | data: `{"a": "\\"}`, 25 | exp: EscStringStruct{A: `\`}, 26 | }, 27 | { 28 | data: `{"a": "\\\""}`, 29 | exp: EscStringStruct{A: `\"`}, 30 | }, 31 | { 32 | data: `{"a": "\\\\'"}`, 33 | exp: EscStringStruct{A: `\\'`}, 34 | }, 35 | { 36 | data: `{"a": "\t\\\nx\\\""}`, 37 | exp: EscStringStruct{A: "\t\\\nx\\\""}, 38 | }, 39 | { 40 | data: `{"a": "\r\n"}`, 41 | exp: EscStringStruct{A: "\r\n"}, 42 | }, 43 | { 44 | data: `{"a": "\r\n\u4e2D\u56fD\\\""}`, 45 | exp: EscStringStruct{A: "\r\n中国\\\""}, 46 | }, 47 | } 48 | 49 | for i, c := range cases { 50 | var val EscStringStruct 51 | err := tinyjson.Unmarshal([]byte(c.data), &val) 52 | if err != nil { 53 | t.Error(err) 54 | } 55 | if !reflect.DeepEqual(val, c.exp) { 56 | t.Errorf("[%d] TestStrFieldsUnescaping(): got=%q, exp=%q", i, val, c.exp) 57 | } 58 | } 59 | } 60 | 61 | func TestIntFieldsUnescaping(t *testing.T) { 62 | cases := []struct { 63 | data string 64 | exp EscIntStruct 65 | }{ 66 | { 67 | data: `{}`, 68 | exp: EscIntStruct{A: 0}, 69 | }, 70 | { 71 | data: `{"a": "1"}`, 72 | exp: EscIntStruct{A: 1}, 73 | }, 74 | { 75 | data: `{"a": "\u0032"}`, 76 | exp: EscIntStruct{A: 2}, 77 | }, 78 | } 79 | 80 | for i, c := range cases { 81 | var val EscIntStruct 82 | err := tinyjson.Unmarshal([]byte(c.data), &val) 83 | if err != nil { 84 | t.Error(err) 85 | } 86 | if !reflect.DeepEqual(val, c.exp) { 87 | t.Errorf("[%d] TestIntFieldsUnescaping(): got=%v, exp=%v", i, val, c.exp) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/html.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | type Struct struct { 4 | Test string 5 | } 6 | -------------------------------------------------------------------------------- /tests/html_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/CosmWasm/tinyjson/jwriter" 7 | ) 8 | 9 | func TestHTML(t *testing.T) { 10 | s := Struct{ 11 | Test: "test", 12 | } 13 | 14 | j := jwriter.Writer{ 15 | NoEscapeHTML: false, 16 | } 17 | s.MarshalTinyJSON(&j) 18 | 19 | data, _ := j.BuildBytes() 20 | 21 | if string(data) != `{"Test":"\u003cb\u003etest\u003c/b\u003e"}` { 22 | t.Fatal("EscapeHTML error:", string(data)) 23 | } 24 | 25 | j.NoEscapeHTML = true 26 | s.MarshalTinyJSON(&j) 27 | 28 | data, _ = j.BuildBytes() 29 | 30 | if string(data) != `{"Test":"test"}` { 31 | t.Fatal("NoEscapeHTML error:", string(data)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/intern.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type NoIntern struct { 5 | Field string `json:"field"` 6 | } 7 | 8 | //tinyjson:json 9 | type Intern struct { 10 | Field string `json:"field,intern"` 11 | } 12 | 13 | var intern = Intern{Field: "interned"} 14 | var internString = `{"field":"interned"}` 15 | -------------------------------------------------------------------------------- /tests/intern_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/CosmWasm/tinyjson" 7 | ) 8 | 9 | func TestStringIntern(t *testing.T) { 10 | data := []byte(`{"field": "string interning test"}`) 11 | 12 | var i Intern 13 | allocsPerRun := testing.AllocsPerRun(1000, func() { 14 | i = Intern{} 15 | err := tinyjson.Unmarshal(data, &i) 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | if i.Field != "string interning test" { 20 | t.Fatalf("wrong value: %q", i.Field) 21 | } 22 | }) 23 | if allocsPerRun != 0 { 24 | t.Fatalf("expected 0 allocs, got %f", allocsPerRun) 25 | } 26 | 27 | var n NoIntern 28 | allocsPerRun = testing.AllocsPerRun(1000, func() { 29 | n = NoIntern{} 30 | err := tinyjson.Unmarshal(data, &n) 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | if n.Field != "string interning test" { 35 | t.Fatalf("wrong value: %q", n.Field) 36 | } 37 | }) 38 | if allocsPerRun != 1 { 39 | t.Fatalf("expected 1 allocs, got %f", allocsPerRun) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/key_marshaler_map.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | type KeyWithEncodingMarshaler int 4 | 5 | func (f KeyWithEncodingMarshaler) MarshalText() (text []byte, err error) { 6 | return []byte("hello"), nil 7 | } 8 | 9 | func (f *KeyWithEncodingMarshaler) UnmarshalText(text []byte) error { 10 | if string(text) == "hello" { 11 | *f = 5 12 | } 13 | return nil 14 | } 15 | 16 | //tinyjson:json 17 | type KeyWithEncodingMarshalers map[KeyWithEncodingMarshaler]string 18 | 19 | var mapWithEncodingMarshaler KeyWithEncodingMarshalers = KeyWithEncodingMarshalers{5: "hello"} 20 | var mapWithEncodingMarshalerString = `{"hello":"hello"}` 21 | -------------------------------------------------------------------------------- /tests/members_escaped.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type MembersEscaped struct { 5 | A string `json:"漢語"` 6 | } 7 | -------------------------------------------------------------------------------- /tests/members_escaping_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/CosmWasm/tinyjson" 8 | ) 9 | 10 | func TestMembersEscaping(t *testing.T) { 11 | cases := []struct { 12 | data string 13 | esc MembersEscaped 14 | unesc MembersUnescaped 15 | }{ 16 | { 17 | data: `{"漢語": "中国"}`, 18 | esc: MembersEscaped{A: "中国"}, 19 | unesc: MembersUnescaped{A: "中国"}, 20 | }, 21 | { 22 | data: `{"漢語": "\u4e2D\u56fD"}`, 23 | esc: MembersEscaped{A: "中国"}, 24 | unesc: MembersUnescaped{A: "中国"}, 25 | }, 26 | { 27 | data: `{"\u6f22\u8a9E": "中国"}`, 28 | esc: MembersEscaped{A: "中国"}, 29 | unesc: MembersUnescaped{A: ""}, 30 | }, 31 | { 32 | data: `{"\u6f22\u8a9E": "\u4e2D\u56fD"}`, 33 | esc: MembersEscaped{A: "中国"}, 34 | unesc: MembersUnescaped{A: ""}, 35 | }, 36 | } 37 | 38 | for i, c := range cases { 39 | var esc MembersEscaped 40 | err := tinyjson.Unmarshal([]byte(c.data), &esc) 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | if !reflect.DeepEqual(esc, c.esc) { 45 | t.Errorf("[%d] TestMembersEscaping(): got=%+v, exp=%+v", i, esc, c.esc) 46 | } 47 | 48 | var unesc MembersUnescaped 49 | err = tinyjson.Unmarshal([]byte(c.data), &unesc) 50 | if err != nil { 51 | t.Error(err) 52 | } 53 | if !reflect.DeepEqual(unesc, c.unesc) { 54 | t.Errorf("[%d] TestMembersEscaping(): no-unescaping case: got=%+v, exp=%+v", i, esc, c.esc) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/members_unescaped.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type MembersUnescaped struct { 5 | A string `json:"漢語"` 6 | } 7 | -------------------------------------------------------------------------------- /tests/named_type.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type NamedType struct { 5 | Inner struct { 6 | // tinyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output 7 | // something about a named type inside an anonmymous type is triggering this bug 8 | Field MyString `tag:"value"` 9 | Field2 int "tag:\"value with ` in it\"" 10 | } 11 | } 12 | 13 | type MyString string 14 | 15 | var namedTypeValue NamedType 16 | 17 | func init() { 18 | namedTypeValue.Inner.Field = "test" 19 | namedTypeValue.Inner.Field2 = 123 20 | } 21 | 22 | var namedTypeValueString = `{"Inner":{"Field":"test","Field2":123}}` 23 | -------------------------------------------------------------------------------- /tests/nested_easy.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/CosmWasm/tinyjson" 5 | "github.com/CosmWasm/tinyjson/jwriter" 6 | ) 7 | 8 | //tinyjson:json 9 | type NestedInterfaces struct { 10 | Value interface{} 11 | Slice []interface{} 12 | Map map[string]interface{} 13 | } 14 | 15 | type NestedEasyMarshaler struct { 16 | EasilyMarshaled bool 17 | } 18 | 19 | var _ tinyjson.Marshaler = &NestedEasyMarshaler{} 20 | 21 | func (i *NestedEasyMarshaler) MarshalTinyJSON(w *jwriter.Writer) { 22 | // We use this method only to indicate that tinyjson.Marshaler 23 | // interface was really used while encoding. 24 | i.EasilyMarshaled = true 25 | } 26 | -------------------------------------------------------------------------------- /tests/nested_marshaler.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/CosmWasm/tinyjson" 5 | "github.com/CosmWasm/tinyjson/jlexer" 6 | "github.com/CosmWasm/tinyjson/jwriter" 7 | ) 8 | 9 | //tinyjson:json 10 | type NestedMarshaler struct { 11 | Value tinyjson.MarshalerUnmarshaler 12 | Value2 int 13 | } 14 | 15 | type StructWithMarshaler struct { 16 | Value int 17 | } 18 | 19 | func (s *StructWithMarshaler) UnmarshalTinyJSON(w *jlexer.Lexer) { 20 | s.Value = w.Int() 21 | } 22 | 23 | func (s *StructWithMarshaler) MarshalTinyJSON(w *jwriter.Writer) { 24 | w.Int(s.Value) 25 | } 26 | -------------------------------------------------------------------------------- /tests/nocopy.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type NocopyStruct struct { 5 | A string `json:"a"` 6 | B string `json:"b,nocopy"` 7 | } 8 | -------------------------------------------------------------------------------- /tests/nocopy_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "unsafe" 7 | 8 | "github.com/CosmWasm/tinyjson" 9 | ) 10 | 11 | // verifies if string pointer belongs to the given buffer or outside of it 12 | func strBelongsTo(s string, buf []byte) bool { 13 | sPtr := (*reflect.StringHeader)(unsafe.Pointer(&s)).Data 14 | bufPtr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data 15 | 16 | if bufPtr <= sPtr && sPtr < bufPtr+uintptr(len(buf)) { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | func TestNocopy(t *testing.T) { 23 | data := []byte(`{"a": "valueA", "b": "valueB"}`) 24 | exp := NocopyStruct{ 25 | A: "valueA", 26 | B: "valueB", 27 | } 28 | res := NocopyStruct{} 29 | 30 | err := tinyjson.Unmarshal(data, &res) 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | if !reflect.DeepEqual(exp, res) { 35 | t.Errorf("TestNocopy(): got=%+v, exp=%+v", res, exp) 36 | } 37 | 38 | if strBelongsTo(res.A, data) { 39 | t.Error("TestNocopy(): field A was not copied and refers to buffer") 40 | } 41 | if !strBelongsTo(res.B, data) { 42 | t.Error("TestNocopy(): field B was copied rather than refer to bufferr") 43 | } 44 | 45 | data = []byte(`{"b": "valueNoCopy"}`) 46 | res = NocopyStruct{} 47 | allocsPerRun := testing.AllocsPerRun(1000, func() { 48 | err := tinyjson.Unmarshal(data, &res) 49 | if err != nil { 50 | t.Error(err) 51 | } 52 | if res.B != "valueNoCopy" { 53 | t.Fatalf("wrong value: %q", res.B) 54 | } 55 | }) 56 | if allocsPerRun != 0 { 57 | t.Fatalf("noCopy field unmarshal: expected 0 allocs, got %f", allocsPerRun) 58 | } 59 | 60 | data = []byte(`{"a": "valueNoCopy"}`) 61 | allocsPerRun = testing.AllocsPerRun(1000, func() { 62 | err := tinyjson.Unmarshal(data, &res) 63 | if err != nil { 64 | t.Error(err) 65 | } 66 | if res.A != "valueNoCopy" { 67 | t.Fatalf("wrong value: %q", res.A) 68 | } 69 | }) 70 | if allocsPerRun != 1 { 71 | t.Fatalf("copy field unmarshal: expected 1 allocs, got %f", allocsPerRun) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/nothing.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | // No structs in this file 4 | -------------------------------------------------------------------------------- /tests/omitempty.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type OmitEmptyDefault struct { 5 | Field string 6 | Str string 7 | Str1 string `json:"s,!omitempty"` 8 | Str2 string `json:",!omitempty"` 9 | } 10 | 11 | var omitEmptyDefaultValue = OmitEmptyDefault{Field: "test"} 12 | var omitEmptyDefaultString = `{"Field":"test","s":"","Str2":""}` 13 | -------------------------------------------------------------------------------- /tests/opt_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | "testing" 7 | 8 | "encoding/json" 9 | 10 | "github.com/CosmWasm/tinyjson/opt" 11 | ) 12 | 13 | // This struct type must NOT have a generated marshaler 14 | type OptsVanilla struct { 15 | Int opt.Int 16 | Uint opt.Uint 17 | 18 | Int8 opt.Int8 19 | Int16 opt.Int16 20 | Int32 opt.Int32 21 | Int64 opt.Int64 22 | 23 | Uint8 opt.Uint8 24 | Uint16 opt.Uint16 25 | Uint32 opt.Uint32 26 | Uint64 opt.Uint64 27 | 28 | Bool opt.Bool 29 | String opt.String 30 | } 31 | 32 | var optsVanillaValue = OptsVanilla{ 33 | Int: opt.OInt(-123), 34 | Uint: opt.OUint(123), 35 | 36 | Int8: opt.OInt8(math.MaxInt8), 37 | Int16: opt.OInt16(math.MaxInt16), 38 | Int32: opt.OInt32(math.MaxInt32), 39 | Int64: opt.OInt64(math.MaxInt64), 40 | 41 | Uint8: opt.OUint8(math.MaxUint8), 42 | Uint16: opt.OUint16(math.MaxUint16), 43 | Uint32: opt.OUint32(math.MaxUint32), 44 | Uint64: opt.OUint64(math.MaxUint64), 45 | 46 | Bool: opt.OBool(true), 47 | String: opt.OString("foo"), 48 | } 49 | 50 | func TestOptsVanilla(t *testing.T) { 51 | data, err := json.Marshal(optsVanillaValue) 52 | if err != nil { 53 | t.Errorf("Failed to marshal vanilla opts: %v", err) 54 | } 55 | 56 | var ov OptsVanilla 57 | if err := json.Unmarshal(data, &ov); err != nil { 58 | t.Errorf("Failed to unmarshal vanilla opts: %v", err) 59 | } 60 | 61 | if !reflect.DeepEqual(optsVanillaValue, ov) { 62 | t.Errorf("Vanilla opts unmarshal returned invalid value %+v, want %+v", ov, optsVanillaValue) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/reference_to_pointer.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | type Struct1 struct { 4 | } 5 | 6 | //tinyjson:json 7 | type Struct2 struct { 8 | From *Struct1 `json:"from,omitempty"` 9 | Through *Struct1 `json:"through,omitempty"` 10 | } 11 | -------------------------------------------------------------------------------- /tests/required_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestRequiredField(t *testing.T) { 10 | cases := []struct{ json, errorMessage string }{ 11 | {`{"first_name":"Foo", "last_name": "Bar"}`, ""}, 12 | {`{"last_name":"Bar"}`, "key 'first_name' is required"}, 13 | {"{}", "key 'first_name' is required"}, 14 | } 15 | 16 | for _, tc := range cases { 17 | var v RequiredOptionalStruct 18 | err := v.UnmarshalJSON([]byte(tc.json)) 19 | if tc.errorMessage == "" { 20 | if err != nil { 21 | t.Errorf("%s. UnmarshalJSON didn`t expect error: %v", tc.json, err) 22 | } 23 | } else { 24 | if fmt.Sprintf("%v", err) != tc.errorMessage { 25 | t.Errorf("%s. UnmarshalJSON expected error: %v. got: %v", tc.json, tc.errorMessage, err) 26 | } 27 | } 28 | } 29 | } 30 | 31 | func TestRequiredOptionalMap(t *testing.T) { 32 | baseJson := `{"req_map":{}, "oe_map":{}, "noe_map":{}, "oe_slice":[]}` 33 | wantDecoding := RequiredOptionalMap{MapIntString{}, nil, MapIntString{}} 34 | 35 | var v RequiredOptionalMap 36 | if err := v.UnmarshalJSON([]byte(baseJson)); err != nil { 37 | t.Errorf("%s. UnmarshalJSON didn't expect error: %v", baseJson, err) 38 | } 39 | if !reflect.DeepEqual(v, wantDecoding) { 40 | t.Errorf("%s. UnmarshalJSON expected to gen: %v. got: %v", baseJson, wantDecoding, v) 41 | } 42 | 43 | baseStruct := RequiredOptionalMap{MapIntString{}, MapIntString{}, MapIntString{}} 44 | wantJson := `{"req_map":{},"noe_map":{}}` 45 | data, err := baseStruct.MarshalJSON() 46 | if err != nil { 47 | t.Errorf("MarshalJSON didn't expect error: %v on %v", err, data) 48 | } else if string(data) != wantJson { 49 | t.Errorf("%v. MarshalJSON wanted: %s got %s", baseStruct, wantJson, string(data)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/snake.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type SnakeStruct struct { 5 | WeirdHTTPStuff bool 6 | CustomNamedField string `json:"cUsToM"` 7 | } 8 | 9 | var snakeStructValue SnakeStruct 10 | var snakeStructString = `{"weird_http_stuff":false,"cUsToM":""}` 11 | -------------------------------------------------------------------------------- /tests/type_declaration.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:json 4 | type ( 5 | GenDeclared1 struct { 6 | Value string 7 | } 8 | 9 | // A gen declared tinyjson struct with a comment 10 | GenDeclaredWithComment struct { 11 | Value string 12 | } 13 | ) 14 | 15 | type ( 16 | //tinyjson:json 17 | TypeDeclared struct { 18 | Value string 19 | } 20 | 21 | TypeNotDeclared struct { 22 | Value string 23 | } 24 | ) 25 | 26 | var ( 27 | myGenDeclaredValue = TypeDeclared{Value: "GenDeclared"} 28 | myGenDeclaredString = `{"Value":"GenDeclared"}` 29 | myGenDeclaredWithCommentValue = TypeDeclared{Value: "GenDeclaredWithComment"} 30 | myGenDeclaredWithCommentString = `{"Value":"GenDeclaredWithComment"}` 31 | myTypeDeclaredValue = TypeDeclared{Value: "TypeDeclared"} 32 | myTypeDeclaredString = `{"Value":"TypeDeclared"}` 33 | ) 34 | -------------------------------------------------------------------------------- /tests/type_declaration_skip.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | //tinyjson:skip 4 | type TypeSkipped struct { 5 | Value string 6 | } 7 | 8 | type TypeNotSkipped struct { 9 | Value string 10 | } 11 | 12 | var ( 13 | myTypeNotSkippedValue = TypeDeclared{Value: "TypeNotSkipped"} 14 | myTypeNotSkippedString = `{"Value":"TypeNotSkipped"}` 15 | ) 16 | -------------------------------------------------------------------------------- /tests/unknown_fields.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import "github.com/CosmWasm/tinyjson" 4 | 5 | //tinyjson:json 6 | type StructWithUnknownsProxy struct { 7 | tinyjson.UnknownFieldsProxy 8 | 9 | Field1 string 10 | } 11 | 12 | //tinyjson:json 13 | type StructWithUnknownsProxyWithOmitempty struct { 14 | tinyjson.UnknownFieldsProxy 15 | 16 | Field1 string `json:",omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /tests/unknown_fields_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestUnknownFieldsProxy(t *testing.T) { 9 | baseJson := `{"Field1":"123","Field2":"321"}` 10 | 11 | s := StructWithUnknownsProxy{} 12 | 13 | err := s.UnmarshalJSON([]byte(baseJson)) 14 | if err != nil { 15 | t.Errorf("UnmarshalJSON didn't expect error: %v", err) 16 | } 17 | 18 | if s.Field1 != "123" { 19 | t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1) 20 | } 21 | 22 | data, err := s.MarshalJSON() 23 | if err != nil { 24 | t.Errorf("MarshalJSON didn't expect error: %v", err) 25 | } 26 | 27 | if !reflect.DeepEqual(baseJson, string(data)) { 28 | t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data)) 29 | } 30 | } 31 | 32 | func TestUnknownFieldsProxyWithOmitempty(t *testing.T) { 33 | baseJson := `{"Field1":"123","Field2":"321"}` 34 | 35 | s := StructWithUnknownsProxyWithOmitempty{} 36 | 37 | err := s.UnmarshalJSON([]byte(baseJson)) 38 | if err != nil { 39 | t.Errorf("UnmarshalJSON didn't expect error: %v", err) 40 | } 41 | 42 | if s.Field1 != "123" { 43 | t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1) 44 | } 45 | 46 | data, err := s.MarshalJSON() 47 | if err != nil { 48 | t.Errorf("MarshalJSON didn't expect error: %v", err) 49 | } 50 | 51 | if !reflect.DeepEqual(baseJson, string(data)) { 52 | t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data)) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tiny-tests/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package tinytest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkStd_Unmarshal_Env(b *testing.B) { 8 | for i := 0; i < b.N; i++ { 9 | var s Env 10 | err := s.UnmarshalJSON(sampleEnvText) 11 | if err != nil { 12 | b.Error(err) 13 | } 14 | } 15 | } 16 | 17 | func BenchmarkStd_Unmarshal_Info(b *testing.B) { 18 | for i := 0; i < b.N; i++ { 19 | var s MessageInfo 20 | err := s.UnmarshalJSON(sampleMsgInfoText) 21 | if err != nil { 22 | b.Error(err) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tiny-tests/cosmwasm.go: -------------------------------------------------------------------------------- 1 | package tinytest 2 | 3 | // TODO: investigate nocopy optimizations 4 | 5 | // basic, standard struct (with embedded structs) 6 | type Env struct { 7 | Block BlockInfo 8 | Contract ContractInfo 9 | } 10 | 11 | type BlockInfo struct { 12 | Height int64 13 | Time int64 `json:",string"` 14 | } 15 | 16 | type ContractInfo struct { 17 | Address string 18 | } 19 | 20 | // another important struct that includes a slice of structs (which caused issues with another parser) 21 | type MessageInfo struct { 22 | Signer string 23 | // Note: added custom tag "emptyslice" to never encode to nil, but rather [] 24 | Funds []Coin `json:",emptyslice"` 25 | } 26 | 27 | type Coin struct { 28 | Denom string 29 | Amount string 30 | } 31 | 32 | // emulate Rust enum, only one should ever be set 33 | type ExecuteMsg struct { 34 | Deposit *DepositMsg `json:",omitempty"` 35 | Withdraw *WithdrawMsg `json:",omitempty"` 36 | } 37 | type DepositMsg struct { 38 | ToAccount string 39 | Amount string 40 | } 41 | 42 | // withdraws all funds 43 | type WithdrawMsg struct { 44 | // use a different field here to ensure we have proper types (json must not overlap) 45 | FromAccount string 46 | } 47 | 48 | /**** Test Helpers ****/ 49 | 50 | // For Testing 51 | func (a *MessageInfo) Equals(b *MessageInfo) bool { 52 | if a.Signer != b.Signer { 53 | return false 54 | } 55 | if len(a.Funds) != len(b.Funds) { 56 | return false 57 | } 58 | for i, af := range a.Funds { 59 | bf := b.Funds[i] 60 | if af != bf { 61 | return false 62 | } 63 | } 64 | return true 65 | } 66 | 67 | // For Testing 68 | func (a *ExecuteMsg) Equals(b *ExecuteMsg) bool { 69 | if a.Deposit == nil && b.Deposit != nil { 70 | return false 71 | } 72 | if a.Deposit != nil { 73 | if b.Deposit == nil { 74 | return false 75 | } 76 | if *a.Deposit != *b.Deposit { 77 | return false 78 | } 79 | } 80 | 81 | if a.Withdraw == nil && b.Withdraw != nil { 82 | return false 83 | } 84 | if a.Withdraw != nil { 85 | if b.Withdraw == nil { 86 | return false 87 | } 88 | if *a.Withdraw != *b.Withdraw { 89 | return false 90 | } 91 | } 92 | 93 | return true 94 | } 95 | -------------------------------------------------------------------------------- /tiny-tests/cosmwasm_test.go: -------------------------------------------------------------------------------- 1 | package tinytest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // Encode and decode types 8 | func TestEnvEncoding(t *testing.T) { 9 | cases := map[string]struct { 10 | input string 11 | expected Env 12 | output string 13 | }{ 14 | "empty": { 15 | input: `{"contract":{}}`, 16 | expected: Env{}, 17 | output: `{"block":{"height":0,"time":"0"},"contract":{"address":""}}`, 18 | }, 19 | "full": { 20 | input: `{"contract":{"address":"my-contract"},"block":{"time":"1234567890","height":42}}`, 21 | expected: Env{ 22 | Contract: ContractInfo{Address: "my-contract"}, 23 | Block: BlockInfo{Time: 1234567890, Height: 42}, 24 | }, 25 | output: `{"block":{"height":42,"time":"1234567890"},"contract":{"address":"my-contract"}}`, 26 | }, 27 | } 28 | 29 | for name, tc := range cases { 30 | t.Run(name, func(t *testing.T) { 31 | // check it matches expectations 32 | var loaded Env 33 | err := loaded.UnmarshalJSON([]byte(tc.input)) 34 | if err != nil { 35 | t.Fatalf("Unmarshaling: %s", err) 36 | } 37 | if loaded != tc.expected { 38 | t.Fatalf("Unmarshaled doesn't match expected.\nGot %#v\nWanted %#v", loaded, tc.expected) 39 | } 40 | 41 | // check proper output 42 | bz, err := loaded.MarshalJSON() 43 | if err != nil { 44 | t.Fatalf("Marshalling: %s", err) 45 | } 46 | if string(bz) != tc.output { 47 | t.Fatalf("Unexpected output: '%s'", string(bz)) 48 | } 49 | }) 50 | } 51 | } 52 | 53 | func TestInfoEncoding(t *testing.T) { 54 | cases := map[string]struct { 55 | input string 56 | expected MessageInfo 57 | output string 58 | }{ 59 | "empty": { 60 | input: `{}`, 61 | expected: MessageInfo{}, 62 | output: `{"signer":"","funds":[]}`, 63 | }, 64 | "top fields": { 65 | input: `{"signer":"cosmos1234","funds":[]}`, 66 | expected: MessageInfo{ 67 | Signer: "cosmos1234", 68 | Funds: []Coin{}, 69 | }, 70 | output: `{"signer":"cosmos1234","funds":[]}`, 71 | }, 72 | "multiple coins": { 73 | input: `{"signer":"cosmos1234","funds":[{"amount":"12345","denom":"uatom"},{"amount":"76543","denom":"utgd"}]}`, 74 | expected: MessageInfo{ 75 | Signer: "cosmos1234", 76 | Funds: []Coin{{ 77 | Amount: "12345", 78 | Denom: "uatom", 79 | }, { 80 | Amount: "76543", 81 | Denom: "utgd", 82 | }}, 83 | }, 84 | output: `{"signer":"cosmos1234","funds":[{"denom":"uatom","amount":"12345"},{"denom":"utgd","amount":"76543"}]}`, 85 | }, 86 | } 87 | 88 | for name, tc := range cases { 89 | t.Run(name, func(t *testing.T) { 90 | // check it matches expectations 91 | var loaded MessageInfo 92 | err := loaded.UnmarshalJSON([]byte(tc.input)) 93 | if err != nil { 94 | t.Fatalf("Unmarshaling: %s", err) 95 | } 96 | if !loaded.Equals(&tc.expected) { 97 | t.Fatalf("Unmarshaled doesn't match expected.\nGot %#v\nWanted %#v", loaded, tc.expected) 98 | } 99 | 100 | // check it matches itself 101 | bz, err := loaded.MarshalJSON() 102 | if err != nil { 103 | t.Fatalf("Marshalling: %s", err) 104 | } 105 | if string(bz) != tc.output { 106 | t.Fatalf("Unexpected output: '%s'", string(bz)) 107 | } 108 | }) 109 | } 110 | } 111 | 112 | func TestMessageEncoding(t *testing.T) { 113 | cases := map[string]struct { 114 | input string 115 | expected ExecuteMsg 116 | output string 117 | }{ 118 | "deposit": { 119 | input: `{"deposit":{"to_account":"cosmos1234567","amount":"1865"}}`, 120 | expected: ExecuteMsg{Deposit: &DepositMsg{ToAccount: "cosmos1234567", Amount: "1865"}}, 121 | output: `{"deposit":{"to_account":"cosmos1234567","amount":"1865"}}`, 122 | }, 123 | "withdraw": { 124 | input: `{"withdraw":{"from_account":"wasm1542"}}`, 125 | expected: ExecuteMsg{Withdraw: &WithdrawMsg{FromAccount: "wasm1542"}}, 126 | output: `{"withdraw":{"from_account":"wasm1542"}}`, 127 | }, 128 | } 129 | 130 | for name, tc := range cases { 131 | t.Run(name, func(t *testing.T) { 132 | // check it matches expectations 133 | var loaded ExecuteMsg 134 | err := loaded.UnmarshalJSON([]byte(tc.input)) 135 | if err != nil { 136 | t.Fatalf("Unmarshaling: %s", err) 137 | } 138 | if !loaded.Equals(&tc.expected) { 139 | t.Fatalf("Unmarshaled doesn't match expected.\nGot %#v\nWanted %#v", loaded, tc.expected) 140 | } 141 | 142 | // check it matches itself 143 | bz, err := loaded.MarshalJSON() 144 | if err != nil { 145 | t.Fatalf("Marshalling: %s", err) 146 | } 147 | if string(bz) != tc.output { 148 | t.Fatalf("Unexpected output: '%s'", string(bz)) 149 | } 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tiny-tests/generate_test.go: -------------------------------------------------------------------------------- 1 | package tinytest 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | var sampleEnv = Env{ 9 | Contract: ContractInfo{Address: "wasm18vd8fpwxzck93qlwghaj6arh4p7c5n89k7fvsl"}, 10 | // Time in nanoseconds since epoch start 11 | Block: BlockInfo{Height: 78000, Time: 1629131191823419000}, 12 | } 13 | 14 | var sampleEnvText = []byte(`{"block":{"height":78000,"time":"1629131191823419000"},"contract":{"address":"wasm18vd8fpwxzck93qlwghaj6arh4p7c5n89k7fvsl"}}`) 15 | 16 | var sampleMsgInfo = MessageInfo{ 17 | Signer: "wasm18vd8fpwxzck93qlwghaj6arh4p7c5n89k7fvsl", 18 | Funds: []Coin{{Amount: "123000000", Denom: "utgd"}}, 19 | } 20 | 21 | var sampleMsgInfoText = []byte(`{"signer":"wasm18vd8fpwxzck93qlwghaj6arh4p7c5n89k7fvsl","funds":[{"denom":"utgd","amount":"123000000"}]}`) 22 | 23 | func TestGenerateData(t *testing.T) { 24 | bz, _ := sampleEnv.MarshalJSON() 25 | // fmt.Println(string(bz)) 26 | if !bytes.Equal(bz, sampleEnvText) { 27 | t.Fatal("Update sample text") 28 | } 29 | bz, _ = sampleMsgInfo.MarshalJSON() 30 | // fmt.Println(string(bz)) 31 | if !bytes.Equal(bz, sampleMsgInfoText) { 32 | t.Fatal("Update sample text") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tinyjson/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/CosmWasm/tinyjson/bootstrap" 12 | // Reference the gen package to be friendly to vendoring tools, 13 | // as it is an indirect dependency. 14 | // (The temporary bootstrapping code uses it.) 15 | _ "github.com/CosmWasm/tinyjson/gen" 16 | "github.com/CosmWasm/tinyjson/parser" 17 | ) 18 | 19 | var buildTags = flag.String("build_tags", "", "build tags to add to generated file") 20 | var genBuildFlags = flag.String("gen_build_flags", "", "build flags when running the generator while bootstrapping") 21 | var snakeCase = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default") 22 | var lowerCamelCase = flag.Bool("lower_camel_case", false, "use lowerCamelCase names instead of CamelCase by default") 23 | var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON funcs") 24 | var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default") 25 | var allStructs = flag.Bool("all", false, "generate marshaler/unmarshalers for all structs in a file") 26 | var simpleBytes = flag.Bool("byte", false, "use simple bytes instead of Base64Bytes for slice of bytes") 27 | var leaveTemps = flag.Bool("leave_temps", false, "do not delete temporary files") 28 | var stubs = flag.Bool("stubs", false, "only generate stubs for marshaler/unmarshaler funcs") 29 | var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output file") 30 | var specifiedName = flag.String("output_filename", "", "specify the filename of the output") 31 | var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file") 32 | var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared") 33 | var skipMemberNameUnescaping = flag.Bool("disable_members_unescape", false, "don't perform unescaping of member names to improve performance") 34 | 35 | func generate(fname string) (err error) { 36 | fInfo, err := os.Stat(fname) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | p := parser.Parser{AllStructs: *allStructs} 42 | if err := p.Parse(fname, fInfo.IsDir()); err != nil { 43 | return fmt.Errorf("Error parsing %v: %v", fname, err) 44 | } 45 | 46 | var outName string 47 | if fInfo.IsDir() { 48 | outName = filepath.Join(fname, p.PkgName+"_tinyjson.go") 49 | } else { 50 | if s := strings.TrimSuffix(fname, ".go"); s == fname { 51 | return errors.New("Filename must end in '.go'") 52 | } else { 53 | outName = s + "_tinyjson.go" 54 | } 55 | } 56 | 57 | if *specifiedName != "" { 58 | outName = *specifiedName 59 | } 60 | 61 | var trimmedBuildTags string 62 | if *buildTags != "" { 63 | trimmedBuildTags = strings.TrimSpace(*buildTags) 64 | } 65 | 66 | var trimmedGenBuildFlags string 67 | if *genBuildFlags != "" { 68 | trimmedGenBuildFlags = strings.TrimSpace(*genBuildFlags) 69 | } 70 | 71 | g := bootstrap.Generator{ 72 | BuildTags: trimmedBuildTags, 73 | GenBuildFlags: trimmedGenBuildFlags, 74 | PkgPath: p.PkgPath, 75 | PkgName: p.PkgName, 76 | Types: p.StructNames, 77 | SnakeCase: *snakeCase, 78 | LowerCamelCase: *lowerCamelCase, 79 | NoStdMarshalers: *noStdMarshalers, 80 | DisallowUnknownFields: *disallowUnknownFields, 81 | SkipMemberNameUnescaping: *skipMemberNameUnescaping, 82 | OmitEmpty: *omitEmpty, 83 | LeaveTemps: *leaveTemps, 84 | OutName: outName, 85 | StubsOnly: *stubs, 86 | NoFormat: *noformat, 87 | SimpleBytes: *simpleBytes, 88 | } 89 | 90 | if err := g.Run(); err != nil { 91 | return fmt.Errorf("Bootstrap failed: %v", err) 92 | } 93 | return nil 94 | } 95 | 96 | func main() { 97 | flag.Parse() 98 | 99 | files := flag.Args() 100 | 101 | gofile := os.Getenv("GOFILE") 102 | if *processPkg { 103 | gofile = filepath.Dir(gofile) 104 | } 105 | 106 | if len(files) == 0 && gofile != "" { 107 | files = []string{gofile} 108 | } else if len(files) == 0 { 109 | flag.Usage() 110 | os.Exit(1) 111 | } 112 | 113 | for _, fname := range files { 114 | if err := generate(fname); err != nil { 115 | fmt.Fprintln(os.Stderr, err) 116 | os.Exit(1) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /unknown_fields.go: -------------------------------------------------------------------------------- 1 | package tinyjson 2 | 3 | import ( 4 | jlexer "github.com/CosmWasm/tinyjson/jlexer" 5 | "github.com/CosmWasm/tinyjson/jwriter" 6 | ) 7 | 8 | // UnknownFieldsProxy implemets UnknownsUnmarshaler and UnknownsMarshaler 9 | // use it as embedded field in your structure to parse and then serialize unknown struct fields 10 | type UnknownFieldsProxy struct { 11 | unknownFields map[string][]byte 12 | } 13 | 14 | func (s *UnknownFieldsProxy) UnmarshalUnknown(in *jlexer.Lexer, key string) { 15 | if s.unknownFields == nil { 16 | s.unknownFields = make(map[string][]byte, 1) 17 | } 18 | s.unknownFields[key] = in.Raw() 19 | } 20 | 21 | func (s UnknownFieldsProxy) MarshalUnknowns(out *jwriter.Writer, first bool) { 22 | for key, val := range s.unknownFields { 23 | if first { 24 | first = false 25 | } else { 26 | out.RawByte(',') 27 | } 28 | out.String(string(key)) 29 | out.RawByte(':') 30 | out.Raw(val, nil) 31 | } 32 | } 33 | --------------------------------------------------------------------------------