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  }