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