1
2
3
4
5
6
7 package json_test
8
9 import (
10 "bytes"
11 "cmp"
12 "fmt"
13 "io"
14 "os"
15 "path"
16 "reflect"
17 "strings"
18 "testing"
19 "testing/iotest"
20 "time"
21
22 jsonv1 "encoding/json"
23
24 jsonv1in2 "encoding/json"
25 "encoding/json/internal/jsontest"
26 "encoding/json/jsontext"
27 jsonv2 "encoding/json/v2"
28 )
29
30
31 var benchVersion = cmp.Or(os.Getenv("BENCHMARK_VERSION"), "v2")
32
33 var jsonFuncs = func() (funcs struct {
34 marshal func(any) ([]byte, error)
35 unmarshal func([]byte, any) error
36 encodeValue func(w io.Writer, b []byte) error
37 encodeTokens func(w io.Writer, toks []jsontext.Token) error
38 decodeValue func(r io.Reader) error
39 decodeTokens func(r io.Reader) error
40 }) {
41 ignoreEOF := func(err error) error {
42 if err == io.EOF {
43 err = nil
44 }
45 return err
46 }
47
48 switch benchVersion {
49 case "v1":
50 funcs.marshal = jsonv1.Marshal
51 funcs.unmarshal = jsonv1.Unmarshal
52 funcs.encodeValue = func(w io.Writer, b []byte) error {
53 return jsonv1.NewEncoder(w).Encode(jsonv1.RawMessage(b))
54 }
55 funcs.decodeValue = func(r io.Reader) error {
56 var v jsonv1.RawMessage
57 return jsonv1.NewDecoder(r).Decode(&v)
58 }
59 funcs.decodeTokens = func(r io.Reader) error {
60 d := jsonv1.NewDecoder(r)
61 for {
62 if _, err := d.Token(); err != nil {
63 return ignoreEOF(err)
64 }
65 }
66 }
67 case "v1in2":
68 funcs.marshal = jsonv1in2.Marshal
69 funcs.unmarshal = jsonv1in2.Unmarshal
70 funcs.encodeValue = func(w io.Writer, b []byte) error {
71 return jsonv1in2.NewEncoder(w).Encode(jsonv1in2.RawMessage(b))
72 }
73 funcs.decodeValue = func(r io.Reader) error {
74 var v jsonv1in2.RawMessage
75 return jsonv1in2.NewDecoder(r).Decode(&v)
76 }
77 funcs.decodeTokens = func(r io.Reader) error {
78 d := jsonv1in2.NewDecoder(r)
79 for {
80 if _, err := d.Token(); err != nil {
81 return ignoreEOF(err)
82 }
83 }
84 }
85 case "v2":
86 funcs.marshal = func(v any) ([]byte, error) { return jsonv2.Marshal(v) }
87 funcs.unmarshal = func(b []byte, v any) error { return jsonv2.Unmarshal(b, v) }
88 funcs.encodeValue = func(w io.Writer, b []byte) error {
89 return jsontext.NewEncoder(w).WriteValue(b)
90 }
91 funcs.encodeTokens = func(w io.Writer, toks []jsontext.Token) error {
92 e := jsontext.NewEncoder(w)
93 for _, tok := range toks {
94 if err := e.WriteToken(tok); err != nil {
95 return err
96 }
97 }
98 return nil
99 }
100 funcs.decodeValue = func(r io.Reader) error {
101 _, err := jsontext.NewDecoder(r).ReadValue()
102 return err
103 }
104 funcs.decodeTokens = func(r io.Reader) error {
105 d := jsontext.NewDecoder(r)
106 for {
107 if _, err := d.ReadToken(); err != nil {
108 return ignoreEOF(err)
109 }
110 }
111 }
112 default:
113 panic("unknown version: " + benchVersion)
114 }
115 return
116 }()
117
118
119
120 type bytesBuffer struct{ *bytes.Buffer }
121
122 func addr[T any](v T) *T {
123 return &v
124 }
125
126 func len64[Bytes ~[]byte | ~string](in Bytes) int64 {
127 return int64(len(in))
128 }
129
130 var arshalTestdata = []struct {
131 name string
132 raw []byte
133 val any
134 new func() any
135 skipV1 bool
136 }{{
137 name: "Bool",
138 raw: []byte("true"),
139 val: addr(true),
140 new: func() any { return new(bool) },
141 }, {
142 name: "String",
143 raw: []byte(`"hello, world!"`),
144 val: addr("hello, world!"),
145 new: func() any { return new(string) },
146 }, {
147 name: "Int",
148 raw: []byte("-1234"),
149 val: addr(int64(-1234)),
150 new: func() any { return new(int64) },
151 }, {
152 name: "Uint",
153 raw: []byte("1234"),
154 val: addr(uint64(1234)),
155 new: func() any { return new(uint64) },
156 }, {
157 name: "Float",
158 raw: []byte("12.34"),
159 val: addr(float64(12.34)),
160 new: func() any { return new(float64) },
161 }, {
162 name: "Map/ManyEmpty",
163 raw: []byte(`[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]`),
164 val: addr(func() (out []map[string]string) {
165 for range 100 {
166 out = append(out, map[string]string{})
167 }
168 return out
169 }()),
170 new: func() any { return new([]map[string]string) },
171 }, {
172 name: "Map/OneLarge",
173 raw: []byte(`{"A":"A","B":"B","C":"C","D":"D","E":"E","F":"F","G":"G","H":"H","I":"I","J":"J","K":"K","L":"L","M":"M","N":"N","O":"O","P":"P","Q":"Q","R":"R","S":"S","T":"T","U":"U","V":"V","W":"W","X":"X","Y":"Y","Z":"Z"}`),
174 val: addr(map[string]string{"A": "A", "B": "B", "C": "C", "D": "D", "E": "E", "F": "F", "G": "G", "H": "H", "I": "I", "J": "J", "K": "K", "L": "L", "M": "M", "N": "N", "O": "O", "P": "P", "Q": "Q", "R": "R", "S": "S", "T": "T", "U": "U", "V": "V", "W": "W", "X": "X", "Y": "Y", "Z": "Z"}),
175 new: func() any { return new(map[string]string) },
176 }, {
177 name: "Map/ManySmall",
178 raw: []byte(`{"A":{"K":"V"},"B":{"K":"V"},"C":{"K":"V"},"D":{"K":"V"},"E":{"K":"V"},"F":{"K":"V"},"G":{"K":"V"},"H":{"K":"V"},"I":{"K":"V"},"J":{"K":"V"},"K":{"K":"V"},"L":{"K":"V"},"M":{"K":"V"},"N":{"K":"V"},"O":{"K":"V"},"P":{"K":"V"},"Q":{"K":"V"},"R":{"K":"V"},"S":{"K":"V"},"T":{"K":"V"},"U":{"K":"V"},"V":{"K":"V"},"W":{"K":"V"},"X":{"K":"V"},"Y":{"K":"V"},"Z":{"K":"V"}}`),
179 val: addr(map[string]map[string]string{"A": {"K": "V"}, "B": {"K": "V"}, "C": {"K": "V"}, "D": {"K": "V"}, "E": {"K": "V"}, "F": {"K": "V"}, "G": {"K": "V"}, "H": {"K": "V"}, "I": {"K": "V"}, "J": {"K": "V"}, "K": {"K": "V"}, "L": {"K": "V"}, "M": {"K": "V"}, "N": {"K": "V"}, "O": {"K": "V"}, "P": {"K": "V"}, "Q": {"K": "V"}, "R": {"K": "V"}, "S": {"K": "V"}, "T": {"K": "V"}, "U": {"K": "V"}, "V": {"K": "V"}, "W": {"K": "V"}, "X": {"K": "V"}, "Y": {"K": "V"}, "Z": {"K": "V"}}),
180 new: func() any { return new(map[string]map[string]string) },
181 }, {
182 name: "Struct/ManyEmpty",
183 raw: []byte(`[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]`),
184 val: addr(make([]struct{}, 100)),
185 new: func() any {
186 return new([]struct{})
187 },
188 }, {
189 name: "Struct/OneLarge",
190 raw: []byte(`{"A":"A","B":"B","C":"C","D":"D","E":"E","F":"F","G":"G","H":"H","I":"I","J":"J","K":"K","L":"L","M":"M","N":"N","O":"O","P":"P","Q":"Q","R":"R","S":"S","T":"T","U":"U","V":"V","W":"W","X":"X","Y":"Y","Z":"Z"}`),
191 val: addr(struct{ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z string }{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}),
192 new: func() any {
193 return new(struct{ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z string })
194 },
195 }, {
196 name: "Struct/ManySmall",
197 raw: []byte(`{"A":{"K":"V"},"B":{"K":"V"},"C":{"K":"V"},"D":{"K":"V"},"E":{"K":"V"},"F":{"K":"V"},"G":{"K":"V"},"H":{"K":"V"},"I":{"K":"V"},"J":{"K":"V"},"K":{"K":"V"},"L":{"K":"V"},"M":{"K":"V"},"N":{"K":"V"},"O":{"K":"V"},"P":{"K":"V"},"Q":{"K":"V"},"R":{"K":"V"},"S":{"K":"V"},"T":{"K":"V"},"U":{"K":"V"},"V":{"K":"V"},"W":{"K":"V"},"X":{"K":"V"},"Y":{"K":"V"},"Z":{"K":"V"}}`),
198 val: func() any {
199 V := struct{ K string }{"V"}
200 return addr(struct{ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z struct{ K string } }{
201 V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V,
202 })
203 }(),
204 new: func() any {
205 return new(struct{ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z struct{ K string } })
206 },
207 }, {
208 name: "Slice/ManyEmpty",
209 raw: []byte(`[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]`),
210 val: addr(func() (out [][]string) {
211 for range 100 {
212 out = append(out, []string{})
213 }
214 return out
215 }()),
216 new: func() any { return new([][]string) },
217 }, {
218 name: "Slice/OneLarge",
219 raw: []byte(`["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]`),
220 val: addr([]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}),
221 new: func() any { return new([]string) },
222 }, {
223 name: "Slice/ManySmall",
224 raw: []byte(`[["A"],["B"],["C"],["D"],["E"],["F"],["G"],["H"],["I"],["J"],["K"],["L"],["M"],["N"],["O"],["P"],["Q"],["R"],["S"],["T"],["U"],["V"],["W"],["X"],["Y"],["Z"]]`),
225 val: addr([][]string{{"A"}, {"B"}, {"C"}, {"D"}, {"E"}, {"F"}, {"G"}, {"H"}, {"I"}, {"J"}, {"K"}, {"L"}, {"M"}, {"N"}, {"O"}, {"P"}, {"Q"}, {"R"}, {"S"}, {"T"}, {"U"}, {"V"}, {"W"}, {"X"}, {"Y"}, {"Z"}}),
226 new: func() any { return new([][]string) },
227 }, {
228 name: "Array/OneLarge",
229 raw: []byte(`["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]`),
230 val: addr([26]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}),
231 new: func() any { return new([26]string) },
232 }, {
233 name: "Array/ManySmall",
234 raw: []byte(`[["A"],["B"],["C"],["D"],["E"],["F"],["G"],["H"],["I"],["J"],["K"],["L"],["M"],["N"],["O"],["P"],["Q"],["R"],["S"],["T"],["U"],["V"],["W"],["X"],["Y"],["Z"]]`),
235 val: addr([26][1]string{{"A"}, {"B"}, {"C"}, {"D"}, {"E"}, {"F"}, {"G"}, {"H"}, {"I"}, {"J"}, {"K"}, {"L"}, {"M"}, {"N"}, {"O"}, {"P"}, {"Q"}, {"R"}, {"S"}, {"T"}, {"U"}, {"V"}, {"W"}, {"X"}, {"Y"}, {"Z"}}),
236 new: func() any { return new([26][1]string) },
237 }, {
238 name: "Bytes/Slice",
239 raw: []byte(`"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="`),
240 val: addr([]byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}),
241 new: func() any { return new([]byte) },
242 }, {
243 name: "Bytes/Array",
244 raw: []byte(`"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="`),
245 val: addr([32]byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}),
246 new: func() any { return new([32]byte) },
247 skipV1: true,
248 }, {
249 name: "Pointer",
250 raw: []byte("true"),
251 val: addr(addr(addr(addr(addr(addr(addr(addr(addr(addr(addr(true))))))))))),
252 new: func() any { return new(**********bool) },
253 }, {
254 name: "TextArshal",
255 raw: []byte(`"method"`),
256 val: new(textArshaler),
257 new: func() any { return new(textArshaler) },
258 }, {
259 name: "JSONArshalV1",
260 raw: []byte(`"method"`),
261 val: new(jsonArshalerV1),
262 new: func() any { return new(jsonArshalerV1) },
263 }, {
264 name: "JSONArshalV2",
265 raw: []byte(`"method"`),
266 val: new(jsonArshalerV2),
267 new: func() any { return new(jsonArshalerV2) },
268 skipV1: true,
269 }, {
270 name: "Duration",
271 raw: []byte(`"1h1m1s"`),
272 val: addr(time.Hour + time.Minute + time.Second),
273 new: func() any { return new(time.Duration) },
274 skipV1: true,
275 }, {
276 name: "Time",
277 raw: []byte(`"2006-01-02T22:04:05Z"`),
278 val: addr(time.Unix(1136239445, 0).UTC()),
279 new: func() any { return new(time.Time) },
280 }}
281
282 type textArshaler struct{ _ [4]int }
283
284 func (textArshaler) MarshalText() ([]byte, error) {
285 return []byte("method"), nil
286 }
287 func (*textArshaler) UnmarshalText(b []byte) error {
288 if string(b) != "method" {
289 return fmt.Errorf("UnmarshalText: got %q, want %q", b, "method")
290 }
291 return nil
292 }
293
294 type jsonArshalerV1 struct{ _ [4]int }
295
296 func (jsonArshalerV1) MarshalJSON() ([]byte, error) {
297 return []byte(`"method"`), nil
298 }
299 func (*jsonArshalerV1) UnmarshalJSON(b []byte) error {
300 if string(b) != `"method"` {
301 return fmt.Errorf("UnmarshalJSON: got %q, want %q", b, `"method"`)
302 }
303 return nil
304 }
305
306 type jsonArshalerV2 struct{ _ [4]int }
307
308 func (jsonArshalerV2) MarshalJSONTo(enc *jsontext.Encoder) error {
309 return enc.WriteToken(jsontext.String("method"))
310 }
311 func (*jsonArshalerV2) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
312 b, err := dec.ReadValue()
313 if string(b) != `"method"` {
314 return fmt.Errorf("UnmarshalJSONFrom: got %q, want %q", b, `"method"`)
315 }
316 return err
317 }
318
319 func TestBenchmarkUnmarshal(t *testing.T) { runUnmarshal(t) }
320 func BenchmarkUnmarshal(b *testing.B) { runUnmarshal(b) }
321
322 func runUnmarshal(tb testing.TB) {
323 for _, tt := range arshalTestdata {
324 if tt.skipV1 && strings.HasPrefix(benchVersion, "v1") {
325 runTestOrBench(tb, tt.name, 0, func(tb testing.TB) { tb.Skip("not supported in v1") })
326 return
327 }
328
329
330 var val any
331 run := func(tb testing.TB) {
332 val = tt.new()
333 if err := jsonFuncs.unmarshal(tt.raw, val); err != nil {
334 tb.Fatalf("Unmarshal error: %v", err)
335 }
336 }
337
338
339 if _, ok := tb.(*testing.T); ok {
340 run0 := run
341 run = func(tb testing.TB) {
342 run0(tb)
343 if !reflect.DeepEqual(val, tt.val) {
344 tb.Fatalf("Unmarshal output mismatch:\ngot %v\nwant %v", val, tt.val)
345 }
346 }
347 }
348
349 runTestOrBench(tb, tt.name, len64(tt.raw), run)
350 }
351 }
352
353 func TestBenchmarkMarshal(t *testing.T) { runMarshal(t) }
354 func BenchmarkMarshal(b *testing.B) { runMarshal(b) }
355
356 func runMarshal(tb testing.TB) {
357 for _, tt := range arshalTestdata {
358 if tt.skipV1 && strings.HasPrefix(benchVersion, "v1") {
359 runTestOrBench(tb, tt.name, 0, func(tb testing.TB) { tb.Skip("not supported in v1") })
360 return
361 }
362
363
364 var raw []byte
365 run := func(tb testing.TB) {
366 var err error
367 raw, err = jsonFuncs.marshal(tt.val)
368 if err != nil {
369 tb.Fatalf("Marshal error: %v", err)
370 }
371 }
372
373
374 if _, ok := tb.(*testing.T); ok {
375 run0 := run
376 run = func(tb testing.TB) {
377 run0(tb)
378 if !bytes.Equal(raw, tt.raw) {
379
380 byteHistogram := func(b []byte) (h [256]int) {
381 for _, c := range b {
382 h[c]++
383 }
384 return h
385 }
386 if !(strings.HasPrefix(tt.name, "Map/") && byteHistogram(raw) == byteHistogram(tt.raw)) {
387 tb.Fatalf("Marshal output mismatch:\ngot %s\nwant %s", raw, tt.raw)
388 }
389 }
390 }
391 }
392
393 runTestOrBench(tb, tt.name, len64(tt.raw), run)
394 }
395 }
396
397 func TestBenchmarkTestdata(t *testing.T) { runAllTestdata(t) }
398 func BenchmarkTestdata(b *testing.B) { runAllTestdata(b) }
399
400 func runAllTestdata(tb testing.TB) {
401 for _, td := range jsontest.Data {
402 for _, arshalName := range []string{"Marshal", "Unmarshal"} {
403 for _, typeName := range []string{"Concrete", "Interface"} {
404 newValue := func() any { return new(any) }
405 if typeName == "Concrete" {
406 if td.New == nil {
407 continue
408 }
409 newValue = td.New
410 }
411 value := mustUnmarshalValue(tb, td.Data(), newValue)
412 name := path.Join(td.Name, arshalName, typeName)
413 runTestOrBench(tb, name, int64(len(td.Data())), func(tb testing.TB) {
414 runArshal(tb, arshalName, newValue, td.Data(), value)
415 })
416 }
417 }
418
419 tokens := mustDecodeTokens(tb, td.Data())
420 buffer := make([]byte, 0, 2*len(td.Data()))
421 for _, codeName := range []string{"Encode", "Decode"} {
422 for _, typeName := range []string{"Token", "Value"} {
423 for _, modeName := range []string{"Streaming", "Buffered"} {
424 name := path.Join(td.Name, codeName, typeName, modeName)
425 runTestOrBench(tb, name, int64(len(td.Data())), func(tb testing.TB) {
426 runCode(tb, codeName, typeName, modeName, buffer, td.Data(), tokens)
427 })
428 }
429 }
430 }
431 }
432 }
433
434 func mustUnmarshalValue(t testing.TB, data []byte, newValue func() any) (value any) {
435 value = newValue()
436 if err := jsonv2.Unmarshal(data, value); err != nil {
437 t.Fatalf("Unmarshal error: %v", err)
438 }
439 return value
440 }
441
442 func runArshal(t testing.TB, arshalName string, newValue func() any, data []byte, value any) {
443 switch arshalName {
444 case "Marshal":
445 if _, err := jsonFuncs.marshal(value); err != nil {
446 t.Fatalf("Marshal error: %v", err)
447 }
448 case "Unmarshal":
449 if err := jsonFuncs.unmarshal(data, newValue()); err != nil {
450 t.Fatalf("Unmarshal error: %v", err)
451 }
452 }
453 }
454
455 func mustDecodeTokens(t testing.TB, data []byte) []jsontext.Token {
456 var tokens []jsontext.Token
457 dec := jsontext.NewDecoder(bytes.NewReader(data))
458 for {
459 tok, err := dec.ReadToken()
460 if err != nil {
461 if err == io.EOF {
462 break
463 }
464 t.Fatalf("Decoder.ReadToken error: %v", err)
465 }
466
467
468
469 switch tok.Kind() {
470 case '"':
471 tokens = append(tokens, jsontext.String(tok.String()))
472 case '0':
473 tokens = append(tokens, jsontext.Float(tok.Float()))
474 default:
475 tokens = append(tokens, tok.Clone())
476 }
477 }
478 return tokens
479 }
480
481 func runCode(t testing.TB, codeName, typeName, modeName string, buffer, data []byte, tokens []jsontext.Token) {
482 switch codeName {
483 case "Encode":
484 runEncode(t, typeName, modeName, buffer, data, tokens)
485 case "Decode":
486 runDecode(t, typeName, modeName, buffer, data, tokens)
487 }
488 }
489
490 func runEncode(t testing.TB, typeName, modeName string, buffer, data []byte, tokens []jsontext.Token) {
491 if strings.HasPrefix(benchVersion, "v1") {
492 switch {
493 case modeName == "Buffered":
494 t.Skip("no support for direct buffered output in v1; see https://go.dev/issue/7872")
495 case typeName == "Token":
496 t.Skip("no support for encoding tokens in v1; see https://go.dev/issue/40127")
497 }
498 }
499
500 var w io.Writer
501 switch modeName {
502 case "Streaming":
503 w = bytesBuffer{bytes.NewBuffer(buffer[:0])}
504 case "Buffered":
505 w = bytes.NewBuffer(buffer[:0])
506 }
507 switch typeName {
508 case "Token":
509 if err := jsonFuncs.encodeTokens(w, tokens); err != nil {
510 t.Fatalf("Encoder.WriteToken error: %v", err)
511 }
512 case "Value":
513 if err := jsonFuncs.encodeValue(w, data); err != nil {
514 t.Fatalf("Encoder.WriteValue error: %v", err)
515 }
516 }
517 }
518
519 func runDecode(t testing.TB, typeName, modeName string, buffer, data []byte, tokens []jsontext.Token) {
520 if strings.HasPrefix(benchVersion, "v1") && modeName == "Buffered" {
521 t.Skip("no support for direct buffered input in v1; see https://go.dev/issue/11046")
522 }
523
524 var r io.Reader
525 switch modeName {
526 case "Streaming":
527 r = bytesBuffer{bytes.NewBuffer(data)}
528 case "Buffered":
529 r = bytes.NewBuffer(data)
530 }
531 switch typeName {
532 case "Token":
533 if err := jsonFuncs.decodeTokens(r); err != nil {
534 t.Fatalf("Decoder.ReadToken error: %v", err)
535 }
536 case "Value":
537 if err := jsonFuncs.decodeValue(r); err != nil {
538 t.Fatalf("Decoder.ReadValue error: %v", err)
539 }
540 }
541 }
542
543 var ws = strings.Repeat(" ", 4<<10)
544 var slowStreamingDecodeTestdata = []struct {
545 name string
546 data []byte
547 }{
548 {"LargeString", []byte(`"` + strings.Repeat(" ", 4<<10) + `"`)},
549 {"LargeNumber", []byte("0." + strings.Repeat("0", 4<<10))},
550 {"LargeWhitespace/Null", []byte(ws + "null" + ws)},
551 {"LargeWhitespace/Object", []byte(ws + "{" + ws + `"name1"` + ws + ":" + ws + `"value"` + ws + "," + ws + `"name2"` + ws + ":" + ws + `"value"` + ws + "}" + ws)},
552 {"LargeWhitespace/Array", []byte(ws + "[" + ws + `"value"` + ws + "," + ws + `"value"` + ws + "]" + ws)},
553 }
554
555 func TestBenchmarkSlowStreamingDecode(t *testing.T) { runAllSlowStreamingDecode(t) }
556 func BenchmarkSlowStreamingDecode(b *testing.B) { runAllSlowStreamingDecode(b) }
557
558 func runAllSlowStreamingDecode(tb testing.TB) {
559 for _, td := range slowStreamingDecodeTestdata {
560 for _, typeName := range []string{"Token", "Value"} {
561 name := path.Join(td.name, typeName)
562 runTestOrBench(tb, name, len64(td.data), func(tb testing.TB) {
563 runSlowStreamingDecode(tb, typeName, td.data)
564 })
565 }
566 }
567 }
568
569
570
571
572 func runSlowStreamingDecode(t testing.TB, typeName string, data []byte) {
573 r := iotest.OneByteReader(bytes.NewReader(data))
574 switch typeName {
575 case "Token":
576 if err := jsonFuncs.decodeTokens(r); err != nil {
577 t.Fatalf("Decoder.ReadToken error: %v", err)
578 }
579 case "Value":
580 if err := jsonFuncs.decodeValue(r); err != nil {
581 t.Fatalf("Decoder.ReadValue error: %v", err)
582 }
583 }
584 }
585
586 func TestBenchmarkTextValue(t *testing.T) { runValue(t) }
587 func BenchmarkTextValue(b *testing.B) { runValue(b) }
588
589 func runValue(tb testing.TB) {
590 if testing.Short() {
591 tb.Skip()
592 }
593 var data []byte
594 for _, ts := range jsontest.Data {
595 if ts.Name == "CitmCatalog" {
596 data = ts.Data()
597 }
598 }
599
600 runTestOrBench(tb, "IsValid", len64(data), func(tb testing.TB) {
601 jsontext.Value(data).IsValid()
602 })
603
604 methods := []struct {
605 name string
606 format func(*jsontext.Value, ...jsontext.Options) error
607 }{
608 {"Compact", (*jsontext.Value).Compact},
609 {"Indent", (*jsontext.Value).Indent},
610 {"Canonicalize", (*jsontext.Value).Canonicalize},
611 }
612
613 var v jsontext.Value
614 for _, method := range methods {
615 runTestOrBench(tb, method.name, len64(data), func(tb testing.TB) {
616 v = append(v[:0], data...)
617 if err := method.format(&v); err != nil {
618 tb.Errorf("jsontext.Value.%v error: %v", method.name, err)
619 }
620 })
621 v = append(v[:0], data...)
622 method.format(&v)
623 runTestOrBench(tb, method.name+"/Noop", len64(data), func(tb testing.TB) {
624 if err := method.format(&v); err != nil {
625 tb.Errorf("jsontext.Value.%v error: %v", method.name, err)
626 }
627 })
628 }
629 }
630
631 func runTestOrBench(tb testing.TB, name string, numBytes int64, run func(tb testing.TB)) {
632 switch tb := tb.(type) {
633 case *testing.T:
634 tb.Run(name, func(t *testing.T) {
635 run(t)
636 })
637 case *testing.B:
638 tb.Run(name, func(b *testing.B) {
639 b.ResetTimer()
640 b.ReportAllocs()
641 b.SetBytes(numBytes)
642 for range b.N {
643 run(b)
644 }
645 })
646 }
647 }
648
View as plain text