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.Fatal("Encode:", 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.Fatal("Encode:", err)
124 }
125 if _, err := Marshal(dummy); err == nil {
126 b.Fatal("expect an error here")
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.Fatal("Marshal:", 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.Fatal("Marshal:", err)
170 }
171 if _, err := Marshal(dummy); err == nil {
172 b.Fatal("expect an error here")
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.Fatal("Marshal:", 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.Fatal("Marshal:", err)
219 }
220 if _, err := Marshal(dummy); err == nil {
221 b.Fatal("expect an error here")
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 BenchmarkCodeDecoder(b *testing.B) {
250 b.ReportAllocs()
251 if codeJSON == nil {
252 b.StopTimer()
253 codeInit()
254 b.StartTimer()
255 }
256 b.RunParallel(func(pb *testing.PB) {
257 var buf bytes.Buffer
258 dec := NewDecoder(&buf)
259 var r codeResponse
260 for pb.Next() {
261 buf.Write(codeJSON)
262
263 buf.WriteByte('\n')
264 buf.WriteByte('\n')
265 buf.WriteByte('\n')
266 if err := dec.Decode(&r); err != nil {
267 b.Fatal("Decode:", err)
268 }
269 }
270 })
271 b.SetBytes(int64(len(codeJSON)))
272 }
273
274 func BenchmarkUnicodeDecoder(b *testing.B) {
275 b.ReportAllocs()
276 j := []byte(`"\uD83D\uDE01"`)
277 b.SetBytes(int64(len(j)))
278 r := bytes.NewReader(j)
279 dec := NewDecoder(r)
280 var out string
281 b.ResetTimer()
282 for i := 0; i < b.N; i++ {
283 if err := dec.Decode(&out); err != nil {
284 b.Fatal("Decode:", err)
285 }
286 r.Seek(0, 0)
287 }
288 }
289
290 func BenchmarkDecoderStream(b *testing.B) {
291 b.ReportAllocs()
292 b.StopTimer()
293 var buf bytes.Buffer
294 dec := NewDecoder(&buf)
295 buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
296 var x any
297 if err := dec.Decode(&x); err != nil {
298 b.Fatal("Decode:", err)
299 }
300 ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
301 b.StartTimer()
302 for i := 0; i < b.N; i++ {
303 if i%300000 == 0 {
304 buf.WriteString(ones)
305 }
306 x = nil
307 if err := dec.Decode(&x); err != nil || x != 1.0 {
308 b.Fatalf("Decode: %v after %d", err, i)
309 }
310 }
311 }
312
313 func BenchmarkCodeUnmarshal(b *testing.B) {
314 b.ReportAllocs()
315 if codeJSON == nil {
316 b.StopTimer()
317 codeInit()
318 b.StartTimer()
319 }
320 b.RunParallel(func(pb *testing.PB) {
321 for pb.Next() {
322 var r codeResponse
323 if err := Unmarshal(codeJSON, &r); err != nil {
324 b.Fatal("Unmarshal:", err)
325 }
326 }
327 })
328 b.SetBytes(int64(len(codeJSON)))
329 }
330
331 func BenchmarkCodeUnmarshalReuse(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 var r codeResponse
340 for pb.Next() {
341 if err := Unmarshal(codeJSON, &r); err != nil {
342 b.Fatal("Unmarshal:", err)
343 }
344 }
345 })
346 b.SetBytes(int64(len(codeJSON)))
347 }
348
349 func BenchmarkUnmarshalString(b *testing.B) {
350 b.ReportAllocs()
351 data := []byte(`"hello, world"`)
352 b.RunParallel(func(pb *testing.PB) {
353 var s string
354 for pb.Next() {
355 if err := Unmarshal(data, &s); err != nil {
356 b.Fatal("Unmarshal:", err)
357 }
358 }
359 })
360 }
361
362 func BenchmarkUnmarshalFloat64(b *testing.B) {
363 b.ReportAllocs()
364 data := []byte(`3.14`)
365 b.RunParallel(func(pb *testing.PB) {
366 var f float64
367 for pb.Next() {
368 if err := Unmarshal(data, &f); err != nil {
369 b.Fatal("Unmarshal:", err)
370 }
371 }
372 })
373 }
374
375 func BenchmarkUnmarshalInt64(b *testing.B) {
376 b.ReportAllocs()
377 data := []byte(`3`)
378 b.RunParallel(func(pb *testing.PB) {
379 var x int64
380 for pb.Next() {
381 if err := Unmarshal(data, &x); err != nil {
382 b.Fatal("Unmarshal:", err)
383 }
384 }
385 })
386 }
387
388 func BenchmarkIssue10335(b *testing.B) {
389 b.ReportAllocs()
390 j := []byte(`{"a":{ }}`)
391 b.RunParallel(func(pb *testing.PB) {
392 var s struct{}
393 for pb.Next() {
394 if err := Unmarshal(j, &s); err != nil {
395 b.Fatal(err)
396 }
397 }
398 })
399 }
400
401 func BenchmarkIssue34127(b *testing.B) {
402 b.ReportAllocs()
403 j := struct {
404 Bar string `json:"bar,string"`
405 }{
406 Bar: `foobar`,
407 }
408 b.RunParallel(func(pb *testing.PB) {
409 for pb.Next() {
410 if _, err := Marshal(&j); err != nil {
411 b.Fatal(err)
412 }
413 }
414 })
415 }
416
417 func BenchmarkUnmapped(b *testing.B) {
418 b.ReportAllocs()
419 j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
420 b.RunParallel(func(pb *testing.PB) {
421 var s struct{}
422 for pb.Next() {
423 if err := Unmarshal(j, &s); err != nil {
424 b.Fatal(err)
425 }
426 }
427 })
428 }
429
430 func BenchmarkTypeFieldsCache(b *testing.B) {
431 b.ReportAllocs()
432 var maxTypes int = 1e6
433 if testenv.Builder() != "" {
434 maxTypes = 1e3
435 }
436
437
438 types := make([]reflect.Type, maxTypes)
439 fs := []reflect.StructField{{
440 Type: reflect.TypeOf(""),
441 Index: []int{0},
442 }}
443 for i := range types {
444 fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)
445 types[i] = reflect.StructOf(fs)
446 }
447
448
449 clearCache := func() {
450 fieldCache = sync.Map{}
451 }
452
453
454
455 for nt := 1; nt <= maxTypes; nt *= 10 {
456 ts := types[:nt]
457 b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {
458 nc := runtime.GOMAXPROCS(0)
459 for i := 0; i < b.N; i++ {
460 clearCache()
461 var wg sync.WaitGroup
462 for j := 0; j < nc; j++ {
463 wg.Add(1)
464 go func(j int) {
465 for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {
466 cachedTypeFields(t)
467 }
468 wg.Done()
469 }(j)
470 }
471 wg.Wait()
472 }
473 })
474 }
475
476
477
478 for nt := 1; nt <= maxTypes; nt *= 10 {
479
480 clearCache()
481 for _, t := range types[:nt] {
482 cachedTypeFields(t)
483 }
484 b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {
485 b.RunParallel(func(pb *testing.PB) {
486 for pb.Next() {
487 cachedTypeFields(types[0])
488 }
489 })
490 })
491 }
492 }
493
494 func BenchmarkEncodeMarshaler(b *testing.B) {
495 b.ReportAllocs()
496
497 m := struct {
498 A int
499 B RawMessage
500 }{}
501
502 b.RunParallel(func(pb *testing.PB) {
503 enc := NewEncoder(io.Discard)
504
505 for pb.Next() {
506 if err := enc.Encode(&m); err != nil {
507 b.Fatal("Encode:", err)
508 }
509 }
510 })
511 }
512
513 func BenchmarkEncoderEncode(b *testing.B) {
514 b.ReportAllocs()
515 type T struct {
516 X, Y string
517 }
518 v := &T{"foo", "bar"}
519 b.RunParallel(func(pb *testing.PB) {
520 for pb.Next() {
521 if err := NewEncoder(io.Discard).Encode(v); err != nil {
522 b.Fatal(err)
523 }
524 }
525 })
526 }
527
528 func BenchmarkNumberIsValid(b *testing.B) {
529 s := "-61657.61667E+61673"
530 for i := 0; i < b.N; i++ {
531 isValidNumber(s)
532 }
533 }
534
535 func BenchmarkNumberIsValidRegexp(b *testing.B) {
536 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
537 s := "-61657.61667E+61673"
538 for i := 0; i < b.N; i++ {
539 jsonNumberRegexp.MatchString(s)
540 }
541 }
542
View as plain text