k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/bench_test.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // bytesBuffer is identical to bytes.Buffer, 24 // but a different type to avoid any optimizations for bytes.Buffer. 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 // Setup the unmarshal operation. 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 // Verify the results. 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 // Setup the marshal operation. 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 // Verify the results. 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 // Map marshaling in v2 is non-deterministic. 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 // Prefer exact representation for JSON strings and numbers 381 // since this more closely matches common use cases. 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 // runSlowStreamingDecode tests a streaming Decoder operating on 518 // a slow io.Reader that only returns 1 byte at a time, 519 // which tends to exercise pathological behavior. 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() // CitmCatalog is not loaded in short mode 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...) // reset with original input 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 }