From 970885f01c8bc1fecb7ab1c8ce8e7609bda45530 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Wed, 2 Oct 2019 21:28:10 +0100 Subject: [PATCH] Trivial style tuning on last change. --- benchmark_test.go | 123 ---------------------------------------------- decode.go | 8 ++- limit_test.go | 113 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 125 deletions(-) delete mode 100644 benchmark_test.go create mode 100644 limit_test.go diff --git a/benchmark_test.go b/benchmark_test.go deleted file mode 100644 index ccaf0b4b..00000000 --- a/benchmark_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package yaml_test - -import ( - "strings" - "testing" - - . "gopkg.in/check.v1" - "gopkg.in/yaml.v2" -) - -type testcase struct { - name string - data []byte - error string -} - -func testcases() []testcase { - return []testcase{ - { - name: "1000kb of maps with 100 aliases", - data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`), - error: "yaml: document contains excessive aliasing", - }, - { - name: "1000kb of deeply nested slices", - data: []byte(strings.Repeat(`[`, 1000*1024)), - error: "yaml: exceeded max depth of 10000", - }, - { - name: "1000kb of deeply nested maps", - data: []byte("x: " + strings.Repeat(`{`, 1000*1024)), - error: "yaml: exceeded max depth of 10000", - }, - { - name: "1000kb of deeply nested indents", - data: []byte(strings.Repeat(`- `, 1000*1024)), - error: "yaml: exceeded max depth of 10000", - }, - { - name: "1000kb of 1000-indent lines", - data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)), - }, - {name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)}, - {name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)}, - {name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)}, - {name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)}, - } -} - -func (s *S) TestLimits(c *C) { - if testing.Short() { - return - } - for _, tc := range testcases() { - var v interface{} - err := yaml.Unmarshal(tc.data, &v) - if len(tc.error) > 0 { - c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name)) - } else { - c.Assert(err, IsNil, Commentf("testcase: %s", tc.name)) - } - } -} - -func Benchmark1000KB100Aliases(b *testing.B) { - benchmark(b, "1000kb of maps with 100 aliases") -} -func Benchmark1000KBDeeplyNestedSlices(b *testing.B) { - benchmark(b, "1000kb of deeply nested slices") -} -func Benchmark1000KBDeeplyNestedMaps(b *testing.B) { - benchmark(b, "1000kb of deeply nested maps") -} -func Benchmark1000KBDeeplyNestedIndents(b *testing.B) { - benchmark(b, "1000kb of deeply nested indents") -} -func Benchmark1000KB1000IndentLines(b *testing.B) { - benchmark(b, "1000kb of 1000-indent lines") -} -func Benchmark1KBMaps(b *testing.B) { - benchmark(b, "1kb of maps") -} -func Benchmark10KBMaps(b *testing.B) { - benchmark(b, "10kb of maps") -} -func Benchmark100KBMaps(b *testing.B) { - benchmark(b, "100kb of maps") -} -func Benchmark1000KBMaps(b *testing.B) { - benchmark(b, "1000kb of maps") -} - -func benchmark(b *testing.B, name string) { - var tc testcase - for _, t := range testcases() { - if t.name == name { - tc = t - break - } - } - if tc.name != name { - b.Errorf("testcase %q not found", name) - return - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - var v interface{} - err := yaml.Unmarshal(tc.data, &v) - if len(tc.error) > 0 { - if err == nil { - b.Errorf("expected error, got none") - } else if err.Error() != tc.error { - b.Errorf("expected error '%s', got '%s'", tc.error, err.Error()) - } - } else { - if err != nil { - b.Errorf("unexpected error: %v", err) - } - } - } -} diff --git a/decode.go b/decode.go index 53108765..323b1b79 100644 --- a/decode.go +++ b/decode.go @@ -319,10 +319,14 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm } const ( - // 400,000 decode operations is ~500kb of dense object declarations, or ~5kb of dense object declarations with 10000% alias expansion + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion alias_ratio_range_low = 400000 - // 4,000,000 decode operations is ~5MB of dense object declarations, or ~4.5MB of dense object declarations with 10% alias expansion + + // 4,000,000 decode operations is ~5MB of dense object declarations, or + // ~4.5MB of dense object declarations with 10% alias expansion alias_ratio_range_high = 4000000 + // alias_ratio_range is the range over which we scale allowed alias ratios alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) ) diff --git a/limit_test.go b/limit_test.go new file mode 100644 index 00000000..ba1c0800 --- /dev/null +++ b/limit_test.go @@ -0,0 +1,113 @@ +package yaml_test + +import ( + "strings" + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v2" +) + +var limitTests = []struct { + name string + data []byte + error string +}{ + { + name: "1000kb of maps with 100 aliases", + data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`), + error: "yaml: document contains excessive aliasing", + }, { + name: "1000kb of deeply nested slices", + data: []byte(strings.Repeat(`[`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested maps", + data: []byte("x: " + strings.Repeat(`{`, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of deeply nested indents", + data: []byte(strings.Repeat(`- `, 1000*1024)), + error: "yaml: exceeded max depth of 10000", + }, { + name: "1000kb of 1000-indent lines", + data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)), + }, + {name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)}, + {name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)}, + {name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)}, + {name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)}, +} + +func (s *S) TestLimits(c *C) { + if testing.Short() { + return + } + for _, tc := range limitTests { + var v interface{} + err := yaml.Unmarshal(tc.data, &v) + if len(tc.error) > 0 { + c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name)) + } else { + c.Assert(err, IsNil, Commentf("testcase: %s", tc.name)) + } + } +} + +func Benchmark1000KB100Aliases(b *testing.B) { + benchmark(b, "1000kb of maps with 100 aliases") +} +func Benchmark1000KBDeeplyNestedSlices(b *testing.B) { + benchmark(b, "1000kb of deeply nested slices") +} +func Benchmark1000KBDeeplyNestedMaps(b *testing.B) { + benchmark(b, "1000kb of deeply nested maps") +} +func Benchmark1000KBDeeplyNestedIndents(b *testing.B) { + benchmark(b, "1000kb of deeply nested indents") +} +func Benchmark1000KB1000IndentLines(b *testing.B) { + benchmark(b, "1000kb of 1000-indent lines") +} +func Benchmark1KBMaps(b *testing.B) { + benchmark(b, "1kb of maps") +} +func Benchmark10KBMaps(b *testing.B) { + benchmark(b, "10kb of maps") +} +func Benchmark100KBMaps(b *testing.B) { + benchmark(b, "100kb of maps") +} +func Benchmark1000KBMaps(b *testing.B) { + benchmark(b, "1000kb of maps") +} + +func benchmark(b *testing.B, name string) { + for _, t := range limitTests { + if t.name != name { + continue + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var v interface{} + err := yaml.Unmarshal(t.data, &v) + if len(t.error) > 0 { + if err == nil { + b.Errorf("expected error, got none") + } else if err.Error() != t.error { + b.Errorf("expected error '%s', got '%s'", t.error, err.Error()) + } + } else { + if err != nil { + b.Errorf("unexpected error: %v", err) + } + } + } + + return + } + + b.Errorf("testcase %q not found", name) +}