1
16
17 package encoder
18
19 import (
20 `bytes`
21 `encoding`
22 `encoding/json`
23 `runtime`
24 `runtime/debug`
25 `strconv`
26 `sync`
27 `testing`
28 `time`
29
30 `github.com/bytedance/sonic/internal/rt`
31 `github.com/stretchr/testify/require`
32 )
33
34 func TestMain(m *testing.M) {
35 go func () {
36 if !debugAsyncGC {
37 return
38 }
39 println("Begin GC looping...")
40 for {
41 runtime.GC()
42 debug.FreeOSMemory()
43 }
44 println("stop GC looping!")
45 }()
46 time.Sleep(time.Millisecond)
47 m.Run()
48 }
49
50 func TestGC(t *testing.T) {
51 if debugSyncGC {
52 return
53 }
54 out, err := Encode(_GenericValue, 0)
55 if err != nil {
56 t.Fatal(err)
57 }
58 n := len(out)
59 wg := &sync.WaitGroup{}
60 N := 10000
61 for i:=0; i<N; i++ {
62 wg.Add(1)
63 go func (wg *sync.WaitGroup, size int) {
64 defer wg.Done()
65 out, err := Encode(_GenericValue, 0)
66 if err != nil {
67 t.Error(err)
68 return
69 }
70 if len(out) != size {
71 t.Error(len(out), size)
72 return
73 }
74 runtime.GC()
75 }(wg, n)
76 }
77 wg.Wait()
78 }
79
80 type sample struct {
81 M map[string]interface{}
82 S []interface{}
83 A [0]interface{}
84 MP *map[string]interface{}
85 SP *[]interface{}
86 AP *[0]interface{}
87 }
88
89 func BenchmarkOptionSliceOrMapNoNull(b *testing.B) {
90 b.Run("true", func (b *testing.B) {
91 obj := sample{}
92 _, err := Encode(obj, NoNullSliceOrMap)
93 if err != nil {
94 b.Fatal(err)
95 }
96 b.ResetTimer()
97 for i:=0;i<b.N;i++{
98 _, _ = Encode(obj, NoNullSliceOrMap)
99 }
100 })
101
102 b.Run("false", func (b *testing.B) {
103 obj2 := sample{}
104 _, err := Encode(obj2, 0)
105 if err != nil {
106 b.Fatal(err)
107 }
108 for i:=0;i<b.N;i++{
109 _, _ = Encode(obj2, 0)
110 }
111 })
112 }
113
114 func runEncoderTest(t *testing.T, fn func(string)string, exp string, arg string) {
115 require.Equal(t, exp, fn(arg))
116 }
117
118 func TestEncoder_String(t *testing.T) {
119 runEncoderTest(t, Quote, `""` , "")
120 runEncoderTest(t, Quote, `"hello, world"` , "hello, world")
121 runEncoderTest(t, Quote, `"hello啊啊啊aa"` , "hello啊啊啊aa")
122 runEncoderTest(t, Quote, `"hello\\\"world"` , "hello\\\"world")
123 runEncoderTest(t, Quote, `"hello\n\tworld"` , "hello\n\tworld")
124 runEncoderTest(t, Quote, `"hello\u0000\u0001world"` , "hello\x00\x01world")
125 runEncoderTest(t, Quote, `"hello\u0000\u0001world"` , "hello\x00\x01world")
126 runEncoderTest(t, Quote, `"Cartoonist, Illustrator, and T-Shirt connoisseur"` , "Cartoonist, Illustrator, and T-Shirt connoisseur")
127 }
128
129 type StringStruct struct {
130 X *int `json:"x,string,omitempty"`
131 Y []int `json:"y"`
132 Z json.Number `json:"z,string"`
133 W string `json:"w,string"`
134 }
135
136 func TestEncoder_FieldStringize(t *testing.T) {
137 x := 12345
138 v := StringStruct{X: &x, Y: []int{1, 2, 3}, Z: "4567456", W: "asdf"}
139 r, e := Encode(v, 0)
140 require.NoError(t, e)
141 println(string(r))
142 }
143
144 func TestEncodeErrorAndScratchBuf(t *testing.T) {
145 var obj = map[string]interface{}{
146 "a": json.RawMessage(" [} "),
147 }
148 buf := make([]byte, 0, 10)
149 _ = EncodeInto(&buf, obj, 0)
150 if len(buf) < 0 || len(buf) > 10 {
151 t.Fatal()
152 }
153 }
154
155 type MarshalerImpl struct {
156 X int
157 }
158
159 func (self *MarshalerImpl) MarshalJSON() ([]byte, error) {
160 ret := []byte(strconv.Itoa(self.X))
161 return append(ret, " "...), nil
162 }
163
164 type MarshalerStruct struct {
165 V MarshalerImpl
166 }
167
168 type MarshalerErrorStruct struct {
169 V MarshalerImpl
170 }
171
172 func (self *MarshalerErrorStruct) MarshalJSON() ([]byte, error) {
173 return []byte(`[""] {`), nil
174 }
175
176 type RawMessageStruct struct {
177 X json.RawMessage
178 }
179
180 type TextMarshalerImpl struct {
181 X string
182 }
183
184 func (self *TextMarshalerImpl) MarshalText() ([]byte, error) {
185 return []byte(self.X), nil
186 }
187
188 type TextMarshalerImplV struct {
189 X string
190 }
191
192 func (self TextMarshalerImplV) MarshalText() ([]byte, error) {
193 return []byte(self.X), nil
194 }
195
196 type TextMarshalerStruct struct {
197 V TextMarshalerImpl
198 }
199
200 func TestTextMarshalTextKey_SortKeys(t *testing.T) {
201 v := map[*TextMarshalerImpl]string{
202 {"b"}: "b",
203 {"c"}: "c",
204 {"a"}: "a",
205 }
206 ret, err := Encode(v, SortMapKeys)
207 require.NoError(t, err)
208 require.Equal(t, `{"a":"a","b":"b","c":"c"}`, string(ret))
209
210 v2 := map[TextMarshalerImplV]string{
211 {"b"}: "b",
212 {"c"}: "c",
213 {"a"}: "a",
214 }
215 ret, err = Encode(v2, SortMapKeys)
216 require.NoError(t, err)
217 require.Equal(t, `{"a":"a","b":"b","c":"c"}`, string(ret))
218
219 v3 := map[encoding.TextMarshaler]string{
220 TextMarshalerImplV{"b"}: "b",
221 &TextMarshalerImpl{"c"}: "c",
222 TextMarshalerImplV{"a"}: "a",
223 }
224 ret, err = Encode(v3, SortMapKeys)
225 require.NoError(t, err)
226 require.Equal(t, `{"a":"a","b":"b","c":"c"}`, string(ret))
227 }
228
229 func TestEncoder_EscapeHTML(t *testing.T) {
230
231 test := []string{
232 "&&&&&&&&&&&&&&&&&&&&&&&\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
233 "{\"\"\u2028\x94\xe2\x00\x00\x00\x00\x00\x00\x00\x00\u2028\x80\u2028\x80\u2028\xe2\u2028\x8a\u2028⑀\xa8\x8a\xa8\xe2\u2028\xe2\u2028\xe2\u2028\xe2\u2000\x8d\xe2\u2028\xe2\u2028\xe2\xe2\xa8\"}",
234 }
235 for _, s := range(test) {
236 data := []byte(s)
237 sdst := HTMLEscape(nil, data)
238 var dst bytes.Buffer
239 json.HTMLEscape(&dst, data)
240 require.Equal(t, string(sdst), dst.String())
241 }
242 }
243
244 func TestEncoder_Marshal_EscapeHTML_LargeJson(t *testing.T) {
245 buf1, err1 := Encode(&_BindingValue, SortMapKeys | EscapeHTML)
246 require.NoError(t, err1)
247 buf2, err2 :=json.Marshal(&_BindingValue)
248 require.NoError(t, err2)
249 require.Equal(t, buf1, buf2)
250 }
251
252 var _GenericValue interface{}
253 var _BindingValue TwitterStruct
254
255 func init() {
256 _ = json.Unmarshal([]byte(TwitterJson), &_GenericValue)
257 _ = json.Unmarshal([]byte(TwitterJson), &_BindingValue)
258 }
259
260 func TestEncoder_Generic(t *testing.T) {
261 v, e := Encode(_GenericValue, 0)
262 require.NoError(t, e)
263 println(string(v))
264 }
265
266 func TestEncoder_Binding(t *testing.T) {
267 v, e := Encode(_BindingValue, 0)
268 require.NoError(t, e)
269 println(string(v))
270 }
271
272 func TestEncoder_MapSortKey(t *testing.T) {
273 m := map[string]string {
274 "C": "third",
275 "D": "forth",
276 "A": "first",
277 "F": "sixth",
278 "E": "fifth",
279 "B": "second",
280 }
281 v, e := Encode(m, SortMapKeys)
282 require.NoError(t, e)
283 require.Equal(t, `{"A":"first","B":"second","C":"third","D":"forth","E":"fifth","F":"sixth"}`, string(v))
284 }
285
286 func BenchmarkEncoder_Generic_Sonic(b *testing.B) {
287 _, _ = Encode(_GenericValue, SortMapKeys | EscapeHTML | CompactMarshaler)
288 b.SetBytes(int64(len(TwitterJson)))
289 b.ResetTimer()
290 for i := 0; i < b.N; i++ {
291 _, _ = Encode(_GenericValue, SortMapKeys | EscapeHTML | CompactMarshaler)
292 }
293 }
294
295 func BenchmarkEncoder_Generic_Sonic_Fast(b *testing.B) {
296 _, _ = Encode(_GenericValue, 0)
297 b.SetBytes(int64(len(TwitterJson)))
298 b.ResetTimer()
299 for i := 0; i < b.N; i++ {
300 _, _ = Encode(_GenericValue, 0)
301 }
302 }
303
304 func BenchmarkEncoder_Generic_StdLib(b *testing.B) {
305 _, _ = json.Marshal(_GenericValue)
306 b.SetBytes(int64(len(TwitterJson)))
307 b.ResetTimer()
308 for i := 0; i < b.N; i++ {
309 _, _ = json.Marshal(_GenericValue)
310 }
311 }
312
313 func BenchmarkEncoder_Binding_Sonic(b *testing.B) {
314 _, _ = Encode(&_BindingValue, SortMapKeys | EscapeHTML | CompactMarshaler)
315 b.SetBytes(int64(len(TwitterJson)))
316 b.ResetTimer()
317 for i := 0; i < b.N; i++ {
318 _, _ = Encode(&_BindingValue, SortMapKeys | EscapeHTML | CompactMarshaler)
319 }
320 }
321
322 func BenchmarkEncoder_Binding_Sonic_Fast(b *testing.B) {
323 _, _ = Encode(&_BindingValue, NoQuoteTextMarshaler)
324 b.SetBytes(int64(len(TwitterJson)))
325 b.ResetTimer()
326 for i := 0; i < b.N; i++ {
327 _, _ = Encode(&_BindingValue, NoQuoteTextMarshaler)
328 }
329 }
330
331 func BenchmarkEncoder_Binding_StdLib(b *testing.B) {
332 _, _ = json.Marshal(&_BindingValue)
333 b.SetBytes(int64(len(TwitterJson)))
334 b.ResetTimer()
335 for i := 0; i < b.N; i++ {
336 _, _ = json.Marshal(&_BindingValue)
337 }
338 }
339
340 func BenchmarkEncoder_Parallel_Generic_Sonic(b *testing.B) {
341 _, _ = Encode(_GenericValue, SortMapKeys | EscapeHTML | CompactMarshaler)
342 b.SetBytes(int64(len(TwitterJson)))
343 b.ResetTimer()
344 b.RunParallel(func(pb *testing.PB) {
345 for pb.Next() {
346 _, _ = Encode(_GenericValue, SortMapKeys | EscapeHTML | CompactMarshaler)
347 }
348 })
349 }
350
351 func BenchmarkEncoder_Parallel_Generic_Sonic_Fast(b *testing.B) {
352 _, _ = Encode(_GenericValue, NoQuoteTextMarshaler)
353 b.SetBytes(int64(len(TwitterJson)))
354 b.ResetTimer()
355 b.RunParallel(func(pb *testing.PB) {
356 for pb.Next() {
357 _, _ = Encode(_GenericValue, NoQuoteTextMarshaler)
358 }
359 })
360 }
361
362 func BenchmarkEncoder_Parallel_Generic_StdLib(b *testing.B) {
363 _, _ = json.Marshal(_GenericValue)
364 b.SetBytes(int64(len(TwitterJson)))
365 b.ResetTimer()
366 b.RunParallel(func(pb *testing.PB) {
367 for pb.Next() {
368 _, _ = json.Marshal(_GenericValue)
369 }
370 })
371 }
372
373 func BenchmarkEncoder_Parallel_Binding_Sonic(b *testing.B) {
374 _, _ = Encode(&_BindingValue, SortMapKeys | EscapeHTML | CompactMarshaler)
375 b.SetBytes(int64(len(TwitterJson)))
376 b.ResetTimer()
377 b.RunParallel(func(pb *testing.PB) {
378 for pb.Next() {
379 _, _ = Encode(&_BindingValue, SortMapKeys | EscapeHTML | CompactMarshaler)
380 }
381 })
382 }
383
384 func BenchmarkEncoder_Parallel_Binding_Sonic_Fast(b *testing.B) {
385 _, _ = Encode(&_BindingValue, NoQuoteTextMarshaler)
386 b.SetBytes(int64(len(TwitterJson)))
387 b.ResetTimer()
388 b.RunParallel(func(pb *testing.PB) {
389 for pb.Next() {
390 _, _ = Encode(&_BindingValue, NoQuoteTextMarshaler)
391 }
392 })
393 }
394
395 func BenchmarkEncoder_Parallel_Binding_StdLib(b *testing.B) {
396 _, _ = json.Marshal(&_BindingValue)
397 b.SetBytes(int64(len(TwitterJson)))
398 b.ResetTimer()
399 b.RunParallel(func(pb *testing.PB) {
400 for pb.Next() {
401 _, _ = json.Marshal(&_BindingValue)
402 }
403 })
404 }
405
406 func BenchmarkHTMLEscape_Sonic(b *testing.B) {
407 jsonByte := []byte(TwitterJson)
408 b.SetBytes(int64(len(TwitterJson)))
409 b.ResetTimer()
410 var buf []byte
411 for i := 0; i < b.N; i++ {
412 buf = HTMLEscape(nil, jsonByte)
413 }
414 _ = buf
415 }
416
417 func BenchmarkHTMLEscape_StdLib(b *testing.B) {
418 jsonByte := []byte(TwitterJson)
419 b.SetBytes(int64(len(TwitterJson)))
420 b.ResetTimer()
421 var buf []byte
422 for i := 0; i < b.N; i++ {
423 out := bytes.NewBuffer(make([]byte, 0, len(TwitterJson) * 6 / 5))
424 json.HTMLEscape(out, jsonByte)
425 buf = out.Bytes()
426 }
427 _ = buf
428 }
429
430
431 func BenchmarkValidate_Sonic(b *testing.B) {
432 var data = rt.Str2Mem(TwitterJson)
433 ok, s := Valid(data)
434 if !ok {
435 b.Fatal(s)
436 }
437 b.SetBytes(int64(len(TwitterJson)))
438 b.ResetTimer()
439 for i:=0; i<b.N; i++ {
440 _, _ = Valid(data)
441 }
442 }
443
444 func BenchmarkValidate_Std(b *testing.B) {
445 var data = rt.Str2Mem(TwitterJson)
446 if !json.Valid(data) {
447 b.Fatal()
448 }
449 b.SetBytes(int64(len(TwitterJson)))
450 b.ResetTimer()
451 for i:=0; i<b.N; i++ {
452 _ = json.Valid(data)
453 }
454 }
455
456 func BenchmarkCompact_Std(b *testing.B) {
457 var data = rt.Str2Mem(TwitterJson)
458 var dst = bytes.NewBuffer(nil)
459 if err := json.Compact(dst, data); err != nil {
460 b.Fatal(err)
461 }
462 b.SetBytes(int64(len(TwitterJson)))
463 b.ResetTimer()
464 for i:=0; i<b.N; i++ {
465 dst.Reset()
466 _ = json.Compact(dst, data)
467 }
468 }
469
470 type f64Bench struct {
471 name string
472 float float64
473 }
474 func BenchmarkEncode_Float64(b *testing.B) {
475 var bench = []f64Bench{
476 {"Zero", 0},
477 {"ShortDecimal", 1000},
478 {"Decimal", 33909},
479 {"Float", 339.7784},
480 {"Exp", -5.09e75},
481 {"NegExp", -5.11e-95},
482 {"LongExp", 1.234567890123456e-78},
483 {"Big", 123456789123456789123456789},
484
485 }
486 maxUint := "18446744073709551615"
487 for i := 1; i <= len(maxUint); i++ {
488 name := strconv.FormatInt(int64(i), 10) + "-Digs"
489 num, _ := strconv.ParseUint(string(maxUint[:i]), 10, 64)
490 bench = append(bench, f64Bench{name, float64(num)})
491 }
492 for _, c := range bench {
493 libs := []struct {
494 name string
495 test func(*testing.B)
496 }{{
497 name: "StdLib",
498 test: func(b *testing.B) { _, _ = json.Marshal(c.float); for i := 0; i < b.N; i++ { _, _ = json.Marshal(c.float) }},
499 }, {
500 name: "Sonic",
501 test: func(b *testing.B) { _, _ = Encode(c.float, 0); for i := 0; i < b.N; i++ { _, _ = Encode(c.float, 0) }},
502 }}
503 for _, lib := range libs {
504 name := lib.name + "_" + c.name
505 b.Run(name, lib.test)
506 }
507 }
508 }
509
510 type f32Bench struct {
511 name string
512 float float32
513 }
514 func BenchmarkEncode_Float32(b *testing.B) {
515 var bench = []f32Bench{
516 {"Zero", 0},
517 {"ShortDecimal", 1000},
518 {"Decimal", 33909},
519 {"ExactFraction", 3.375},
520 {"Point", 339.7784},
521 {"Exp", -5.09e25},
522 {"NegExp", -5.11e-25},
523 {"Shortest", 1.234567e-8},
524 }
525
526 maxUint := "18446744073709551615"
527 for i := 1; i <= len(maxUint); i++ {
528 name := strconv.FormatInt(int64(i), 10) + "-Digs"
529 num, _ := strconv.ParseUint(string(maxUint[:i]), 10, 64)
530 bench = append(bench, f32Bench{name, float32(num)})
531 }
532 for _, c := range bench {
533 libs := []struct {
534 name string
535 test func(*testing.B)
536 }{{
537 name: "StdLib",
538 test: func(b *testing.B) { _, _ = json.Marshal(c.float); for i := 0; i < b.N; i++ { _, _ = json.Marshal(c.float) }},
539 }, {
540 name: "Sonic",
541 test: func(b *testing.B) { _, _ = Encode(c.float, 0); for i := 0; i < b.N; i++ { _, _ = Encode(c.float, 0) }},
542 }}
543 for _, lib := range libs {
544 name := lib.name + "_" + c.name
545 b.Run(name, lib.test)
546 }
547 }
548 }
549
View as plain text