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