Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop checking uniqueKeys once we've found 10 errors #544

Open
wants to merge 3 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions decode.go
Expand Up @@ -746,12 +746,16 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
if d.uniqueKeys {
nerrs := len(d.terrors)
UNIQUECHECK:
for i := 0; i < l; i += 2 {
ni := n.Content[i]
for j := i + 2; j < l; j += 2 {
nj := n.Content[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
if len(d.terrors)-nerrs >= 10 {
continue UNIQUECHECK
}
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions decode_test.go
Expand Up @@ -803,6 +803,19 @@ var unmarshalTests = []struct {
},
},

// Explicit mapping
{
`
a:
b
b:
? a
: a`,
&M{"a": "b",
"b": M{
"a": "a",
}},
},
}

type M map[string]interface{}
Expand Down
21 changes: 20 additions & 1 deletion limit_test.go
@@ -1,6 +1,7 @@
package yaml_test

import (
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -29,6 +30,10 @@ var limitTests = []struct {
name: "1000kb of deeply nested indents",
data: []byte(strings.Repeat(`- `, 1000*1024)),
error: "yaml: exceeded max depth of 10000",
}, {
name: "1000 duplicate map keys",
data: []byte(`{` + strings.Repeat(`a,`, 1000) + `}`),
error: "(?s).*already defined at line.*",
}, {
name: "1000kb of 1000-indent lines",
data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)),
Expand All @@ -37,6 +42,8 @@ var limitTests = []struct {
{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) + `]`)},
{name: "1000kb slice nested at max-depth", data: []byte(strings.Repeat(`[`, 10000) + `1` + strings.Repeat(`,1`, 1000*1024/2-20000-1) + strings.Repeat(`]`, 10000))},
{name: "1000kb slice nested in maps at max-depth", data: []byte("{a,b:\n" + strings.Repeat(" {a,b:", 10000-2) + ` [1` + strings.Repeat(",1", 1000*1024/2-6*10000-1) + `]` + strings.Repeat(`}`, 10000-1))},
}

func (s *S) TestLimits(c *C) {
Expand Down Expand Up @@ -82,6 +89,18 @@ func Benchmark1000KBMaps(b *testing.B) {
benchmark(b, "1000kb of maps")
}

func BenchmarkDeepSlice(b *testing.B) {
benchmark(b, "1000kb slice nested at max-depth")
}

func BenchmarkDeepFlow(b *testing.B) {
benchmark(b, "1000kb slice nested in maps at max-depth")
}

func BenchmarkDuplicateKeys(b *testing.B) {
benchmark(b, "1000 duplicate map keys")
}

func benchmark(b *testing.B, name string) {
for _, t := range limitTests {
if t.name != name {
Expand All @@ -96,7 +115,7 @@ func benchmark(b *testing.B, name string) {
if len(t.error) > 0 {
if err == nil {
b.Errorf("expected error, got none")
} else if err.Error() != t.error {
} else if match, _ := regexp.MatchString(`^`+t.error+`$`, err.Error()); !match {
b.Errorf("expected error '%s', got '%s'", t.error, err.Error())
}
} else {
Expand Down