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