1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-11 18:20:28 +09:00

Change tree iterate

This commit is contained in:
mcrakhman 2022-09-04 14:34:37 +02:00 committed by Mikhail Iudin
parent cf63d29b42
commit c099e04434
No known key found for this signature in database
GPG key ID: FAAAA8BAABDFF1C0
3 changed files with 61 additions and 119 deletions

View file

@ -25,6 +25,7 @@ type Change struct {
IsSnapshot bool
DecryptedChange []byte // TODO: check if we need it
ParsedModel interface{}
visited bool
Content *aclpb.Change
Sign []byte

View file

@ -105,6 +105,7 @@ func TestTree_Add(t *testing.T) {
t.Log(time.Since(st))
assert.Equal(t, []string{"9999"}, tr.Heads())
})
// TODO: add my tests
}
func TestTree_Hash(t *testing.T) {
@ -157,9 +158,10 @@ func TestTree_Iterate(t *testing.T) {
newChange("1", "0", "0"),
newChange("1.1", "0", "1"),
newChange("1.2", "0", "1"),
newChange("1.4", "0", "1.2"),
newChange("1.3", "0", "1"),
newChange("1.3.1", "0", "1.3"),
newChange("1.2+3", "0", "1.2", "1.3.1"),
newChange("1.2+3", "0", "1.4", "1.3.1"),
newChange("1.2+3.1", "0", "1.2+3"),
newChange("10", "0", "1.2+3.1", "1.1"),
newChange("last", "0", "10"),
@ -174,7 +176,7 @@ func TestTree_Iterate(t *testing.T) {
res = append(res, c.Id)
return true
})
assert.Equal(t, []string{"0", "1", "1.1", "1.2", "1.3", "1.3.1", "1.2+3", "1.2+3.1", "10", "last"}, res)
assert.Equal(t, []string{"0", "1", "1.1", "1.2", "1.4", "1.3", "1.3.1", "1.2+3", "1.2+3.1", "10", "last"}, res)
})
}
@ -211,4 +213,17 @@ func BenchmarkTree_Add(b *testing.B) {
tr.AddFast(getChanges()...)
}
})
// prepare linear tree
tr := new(Tree)
tr.AddFast(newSnapshot("0", ""))
for j := 0; j < 10000; j++ {
tr.Add(newChange(fmt.Sprint(j+1), "0", fmt.Sprint(j)))
}
b.Run("add linear", func(b *testing.B) {
for i := 0; i < b.N; i++ {
tr.Iterate("0", func(c *Change) (isContinue bool) {
return true
})
}
})
}

View file

@ -1,10 +1,15 @@
package tree
import "sync"
import (
"sync"
)
var itPool = &sync.Pool{
New: func() interface{} {
return &iterator{}
return &iterator{
stack: make([]*Change, 0, 100),
resBuf: make([]*Change, 0, 100),
}
},
}
@ -17,11 +22,9 @@ func freeIterator(i *iterator) {
}
type iterator struct {
compBuf []*Change
queue []*Change
doneMap map[*Change]struct{}
breakpoint *Change
f func(c *Change) bool
resBuf []*Change
stack []*Change
f func(c *Change) bool
}
func (i *iterator) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) {
@ -35,124 +38,47 @@ func (i *iterator) iterateSkip(start *Change, skipBefore *Change, f func(c *Chan
})
}
func (i *iterator) topSort(start *Change) {
stack := i.stack
stack = append(stack, start)
for len(stack) > 0 {
ch := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if ch.visited {
i.resBuf = append(i.resBuf, ch)
continue
}
ch.visited = true
stack = append(stack, ch)
for j := 0; j < len(ch.Next); j++ {
if ch.Next[j].visited {
continue
}
stack = append(stack, ch.Next[j])
}
}
for _, ch := range i.resBuf {
ch.visited = false
}
}
func (i *iterator) iterate(start *Change, f func(c *Change) (isContinue bool)) {
if start == nil {
return
}
// reset
i.queue = i.queue[:0]
i.compBuf = i.compBuf[:0]
i.doneMap = make(map[*Change]struct{})
i.queue = append(i.queue, start)
i.breakpoint = nil
i.resBuf = i.resBuf[:0]
i.stack = i.stack[:0]
i.f = f
for len(i.queue) > 0 {
c := i.queue[0]
i.queue = i.queue[1:]
nl := len(c.Next)
if nl == 1 {
if !i.iterateLin(c) {
return
}
if i.breakpoint != nil {
i.toQueue(i.breakpoint)
i.breakpoint = nil
}
} else {
_, done := i.doneMap[c]
if !done {
if !f(c) {
return
}
i.doneMap[c] = struct{}{}
}
if nl != 0 {
for _, next := range c.Next {
i.toQueue(next)
}
}
}
}
}
func (i *iterator) iterateLin(c *Change) bool {
for len(c.Next) == 1 {
_, done := i.doneMap[c]
if !done {
if !i.f(c) {
return false
}
i.doneMap[c] = struct{}{}
}
c = c.Next[0]
if len(c.PreviousIds) > 1 {
break
}
}
if len(c.Next) == 0 && len(c.PreviousIds) <= 1 {
if !i.f(c) {
return false
}
i.doneMap[c] = struct{}{}
} else {
i.breakpoint = c
}
return true
}
func (i *iterator) comp(c1, c2 *Change) uint8 {
if c1.Id == c2.Id {
return 0
}
i.compBuf = i.compBuf[:0]
i.compBuf = append(i.compBuf, c1.Next...)
var uniq = make(map[*Change]struct{})
var appendUniqueToBuf = func(next []*Change) {
for _, n := range next {
if _, ok := uniq[n]; !ok {
i.compBuf = append(i.compBuf, n)
uniq[n] = struct{}{}
}
}
}
var used int
for len(i.compBuf)-used > 0 {
l := len(i.compBuf) - used
for _, n := range i.compBuf[used:] {
delete(uniq, n)
if n.Id == c2.Id {
return 1
} else {
appendUniqueToBuf(n.Next)
}
}
used += l
}
return 2
}
func (i *iterator) toQueue(c *Change) {
var pos = -1
For:
for idx, qc := range i.queue {
switch i.comp(c, qc) {
// exists
case 0:
i.topSort(start)
for idx := len(i.resBuf) - 1; idx >= 0; idx-- {
if !f(i.resBuf[idx]) {
return
//
case 1:
pos = idx
break For
}
}
if pos == -1 {
i.queue = append(i.queue, c)
} else if pos == 0 {
i.queue = append([]*Change{c}, i.queue...)
} else {
i.queue = append(i.queue[:pos], append([]*Change{c}, i.queue[pos:]...)...)
}
}