diff --git a/api/converter/converter_test.go b/api/converter/converter_test.go
index 682a70cd6..ab23bb7c1 100644
--- a/api/converter/converter_test.go
+++ b/api/converter/converter_test.go
@@ -268,4 +268,29 @@ func TestConverter(t *testing.T) {
clone := converter.FromPresenceChange(pbChange)
assert.Equal(t, change, clone)
})
+
+ t.Run("properly encode and decode tree test", func(t *testing.T) {
+ doc := document.New(helper.TestDocKey(t))
+ assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error {
+ root.SetNewTree("t", &json.TreeNode{
+ Type: "r",
+ Children: []json.TreeNode{
+ {Type: "p", Children: []json.TreeNode{{Type: "text", Value: "12"}}},
+ {Type: "p", Children: []json.TreeNode{{Type: "text", Value: "34"}}},
+ },
+ })
+ assert.Equal(t, "12
34
", root.GetTree("t").ToXML())
+ root.GetTree("t").EditByPath([]int{0, 1}, []int{1, 1}, nil, 0)
+ return nil
+ }))
+ assert.Equal(t, "14
", doc.Root().GetTree("t").ToXML())
+
+ bytes, err := converter.ObjectToBytes(doc.RootObject())
+ assert.NoError(t, err)
+ obj, err := converter.BytesToObject(bytes)
+ assert.NoError(t, err)
+
+ assert.Equal(t, obj.Get("t").(*crdt.Tree).NodeLen(), doc.Root().GetTree("t").NodeLen())
+ assert.Equal(t, obj.Get("t").(*crdt.Tree).Root().Len(), doc.Root().GetTree("t").Len())
+ })
}
diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go
index 6cc53b6fe..96ba7ca9c 100644
--- a/api/converter/from_pb.go
+++ b/api/converter/from_pb.go
@@ -596,6 +596,8 @@ func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) {
}
}
+ root.Index.UpdateDescendantsSize()
+
// build crdt.Tree from root to construct the links between nodes.
return crdt.NewTree(root, nil).Root(), nil
}
diff --git a/pkg/document/crdt/tree.go b/pkg/document/crdt/tree.go
index ed79d4eed..f4841dbb2 100644
--- a/pkg/document/crdt/tree.go
+++ b/pkg/document/crdt/tree.go
@@ -641,6 +641,11 @@ func (t *Tree) Root() *TreeNode {
return t.IndexTree.Root().Value
}
+// NodeLen returns the size of the LLRBTree.
+func (t *Tree) NodeLen() int {
+ return t.NodeMapByID.Len()
+}
+
// ToXML returns the XML encoding of this tree.
func (t *Tree) ToXML() string {
return ToXML(t.Root())
diff --git a/pkg/index/tree.go b/pkg/index/tree.go
index 9a820b5f5..8a96397b1 100644
--- a/pkg/index/tree.go
+++ b/pkg/index/tree.go
@@ -325,7 +325,8 @@ func (n *Node[V]) SetChildren(children []*Node[V]) error {
return nil
}
-// UpdateAncestorsSize updates the size of ancestors.
+// UpdateAncestorsSize updates the size of ancestors. It is used when
+// the size of the node is changed.
func (n *Node[V]) UpdateAncestorsSize() {
parent := n.Parent
sign := 1
@@ -340,6 +341,24 @@ func (n *Node[V]) UpdateAncestorsSize() {
}
}
+// UpdateDescendantsSize updates the size of descendants. It is used when
+// the tree is newly created and the size of the descendants is not calculated.
+func (n *Node[V]) UpdateDescendantsSize() int {
+ if n.Value.IsRemoved() {
+ n.Length = 0
+ return 0
+ }
+
+ sum := 0
+ for _, child := range n.Children(true) {
+ sum += child.UpdateDescendantsSize()
+ }
+
+ n.Length += sum
+
+ return n.PaddedLength()
+}
+
// PaddedLength returns the length of the node with padding.
func (n *Node[V]) PaddedLength() int {
length := n.Length
@@ -496,7 +515,8 @@ func (n *Node[V]) insertAtInternal(newNode *Node[V], offset int) error {
return nil
}
-// Prepend prepends the given nodes to the children.
+// Prepend prepends the given nodes to the children. It is only used
+// for creating a new node from shapshot.
func (n *Node[V]) Prepend(children ...*Node[V]) error {
if n.IsText() {
return ErrInvalidMethodCallForTextNode
@@ -505,10 +525,6 @@ func (n *Node[V]) Prepend(children ...*Node[V]) error {
n.children = append(children, n.children...)
for _, node := range children {
node.Parent = n
-
- if !node.Value.IsRemoved() {
- node.UpdateAncestorsSize()
- }
}
return nil
diff --git a/pkg/llrb/llrb.go b/pkg/llrb/llrb.go
index 316effac0..8057a4849 100644
--- a/pkg/llrb/llrb.go
+++ b/pkg/llrb/llrb.go
@@ -136,6 +136,11 @@ func (t *Tree[K, V]) Floor(key K) (K, V) {
return zeroK, zeroV
}
+// Len returns the length of the tree.
+func (t *Tree[K, V]) Len() int {
+ return t.size
+}
+
func (t *Tree[K, V]) put(node *Node[K, V], key K, value V) *Node[K, V] {
if node == nil {
t.size++