Use symmetric difference iterator #11

Merged
roysc merged 27 commits from symmetric-diff-iterator into main 2023-09-20 03:22:19 +00:00
2 changed files with 55 additions and 9 deletions
Showing only changes of commit d2224af6b5 - Show all commits

View File

@ -11,6 +11,7 @@ type symmDiffIterator struct {
a, b iterState // Nodes returned are those in b - a and a - b (keys only) a, b iterState // Nodes returned are those in b - a and a - b (keys only)
yieldFromA bool // Whether next node comes from a yieldFromA bool // Whether next node comes from a
count int // Number of nodes scanned on either trie count int // Number of nodes scanned on either trie
eqPathIndex int // Count index of last pair of equal paths, to detect an updated key
} }
// NewDifferenceIterator constructs a trie.NodeIterator that iterates over the exclusive elements in b that // NewDifferenceIterator constructs a trie.NodeIterator that iterates over the exclusive elements in b that
@ -47,6 +48,12 @@ func (it *symmDiffIterator) FromA() bool {
return it.yieldFromA return it.yieldFromA
} }
// CommonPath returns true if a node with the current path exists in each sub-iterator - i.e. it
// represents an updated node.
func (it *symmDiffIterator) CommonPath() bool {
return it.count-it.eqPathIndex <= 1
}
func (it *symmDiffIterator) Hash() common.Hash { func (it *symmDiffIterator) Hash() common.Hash {
return it.curr().Hash() return it.curr().Hash()
} }
@ -110,7 +117,12 @@ func (it *symmDiffIterator) seek() {
return return
} }
switch compareNodes(&it.a, &it.b) { cmp := bytes.Compare(it.a.Path(), it.b.Path())
if cmp == 0 {
it.eqPathIndex = it.count
cmp = compareValues(&it.a, &it.b)
}
switch cmp {
case -1: case -1:
it.yieldFromA = true it.yieldFromA = true
return return
@ -136,10 +148,7 @@ func (it *symmDiffIterator) Error() error {
} }
// Compares nodes with equal paths by value // Compares nodes with equal paths by value
func compareNodes(a, b trie.NodeIterator) int { func compareValues(a, b trie.NodeIterator) int {
if cmp := bytes.Compare(a.Path(), b.Path()); cmp != 0 {
return cmp
}
if a.Leaf() && !b.Leaf() { if a.Leaf() && !b.Leaf() {
return -1 return -1
} else if b.Leaf() && !a.Leaf() { } else if b.Leaf() && !a.Leaf() {

View File

@ -50,7 +50,26 @@ func TestSymmetricDifferenceIterator(t *testing.T) {
for di.Next(true) { for di.Next(true) {
t.Errorf("iterator should not yield any elements") t.Errorf("iterator should not yield any elements")
} }
assert.NotEqual(t, 0, count) assert.Equal(t, 4, *count)
})
t.Run("with one difference", func(t *testing.T) {
dba := trie.NewDatabase(rawdb.NewMemoryDatabase())
triea := trie.NewEmpty(dba)
dbb := trie.NewDatabase(rawdb.NewMemoryDatabase())
trieb := trie.NewEmpty(dbb)
trieb.MustUpdate([]byte("foo"), []byte("bar"))
di, count := utils.NewSymmetricDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
for di.Next(true) {
if di.Leaf() {
assert.Equal(t, []byte("foo"), di.LeafKey())
assert.Equal(t, []byte("bar"), di.LeafBlob())
assert.False(t, di.CommonPath())
}
}
assert.Equal(t, 2, *count)
}) })
dba := trie.NewDatabase(rawdb.NewMemoryDatabase()) dba := trie.NewDatabase(rawdb.NewMemoryDatabase())
@ -66,6 +85,7 @@ func TestSymmetricDifferenceIterator(t *testing.T) {
onlyA := make(map[string]string) onlyA := make(map[string]string)
onlyB := make(map[string]string) onlyB := make(map[string]string)
var deletions, creations []string
it, _ := utils.NewSymmetricDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil)) it, _ := utils.NewSymmetricDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
for it.Next(true) { for it.Next(true) {
if !it.Leaf() { if !it.Leaf() {
@ -74,8 +94,14 @@ func TestSymmetricDifferenceIterator(t *testing.T) {
key, value := string(it.LeafKey()), string(it.LeafBlob()) key, value := string(it.LeafKey()), string(it.LeafBlob())
if it.FromA() { if it.FromA() {
onlyA[key] = value onlyA[key] = value
if !it.CommonPath() {
deletions = append(deletions, key)
}
} else { } else {
onlyB[key] = value onlyB[key] = value
if !it.CommonPath() {
creations = append(creations, key)
}
} }
} }
@ -92,6 +118,17 @@ func TestSymmetricDifferenceIterator(t *testing.T) {
"foos": "aa", "foos": "aa",
"jars": "d", "jars": "d",
} }
expectedDeletions := []string{
"bard",
"food",
}
expectedCreations := []string{
"aardvark",
"foos",
"jars",
}
assert.Equal(t, expectedOnlyA, onlyA) assert.Equal(t, expectedOnlyA, onlyA)
assert.Equal(t, expectedOnlyB, onlyB) assert.Equal(t, expectedOnlyB, onlyB)
assert.Equal(t, expectedDeletions, deletions)
assert.Equal(t, expectedCreations, creations)
} }