1 package benchmark_test
2
3 import (
4 "bytes"
5 "io/ioutil"
6 "testing"
7 "time"
8
9 "github.com/pelletier/go-toml/v2"
10 "github.com/stretchr/testify/require"
11 )
12
13 func TestUnmarshalSimple(t *testing.T) {
14 doc := []byte(`A = "hello"`)
15 d := struct {
16 A string
17 }{}
18
19 err := toml.Unmarshal(doc, &d)
20 if err != nil {
21 panic(err)
22 }
23 }
24
25 func BenchmarkUnmarshal(b *testing.B) {
26 b.Run("SimpleDocument", func(b *testing.B) {
27 doc := []byte(`A = "hello"`)
28
29 b.Run("struct", func(b *testing.B) {
30 b.SetBytes(int64(len(doc)))
31 b.ReportAllocs()
32 b.ResetTimer()
33
34 for i := 0; i < b.N; i++ {
35 d := struct {
36 A string
37 }{}
38
39 err := toml.Unmarshal(doc, &d)
40 if err != nil {
41 panic(err)
42 }
43 }
44 })
45
46 b.Run("map", func(b *testing.B) {
47 b.SetBytes(int64(len(doc)))
48 b.ReportAllocs()
49 b.ResetTimer()
50
51 for i := 0; i < b.N; i++ {
52 d := map[string]interface{}{}
53 err := toml.Unmarshal(doc, &d)
54 if err != nil {
55 panic(err)
56 }
57 }
58 })
59 })
60
61 b.Run("ReferenceFile", func(b *testing.B) {
62 bytes, err := ioutil.ReadFile("benchmark.toml")
63 if err != nil {
64 b.Fatal(err)
65 }
66
67 b.Run("struct", func(b *testing.B) {
68 b.SetBytes(int64(len(bytes)))
69 b.ReportAllocs()
70 b.ResetTimer()
71 for i := 0; i < b.N; i++ {
72 d := benchmarkDoc{}
73 err := toml.Unmarshal(bytes, &d)
74 if err != nil {
75 panic(err)
76 }
77 }
78 })
79
80 b.Run("map", func(b *testing.B) {
81 b.SetBytes(int64(len(bytes)))
82 b.ReportAllocs()
83 b.ResetTimer()
84 for i := 0; i < b.N; i++ {
85 d := map[string]interface{}{}
86 err := toml.Unmarshal(bytes, &d)
87 if err != nil {
88 panic(err)
89 }
90 }
91 })
92 })
93
94 b.Run("HugoFrontMatter", func(b *testing.B) {
95 b.SetBytes(int64(len(hugoFrontMatterbytes)))
96 b.ReportAllocs()
97 b.ResetTimer()
98 for i := 0; i < b.N; i++ {
99 d := map[string]interface{}{}
100 err := toml.Unmarshal(hugoFrontMatterbytes, &d)
101 if err != nil {
102 panic(err)
103 }
104 }
105 })
106 }
107
108 func marshal(v interface{}) ([]byte, error) {
109 var b bytes.Buffer
110 enc := toml.NewEncoder(&b)
111 err := enc.Encode(v)
112 return b.Bytes(), err
113 }
114
115 func BenchmarkMarshal(b *testing.B) {
116 b.Run("SimpleDocument", func(b *testing.B) {
117 doc := []byte(`A = "hello"`)
118
119 b.Run("struct", func(b *testing.B) {
120 d := struct {
121 A string
122 }{}
123
124 err := toml.Unmarshal(doc, &d)
125 if err != nil {
126 panic(err)
127 }
128
129 b.ReportAllocs()
130 b.ResetTimer()
131
132 var out []byte
133
134 for i := 0; i < b.N; i++ {
135 out, err = marshal(d)
136 if err != nil {
137 panic(err)
138 }
139 }
140
141 b.SetBytes(int64(len(out)))
142 })
143
144 b.Run("map", func(b *testing.B) {
145 d := map[string]interface{}{}
146 err := toml.Unmarshal(doc, &d)
147 if err != nil {
148 panic(err)
149 }
150
151 b.ReportAllocs()
152 b.ResetTimer()
153
154 var out []byte
155
156 for i := 0; i < b.N; i++ {
157 out, err = marshal(d)
158 if err != nil {
159 panic(err)
160 }
161 }
162
163 b.SetBytes(int64(len(out)))
164 })
165 })
166
167 b.Run("ReferenceFile", func(b *testing.B) {
168 bytes, err := ioutil.ReadFile("benchmark.toml")
169 if err != nil {
170 b.Fatal(err)
171 }
172
173 b.Run("struct", func(b *testing.B) {
174 d := benchmarkDoc{}
175 err := toml.Unmarshal(bytes, &d)
176 if err != nil {
177 panic(err)
178 }
179 b.ReportAllocs()
180 b.ResetTimer()
181
182 var out []byte
183
184 for i := 0; i < b.N; i++ {
185 out, err = marshal(d)
186 if err != nil {
187 panic(err)
188 }
189 }
190
191 b.SetBytes(int64(len(out)))
192 })
193
194 b.Run("map", func(b *testing.B) {
195 d := map[string]interface{}{}
196 err := toml.Unmarshal(bytes, &d)
197 if err != nil {
198 panic(err)
199 }
200
201 b.ReportAllocs()
202 b.ResetTimer()
203
204 var out []byte
205 for i := 0; i < b.N; i++ {
206 out, err = marshal(d)
207 if err != nil {
208 panic(err)
209 }
210 }
211
212 b.SetBytes(int64(len(out)))
213 })
214 })
215
216 b.Run("HugoFrontMatter", func(b *testing.B) {
217 d := map[string]interface{}{}
218 err := toml.Unmarshal(hugoFrontMatterbytes, &d)
219 if err != nil {
220 panic(err)
221 }
222
223 b.ReportAllocs()
224 b.ResetTimer()
225
226 var out []byte
227
228 for i := 0; i < b.N; i++ {
229 out, err = marshal(d)
230 if err != nil {
231 panic(err)
232 }
233 }
234
235 b.SetBytes(int64(len(out)))
236 })
237 }
238
239 type benchmarkDoc struct {
240 Table struct {
241 Key string
242 Subtable struct {
243 Key string
244 }
245 Inline struct {
246 Name struct {
247 First string
248 Last string
249 }
250 Point struct {
251 X int64
252 Y int64
253 }
254 }
255 }
256 String struct {
257 Basic struct {
258 Basic string
259 }
260 Multiline struct {
261 Key1 string
262 Key2 string
263 Key3 string
264 Continued struct {
265 Key1 string
266 Key2 string
267 Key3 string
268 }
269 }
270 Literal struct {
271 Winpath string
272 Winpath2 string
273 Quoted string
274 Regex string
275 Multiline struct {
276 Regex2 string
277 Lines string
278 }
279 }
280 }
281 Integer struct {
282 Key1 int64
283 Key2 int64
284 Key3 int64
285 Key4 int64
286 Underscores struct {
287 Key1 int64
288 Key2 int64
289 Key3 int64
290 }
291 }
292 Float struct {
293 Fractional struct {
294 Key1 float64
295 Key2 float64
296 Key3 float64
297 }
298 Exponent struct {
299 Key1 float64
300 Key2 float64
301 Key3 float64
302 }
303 Both struct {
304 Key float64
305 }
306 Underscores struct {
307 Key1 float64
308 Key2 float64
309 }
310 }
311 Boolean struct {
312 True bool
313 False bool
314 }
315 Datetime struct {
316 Key1 time.Time
317 Key2 time.Time
318 Key3 time.Time
319 }
320 Array struct {
321 Key1 []int64
322 Key2 []string
323 Key3 [][]int64
324
325 Key4 []interface{}
326 Key5 []int64
327 Key6 []int64
328 }
329 Products []struct {
330 Name string
331 Sku int64
332 Color string
333 }
334 Fruit []struct {
335 Name string
336 Physical struct {
337 Color string
338 Shape string
339 }
340 Variety []struct {
341 Name string
342 }
343 }
344 }
345
346 func TestUnmarshalReferenceFile(t *testing.T) {
347 bytes, err := ioutil.ReadFile("benchmark.toml")
348 require.NoError(t, err)
349 d := benchmarkDoc{}
350 err = toml.Unmarshal(bytes, &d)
351 require.NoError(t, err)
352
353 expected := benchmarkDoc{
354 Table: struct {
355 Key string
356 Subtable struct{ Key string }
357 Inline struct {
358 Name struct {
359 First string
360 Last string
361 }
362 Point struct {
363 X int64
364 Y int64
365 }
366 }
367 }{
368 Key: "value",
369 Subtable: struct{ Key string }{
370 Key: "another value",
371 },
372
373 Inline: struct {
374 Name struct {
375 First string
376 Last string
377 }
378 Point struct {
379 X int64
380 Y int64
381 }
382 }{
383 Name: struct {
384 First string
385 Last string
386 }{
387 First: "Tom",
388 Last: "Preston-Werner",
389 },
390 Point: struct {
391 X int64
392 Y int64
393 }{
394 X: 1,
395 Y: 2,
396 },
397 },
398 },
399 String: struct {
400 Basic struct{ Basic string }
401 Multiline struct {
402 Key1 string
403 Key2 string
404 Key3 string
405 Continued struct {
406 Key1 string
407 Key2 string
408 Key3 string
409 }
410 }
411 Literal struct {
412 Winpath string
413 Winpath2 string
414 Quoted string
415 Regex string
416 Multiline struct {
417 Regex2 string
418 Lines string
419 }
420 }
421 }{
422 Basic: struct{ Basic string }{
423 Basic: "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.",
424 },
425 Multiline: struct {
426 Key1 string
427 Key2 string
428 Key3 string
429 Continued struct {
430 Key1 string
431 Key2 string
432 Key3 string
433 }
434 }{
435 Key1: "One\nTwo",
436 Key2: "One\nTwo",
437 Key3: "One\nTwo",
438
439 Continued: struct {
440 Key1 string
441 Key2 string
442 Key3 string
443 }{
444 Key1: `The quick brown fox jumps over the lazy dog.`,
445 Key2: `The quick brown fox jumps over the lazy dog.`,
446 Key3: `The quick brown fox jumps over the lazy dog.`,
447 },
448 },
449 Literal: struct {
450 Winpath string
451 Winpath2 string
452 Quoted string
453 Regex string
454 Multiline struct {
455 Regex2 string
456 Lines string
457 }
458 }{
459 Winpath: `C:\Users\nodejs\templates`,
460 Winpath2: `\\ServerX\admin$\system32\`,
461 Quoted: `Tom "Dubs" Preston-Werner`,
462 Regex: `<\i\c*\s*>`,
463
464 Multiline: struct {
465 Regex2 string
466 Lines string
467 }{
468 Regex2: `I [dw]on't need \d{2} apples`,
469 Lines: `The first newline is
470 trimmed in raw strings.
471 All other whitespace
472 is preserved.
473 `,
474 },
475 },
476 },
477 Integer: struct {
478 Key1 int64
479 Key2 int64
480 Key3 int64
481 Key4 int64
482 Underscores struct {
483 Key1 int64
484 Key2 int64
485 Key3 int64
486 }
487 }{
488 Key1: 99,
489 Key2: 42,
490 Key3: 0,
491 Key4: -17,
492
493 Underscores: struct {
494 Key1 int64
495 Key2 int64
496 Key3 int64
497 }{
498 Key1: 1000,
499 Key2: 5349221,
500 Key3: 12345,
501 },
502 },
503 Float: struct {
504 Fractional struct {
505 Key1 float64
506 Key2 float64
507 Key3 float64
508 }
509 Exponent struct {
510 Key1 float64
511 Key2 float64
512 Key3 float64
513 }
514 Both struct{ Key float64 }
515 Underscores struct {
516 Key1 float64
517 Key2 float64
518 }
519 }{
520 Fractional: struct {
521 Key1 float64
522 Key2 float64
523 Key3 float64
524 }{
525 Key1: 1.0,
526 Key2: 3.1415,
527 Key3: -0.01,
528 },
529 Exponent: struct {
530 Key1 float64
531 Key2 float64
532 Key3 float64
533 }{
534 Key1: 5e+22,
535 Key2: 1e6,
536 Key3: -2e-2,
537 },
538 Both: struct{ Key float64 }{
539 Key: 6.626e-34,
540 },
541 Underscores: struct {
542 Key1 float64
543 Key2 float64
544 }{
545 Key1: 9224617.445991228313,
546 Key2: 1e100,
547 },
548 },
549 Boolean: struct {
550 True bool
551 False bool
552 }{
553 True: true,
554 False: false,
555 },
556 Datetime: struct {
557 Key1 time.Time
558 Key2 time.Time
559 Key3 time.Time
560 }{
561 Key1: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
562 Key2: time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", -7*3600)),
563 Key3: time.Date(1979, 5, 27, 0, 32, 0, 999999000, time.FixedZone("", -7*3600)),
564 },
565 Array: struct {
566 Key1 []int64
567 Key2 []string
568 Key3 [][]int64
569 Key4 []interface{}
570 Key5 []int64
571 Key6 []int64
572 }{
573 Key1: []int64{1, 2, 3},
574 Key2: []string{"red", "yellow", "green"},
575 Key3: [][]int64{{1, 2}, {3, 4, 5}},
576 Key4: []interface{}{
577 []interface{}{int64(1), int64(2)},
578 []interface{}{"a", "b", "c"},
579 },
580 Key5: []int64{1, 2, 3},
581 Key6: []int64{1, 2},
582 },
583 Products: []struct {
584 Name string
585 Sku int64
586 Color string
587 }{
588 {
589 Name: "Hammer",
590 Sku: 738594937,
591 },
592 {},
593 {
594 Name: "Nail",
595 Sku: 284758393,
596 Color: "gray",
597 },
598 },
599 Fruit: []struct {
600 Name string
601 Physical struct {
602 Color string
603 Shape string
604 }
605 Variety []struct{ Name string }
606 }{
607 {
608 Name: "apple",
609 Physical: struct {
610 Color string
611 Shape string
612 }{
613 Color: "red",
614 Shape: "round",
615 },
616 Variety: []struct{ Name string }{
617 {Name: "red delicious"},
618 {Name: "granny smith"},
619 },
620 },
621 {
622 Name: "banana",
623 Variety: []struct{ Name string }{
624 {Name: "plantain"},
625 },
626 },
627 },
628 }
629
630 require.Equal(t, expected, d)
631 }
632
633 var hugoFrontMatterbytes = []byte(`
634 categories = ["Development", "VIM"]
635 date = "2012-04-06"
636 description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
637 slug = "spf13-vim-3-0-release-and-new-website"
638 tags = [".vimrc", "plugins", "spf13-vim", "vim"]
639 title = "spf13-vim 3.0 release and new website"
640 include_toc = true
641 show_comments = false
642
643 [[cascade]]
644 background = "yosemite.jpg"
645 [cascade._target]
646 kind = "page"
647 lang = "en"
648 path = "/blog/**"
649
650 [[cascade]]
651 background = "goldenbridge.jpg"
652 [cascade._target]
653 kind = "section"
654 `)
655
View as plain text