1
2
3
4
5
6
7
8
9
10
11 package json
12
13 import (
14 "bytes"
15 "compress/gzip"
16 "fmt"
17 "internal/testenv"
18 "io"
19 "os"
20 "reflect"
21 "regexp"
22 "runtime"
23 "strings"
24 "sync"
25 "testing"
26 )
27
28 type codeResponse struct {
29 Tree *codeNode `json:"tree"`
30 Username string `json:"username"`
31 }
32
33 type codeNode struct {
34 Name string `json:"name"`
35 Kids []*codeNode `json:"kids"`
36 CLWeight float64 `json:"cl_weight"`
37 Touches int `json:"touches"`
38 MinT int64 `json:"min_t"`
39 MaxT int64 `json:"max_t"`
40 MeanT int64 `json:"mean_t"`
41 }
42
43 var codeJSON []byte
44 var codeStruct codeResponse
45
46 func codeInit() {
47 f, err := os.Open("testdata/code.json.gz")
48 if err != nil {
49 panic(err)
50 }
51 defer f.Close()
52 gz, err := gzip.NewReader(f)
53 if err != nil {
54 panic(err)
55 }
56 data, err := io.ReadAll(gz)
57 if err != nil {
58 panic(err)
59 }
60
61 codeJSON = data
62
63 if err := Unmarshal(codeJSON, &codeStruct); err != nil {
64 panic("unmarshal code.json: " + err.Error())
65 }
66
67 if data, err = Marshal(&codeStruct); err != nil {
68 panic("marshal code.json: " + err.Error())
69 }
70
71 if !bytes.Equal(data, codeJSON) {
72 println("different lengths", len(data), len(codeJSON))
73 for i := 0; i < len(data) && i < len(codeJSON); i++ {
74 if data[i] != codeJSON[i] {
75 println("re-marshal: changed at byte", i)
76 println("orig: ", string(codeJSON[i-10:i+10]))
77 println("new: ", string(data[i-10:i+10]))
78 break
79 }
80 }
81 panic("re-marshal code.json: different result")
82 }
83 }
84
85 func BenchmarkCodeEncoder(b *testing.B) {
86 b.ReportAllocs()
87 if codeJSON == nil {
88 b.StopTimer()
89 codeInit()
90 b.StartTimer()
91 }
92 b.RunParallel(func(pb *testing.PB) {
93 enc := NewEncoder(io.Discard)
94 for pb.Next() {
95 if err := enc.Encode(&codeStruct); err != nil {
96 b.Fatalf("Encode error: %v", err)
97 }
98 }
99 })
100 b.SetBytes(int64(len(codeJSON)))
101 }
102
103 func BenchmarkCodeEncoderError(b *testing.B) {
104 b.ReportAllocs()
105 if codeJSON == nil {
106 b.StopTimer()
107 codeInit()
108 b.StartTimer()
109 }
110
111
112 type Dummy struct {
113 Name string
114 Next *Dummy
115 }
116 dummy := Dummy{Name: "Dummy"}
117 dummy.Next = &dummy
118
119 b.RunParallel(func(pb *testing.PB) {
120 enc := NewEncoder(io.Discard)
121 for pb.Next() {
122 if err := enc.Encode(&codeStruct); err != nil {
123 b.Fatalf("Encode error: %v", err)
124 }
125 if _, err := Marshal(dummy); err == nil {
126 b.Fatal("Marshal error: got nil, want non-nil")
127 }
128 }
129 })
130 b.SetBytes(int64(len(codeJSON)))
131 }
132
133 func BenchmarkCodeMarshal(b *testing.B) {
134 b.ReportAllocs()
135 if codeJSON == nil {
136 b.StopTimer()
137 codeInit()
138 b.StartTimer()
139 }
140 b.RunParallel(func(pb *testing.PB) {
141 for pb.Next() {
142 if _, err := Marshal(&codeStruct); err != nil {
143 b.Fatalf("Marshal error: %v", err)
144 }
145 }
146 })
147 b.SetBytes(int64(len(codeJSON)))
148 }
149
150 func BenchmarkCodeMarshalError(b *testing.B) {
151 b.ReportAllocs()
152 if codeJSON == nil {
153 b.StopTimer()
154 codeInit()
155 b.StartTimer()
156 }
157
158
159 type Dummy struct {
160 Name string
161 Next *Dummy
162 }
163 dummy := Dummy{Name: "Dummy"}
164 dummy.Next = &dummy
165
166 b.RunParallel(func(pb *testing.PB) {
167 for pb.Next() {
168 if _, err := Marshal(&codeStruct); err != nil {
169 b.Fatalf("Marshal error: %v", err)
170 }
171 if _, err := Marshal(dummy); err == nil {
172 b.Fatal("Marshal error: got nil, want non-nil")
173 }
174 }
175 })
176 b.SetBytes(int64(len(codeJSON)))
177 }
178
179 func benchMarshalBytes(n int) func(*testing.B) {
180 sample := []byte("hello world")
181
182
183 v := &struct {
184 Bytes []byte
185 }{
186 bytes.Repeat(sample, (n/len(sample))+1)[:n],
187 }
188 return func(b *testing.B) {
189 for i := 0; i < b.N; i++ {
190 if _, err := Marshal(v); err != nil {
191 b.Fatalf("Marshal error: %v", err)
192 }
193 }
194 }
195 }
196
197 func benchMarshalBytesError(n int) func(*testing.B) {
198 sample := []byte("hello world")
199
200
201 v := &struct {
202 Bytes []byte
203 }{
204 bytes.Repeat(sample, (n/len(sample))+1)[:n],
205 }
206
207
208 type Dummy struct {
209 Name string
210 Next *Dummy
211 }
212 dummy := Dummy{Name: "Dummy"}
213 dummy.Next = &dummy
214
215 return func(b *testing.B) {
216 for i := 0; i < b.N; i++ {
217 if _, err := Marshal(v); err != nil {
218 b.Fatalf("Marshal error: %v", err)
219 }
220 if _, err := Marshal(dummy); err == nil {
221 b.Fatal("Marshal error: got nil, want non-nil")
222 }
223 }
224 }
225 }
226
227 func BenchmarkMarshalBytes(b *testing.B) {
228 b.ReportAllocs()
229
230 b.Run("32", benchMarshalBytes(32))
231
232
233 b.Run("256", benchMarshalBytes(256))
234
235 b.Run("4096", benchMarshalBytes(4096))
236 }
237
238 func BenchmarkMarshalBytesError(b *testing.B) {
239 b.ReportAllocs()
240
241 b.Run("32", benchMarshalBytesError(32))
242
243
244 b.Run("256", benchMarshalBytesError(256))
245
246 b.Run("4096", benchMarshalBytesError(4096))
247 }
248
249 func BenchmarkMarshalMap(b *testing.B) {
250 b.ReportAllocs()
251 m := map[string]int{
252 "key3": 3,
253 "key2": 2,
254 "key1": 1,
255 }
256 b.RunParallel(func(pb *testing.PB) {
257 for pb.Next() {
258 if _, err := Marshal(m); err != nil {
259 b.Fatal("Marshal:", err)
260 }
261 }
262 })
263 }
264
265 func BenchmarkCodeDecoder(b *testing.B) {
266 b.ReportAllocs()
267 if codeJSON == nil {
268 b.StopTimer()
269 codeInit()
270 b.StartTimer()
271 }
272 b.RunParallel(func(pb *testing.PB) {
273 var buf bytes.Buffer
274 dec := NewDecoder(&buf)
275 var r codeResponse
276 for pb.Next() {
277 buf.Write(codeJSON)
278
279 buf.WriteByte('\n')
280 buf.WriteByte('\n')
281 buf.WriteByte('\n')
282 if err := dec.Decode(&r); err != nil {
283 b.Fatalf("Decode error: %v", err)
284 }
285 }
286 })
287 b.SetBytes(int64(len(codeJSON)))
288 }
289
290 func BenchmarkUnicodeDecoder(b *testing.B) {
291 b.ReportAllocs()
292 j := []byte(`"\uD83D\uDE01"`)
293 b.SetBytes(int64(len(j)))
294 r := bytes.NewReader(j)
295 dec := NewDecoder(r)
296 var out string
297 b.ResetTimer()
298 for i := 0; i < b.N; i++ {
299 if err := dec.Decode(&out); err != nil {
300 b.Fatalf("Decode error: %v", err)
301 }
302 r.Seek(0, 0)
303 }
304 }
305
306 func BenchmarkDecoderStream(b *testing.B) {
307 b.ReportAllocs()
308 b.StopTimer()
309 var buf bytes.Buffer
310 dec := NewDecoder(&buf)
311 buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
312 var x any
313 if err := dec.Decode(&x); err != nil {
314 b.Fatalf("Decode error: %v", err)
315 }
316 ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
317 b.StartTimer()
318 for i := 0; i < b.N; i++ {
319 if i%300000 == 0 {
320 buf.WriteString(ones)
321 }
322 x = nil
323 switch err := dec.Decode(&x); {
324 case err != nil:
325 b.Fatalf("Decode error: %v", err)
326 case x != 1.0:
327 b.Fatalf("Decode: got %v want 1.0", i)
328 }
329 }
330 }
331
332 func BenchmarkCodeUnmarshal(b *testing.B) {
333 b.ReportAllocs()
334 if codeJSON == nil {
335 b.StopTimer()
336 codeInit()
337 b.StartTimer()
338 }
339 b.RunParallel(func(pb *testing.PB) {
340 for pb.Next() {
341 var r codeResponse
342 if err := Unmarshal(codeJSON, &r); err != nil {
343 b.Fatalf("Unmarshal error: %v", err)
344 }
345 }
346 })
347 b.SetBytes(int64(len(codeJSON)))
348 }
349
350 func BenchmarkCodeUnmarshalReuse(b *testing.B) {
351 b.ReportAllocs()
352 if codeJSON == nil {
353 b.StopTimer()
354 codeInit()
355 b.StartTimer()
356 }
357 b.RunParallel(func(pb *testing.PB) {
358 var r codeResponse
359 for pb.Next() {
360 if err := Unmarshal(codeJSON, &r); err != nil {
361 b.Fatalf("Unmarshal error: %v", err)
362 }
363 }
364 })
365 b.SetBytes(int64(len(codeJSON)))
366 }
367
368 func BenchmarkUnmarshalString(b *testing.B) {
369 b.ReportAllocs()
370 data := []byte(`"hello, world"`)
371 b.RunParallel(func(pb *testing.PB) {
372 var s string
373 for pb.Next() {
374 if err := Unmarshal(data, &s); err != nil {
375 b.Fatalf("Unmarshal error: %v", err)
376 }
377 }
378 })
379 }
380
381 func BenchmarkUnmarshalFloat64(b *testing.B) {
382 b.ReportAllocs()
383 data := []byte(`3.14`)
384 b.RunParallel(func(pb *testing.PB) {
385 var f float64
386 for pb.Next() {
387 if err := Unmarshal(data, &f); err != nil {
388 b.Fatalf("Unmarshal error: %v", err)
389 }
390 }
391 })
392 }
393
394 func BenchmarkUnmarshalInt64(b *testing.B) {
395 b.ReportAllocs()
396 data := []byte(`3`)
397 b.RunParallel(func(pb *testing.PB) {
398 var x int64
399 for pb.Next() {
400 if err := Unmarshal(data, &x); err != nil {
401 b.Fatalf("Unmarshal error: %v", err)
402 }
403 }
404 })
405 }
406
407 func BenchmarkUnmarshalMap(b *testing.B) {
408 b.ReportAllocs()
409 data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`)
410 b.RunParallel(func(pb *testing.PB) {
411 x := make(map[string]string, 3)
412 for pb.Next() {
413 if err := Unmarshal(data, &x); err != nil {
414 b.Fatalf("Unmarshal error: %v", err)
415 }
416 }
417 })
418 }
419
420 func BenchmarkIssue10335(b *testing.B) {
421 b.ReportAllocs()
422 j := []byte(`{"a":{ }}`)
423 b.RunParallel(func(pb *testing.PB) {
424 var s struct{}
425 for pb.Next() {
426 if err := Unmarshal(j, &s); err != nil {
427 b.Fatalf("Unmarshal error: %v", err)
428 }
429 }
430 })
431 }
432
433 func BenchmarkIssue34127(b *testing.B) {
434 b.ReportAllocs()
435 j := struct {
436 Bar string `json:"bar,string"`
437 }{
438 Bar: `foobar`,
439 }
440 b.RunParallel(func(pb *testing.PB) {
441 for pb.Next() {
442 if _, err := Marshal(&j); err != nil {
443 b.Fatalf("Marshal error: %v", err)
444 }
445 }
446 })
447 }
448
449 func BenchmarkUnmapped(b *testing.B) {
450 b.ReportAllocs()
451 j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
452 b.RunParallel(func(pb *testing.PB) {
453 var s struct{}
454 for pb.Next() {
455 if err := Unmarshal(j, &s); err != nil {
456 b.Fatalf("Unmarshal error: %v", err)
457 }
458 }
459 })
460 }
461
462 func BenchmarkTypeFieldsCache(b *testing.B) {
463 b.ReportAllocs()
464 var maxTypes int = 1e6
465 if testenv.Builder() != "" {
466 maxTypes = 1e3
467 }
468
469
470 types := make([]reflect.Type, maxTypes)
471 fs := []reflect.StructField{{
472 Type: reflect.TypeFor[string](),
473 Index: []int{0},
474 }}
475 for i := range types {
476 fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)
477 types[i] = reflect.StructOf(fs)
478 }
479
480
481 clearCache := func() {
482 fieldCache = sync.Map{}
483 }
484
485
486
487 for nt := 1; nt <= maxTypes; nt *= 10 {
488 ts := types[:nt]
489 b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {
490 nc := runtime.GOMAXPROCS(0)
491 for i := 0; i < b.N; i++ {
492 clearCache()
493 var wg sync.WaitGroup
494 for j := 0; j < nc; j++ {
495 wg.Add(1)
496 go func(j int) {
497 for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {
498 cachedTypeFields(t)
499 }
500 wg.Done()
501 }(j)
502 }
503 wg.Wait()
504 }
505 })
506 }
507
508
509
510 for nt := 1; nt <= maxTypes; nt *= 10 {
511
512 clearCache()
513 for _, t := range types[:nt] {
514 cachedTypeFields(t)
515 }
516 b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {
517 b.RunParallel(func(pb *testing.PB) {
518 for pb.Next() {
519 cachedTypeFields(types[0])
520 }
521 })
522 })
523 }
524 }
525
526 func BenchmarkEncodeMarshaler(b *testing.B) {
527 b.ReportAllocs()
528
529 m := struct {
530 A int
531 B RawMessage
532 }{}
533
534 b.RunParallel(func(pb *testing.PB) {
535 enc := NewEncoder(io.Discard)
536
537 for pb.Next() {
538 if err := enc.Encode(&m); err != nil {
539 b.Fatalf("Encode error: %v", err)
540 }
541 }
542 })
543 }
544
545 func BenchmarkEncoderEncode(b *testing.B) {
546 b.ReportAllocs()
547 type T struct {
548 X, Y string
549 }
550 v := &T{"foo", "bar"}
551 b.RunParallel(func(pb *testing.PB) {
552 for pb.Next() {
553 if err := NewEncoder(io.Discard).Encode(v); err != nil {
554 b.Fatalf("Encode error: %v", err)
555 }
556 }
557 })
558 }
559
560 func BenchmarkNumberIsValid(b *testing.B) {
561 s := "-61657.61667E+61673"
562 for i := 0; i < b.N; i++ {
563 isValidNumber(s)
564 }
565 }
566
567 func BenchmarkNumberIsValidRegexp(b *testing.B) {
568 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
569 s := "-61657.61667E+61673"
570 for i := 0; i < b.N; i++ {
571 jsonNumberRegexp.MatchString(s)
572 }
573 }
574
575 func BenchmarkUnmarshalNumber(b *testing.B) {
576 b.ReportAllocs()
577 data := []byte(`"-61657.61667E+61673"`)
578 var number Number
579 for i := 0; i < b.N; i++ {
580 if err := Unmarshal(data, &number); err != nil {
581 b.Fatal("Unmarshal:", err)
582 }
583 }
584 }
585
View as plain text