1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00
any-sync/commonspace/object/tree/objecttree/treereduce.go
2025-01-04 12:02:04 +01:00

109 lines
2.6 KiB
Go

package objecttree
import (
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
// clearPossibleRoots force removes any snapshots which can further be deemed as roots
func (t *Tree) clearPossibleRoots() {
t.possibleRoots = t.possibleRoots[:0]
}
// makeRootAndRemove removes all changes before start and makes start the root
func (t *Tree) makeRootAndRemove(start *Change) {
if start.Id == t.root.Id {
return
}
t.stackBuf = t.stackBuf[:0]
stack := t.stackBuf
for _, prev := range start.PreviousIds {
stack = append(stack, t.attached[prev])
}
t.dfsPrev(
stack,
[]string{},
func(ch *Change) bool {
return true
},
func(changes []*Change) {
for _, ch := range changes {
delete(t.attached, ch.Id)
}
},
)
// removing unattached because they may refer to previous root
t.unAttached = make(map[string]*Change)
t.root = start
}
// reduceTree tries to reduce the tree to one of possible tree roots
func (t *Tree) reduceTree() (res bool) {
if len(t.possibleRoots) == 0 {
return
}
firstHead := t.attached[t.headIds[0]]
if firstHead.IsSnapshot && len(t.headIds) == 1 {
t.clearPossibleRoots()
t.makeRootAndRemove(firstHead)
return true
}
cur, ok := t.attached[firstHead.SnapshotId]
if !ok {
log.Error("snapshot not found in tree", zap.String("snapshotId", t.attached[t.headIds[0]].SnapshotId))
return false
}
if len(t.headIds) == 1 {
t.clearPossibleRoots()
t.makeRootAndRemove(cur)
return true
}
// gathering snapshots from first head to root
var path []*Change
for cur.Id != t.root.Id {
cur.visited = true
path = append(path, cur)
cur, ok = t.attached[cur.SnapshotId]
if !ok {
log.Error("snapshot not found in tree", zap.String("snapshotId", cur.SnapshotId))
return false
}
}
path = append(path, t.root)
t.root.visited = true
// checking where paths from other heads intersect path
maxIdx := 0
for i := 1; i < len(t.headIds); i++ {
headSnapshot := t.attached[t.headIds[i]].SnapshotId
cur, ok := t.attached[headSnapshot]
if !ok {
log.Error("snapshot not found in tree", zap.String("snapshotId", t.attached[t.headIds[i]].SnapshotId))
return false
}
for {
if cur.visited {
// TODO: we may use counters here but it is not necessary
idx := slices.IndexFunc(path, func(c *Change) bool {
return c.Id == cur.Id
})
if idx > maxIdx {
maxIdx = idx
}
break
}
cur, ok = t.attached[cur.SnapshotId]
if !ok {
log.Error("snapshot not found in tree", zap.String("snapshotId", cur.SnapshotId))
return false
}
}
}
for _, c := range path {
c.visited = false
}
t.clearPossibleRoots()
t.makeRootAndRemove(path[maxIdx])
return true
}