Skip to content

Commit

Permalink
Merge pull request #38 from tdakkota/perf/optimize-equalKey
Browse files Browse the repository at this point in the history
perf: optimize `equalKey`
  • Loading branch information
tdakkota committed Nov 2, 2022
2 parents 7c2bbec + f5a3b57 commit a443d26
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
30 changes: 22 additions & 8 deletions equal_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ package yaml
// original values. It could be a problem if the original value is escaped or written in different style (10 vs 1e1).
// But impact of this difference is very small, go-yaml compares the original values too.
func (n *Node) equalKey(b *Node) bool {
if n == nil || b == nil {
switch {
case n == nil || b == nil:
return false
}
if n.Kind != b.Kind {
case n == b:
return true
case n.Kind != b.Kind:
return false
}

switch n.Kind {
case ScalarNode:
// FIXME(tdakkota): compare canonical values.
Expand All @@ -35,16 +38,27 @@ func (n *Node) equalKey(b *Node) bool {
if len(n.Content) != len(b.Content) {
return false
}

type nodePair struct {
Key *Node
Val *Node
}

switch len(n.Content) {
case 0:
return true
case 2:
a := nodePair{n.Content[0], n.Content[1]}
b := nodePair{b.Content[0], b.Content[1]}
return a.Key.equalKey(b.Key) && a.Val.equalKey(b.Val)
}

type nodeKey struct {
Kind Kind
Value string
ContentLen int
}
type nodePair struct {
Key *Node
Val *Node
}
nodes := make(map[nodeKey][]nodePair, len(n.Content)/2)
nodes := map[nodeKey][]nodePair{}
for i := 0; i < len(n.Content); i += 2 {
key := n.Content[i]
value := n.Content[i+1]
Expand Down
18 changes: 18 additions & 0 deletions equal_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ func TestNode_equalKey(t *testing.T) {
}
return &n
}
theNode := mustNode(`a: 1`)

tests := []struct {
a, b *Node
want bool
}{
// Same node.
{theNode, theNode, true},

// Nil nodes.
{nil, nil, false},
{nil, mustNode("null"), false},
Expand All @@ -30,6 +34,9 @@ func TestNode_equalKey(t *testing.T) {
{mustNode("{}"), mustNode("[]"), false},
{mustNode("{}"), mustNode("true"), false},
{mustNode("[]"), mustNode("true"), false},
{mustNode("{}: 1"), mustNode("a: 1"), false},
{mustNode("{{}: 1}: 1"), mustNode("{a: 1}: 1"), false},
{mustNode("{{}: 1, b: 2}: 1"), mustNode("{a: 1, b: 2}: 1"), false},

// Scalars.
{mustNode("null"), mustNode("null"), true},
Expand All @@ -55,6 +62,7 @@ func TestNode_equalKey(t *testing.T) {
{mustNode("[0, 1]"), mustNode("[0]"), false},

// Objects.
{mustNode("{}"), mustNode("{}"), true},
{mustNode("a: 1"), mustNode("a: 1"), true},
{mustNode(`{"a": 1}`), mustNode("a: 1"), true},
{mustNode("a: 1"), mustNode("a: 1 # comment"), true},
Expand All @@ -64,6 +72,7 @@ func TestNode_equalKey(t *testing.T) {
{mustNode("a: 1"), mustNode("a: 2"), false},
{mustNode("a: 1"), mustNode("b: 1"), false},
{mustNode("a: 1\nb: 1"), mustNode("b: 1"), false},
{mustNode("a: 1\nb: 1\nc: 1"), mustNode("a: 1\nb: 1\nc: 2"), false},

// Objects with complex keys.
{mustNode("[]: 1"), mustNode("[]: 1"), true},
Expand All @@ -72,9 +81,18 @@ func TestNode_equalKey(t *testing.T) {
{mustNode("{b: 1, a: 1}: 1"), mustNode("{a: 1, b: 1}: 1"), true},

{mustNode("{a: 1}: 1"), mustNode("{a: 2}: 1"), false},
{mustNode("{a: 1, b: 2}: 1"), mustNode("{a: 1}: 1"), false},
{mustNode("{b: 1, a: 1}: 1"), mustNode("{a: 1, b: []}: 1"), false},

// Canonical representation. Currently not supported, but should be.
// !int
{mustNode("10"), mustNode("+10"), false},
{mustNode("10"), mustNode("0xa"), false},
{mustNode("10"), mustNode("012"), false},
{mustNode("10"), mustNode("0b1010"), false},
{mustNode("0xA"), mustNode("0xa"), false},
// !!float
{mustNode("10"), mustNode("10.0"), false},
{mustNode("10"), mustNode("1e1"), false},
}
for i, tt := range tests {
Expand Down

0 comments on commit a443d26

Please sign in to comment.