diff --git a/yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtTree.kt b/yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtTree.kt index c80efff61..728083d00 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtTree.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtTree.kt @@ -720,7 +720,13 @@ internal data class CrdtTreeNode private constructor( get() = id.createdAt var removedAt: TimeTicket? = null - private set + private set(value) { + field = value + if (field != null) { + parent?.childRemoved() + return + } + } override val isRemoved: Boolean get() = removedAt != null @@ -835,16 +841,16 @@ internal data class CrdtTreeNode private constructor( fun deepCopy(): CrdtTreeNode { return copy( _value = _value, - childNodes = childrenInternal.map { child -> + childNodes = allChildren.map { child -> child.deepCopy() }.toMutableList(), _attributes = _attributes.deepCopy(), ).also { - it.size = size - it.removedAt = removedAt - it.childrenInternal.forEach { child -> + it.allChildren.forEach { child -> child.parent = it } + it.size = size + it.removedAt = removedAt it.insPrevID = insPrevID it.insNextID = insNextID } diff --git a/yorkie/src/main/kotlin/dev/yorkie/util/IndexTree.kt b/yorkie/src/main/kotlin/dev/yorkie/util/IndexTree.kt index 2249fe590..77fdaed8b 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/util/IndexTree.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/util/IndexTree.kt @@ -1,6 +1,5 @@ package dev.yorkie.util -import androidx.annotation.VisibleForTesting import dev.yorkie.document.time.TimeTicket /** @@ -215,12 +214,14 @@ internal class IndexTree>(val root: T) { node = parent mutableListOf(sizeOfLeftSiblings + treePos.offset) } + node.hasTextChild -> { // TODO(hackerwins): The function does not consider the situation // where Element and Text nodes are mixed in the Element's Children. val sizeOfLeftSiblings = addSizeOfLeftSiblings(node, treePos.offset) mutableListOf(sizeOfLeftSiblings) } + else -> { mutableListOf(treePos.offset) } @@ -372,7 +373,7 @@ internal data class TreePos>( internal abstract class IndexTreeNode>(children: MutableList) { abstract val type: String - protected val childrenInternal: MutableList = children + private val childrenInternal: MutableList = children val isText get() = type == DEFAULT_TEXT_TYPE @@ -397,6 +398,9 @@ internal abstract class IndexTreeNode>(children: MutableLis return parent?.children?.getOrNull(offset + 1) } + private var canUseCachedChildren = false + private var cachedChildren: List = emptyList() + /** * Returns the children of the node. * Tombstone nodes remain awhile in the tree during editing. @@ -404,12 +408,21 @@ internal abstract class IndexTreeNode>(children: MutableLis * So, we need to filter out the tombstone nodes to get the real children. */ val children: List - get() = childrenInternal.filterNot { it.isRemoved } + get() { + return if (canUseCachedChildren) { + cachedChildren + } else { + cachedChildren = childrenInternal.filterNotTo(ArrayList(childrenInternal.size)) { + it.isRemoved + } + canUseCachedChildren = true + cachedChildren + } + } /** * Returns the children of the node including tombstones. */ - @VisibleForTesting val allChildren: List get() = childrenInternal.toList() @@ -461,6 +474,7 @@ internal abstract class IndexTreeNode>(children: MutableLis } childrenInternal.addAll(newNode) + canUseCachedChildren = false newNode.forEach { node -> node.parent = this as T @@ -479,6 +493,7 @@ internal abstract class IndexTreeNode>(children: MutableLis } childrenInternal.addAll(0, newNode.toList()) + canUseCachedChildren = false newNode.forEach { node -> node.parent = this as T @@ -536,6 +551,7 @@ internal abstract class IndexTreeNode>(children: MutableLis } childrenInternal.add(offset, newNode) + canUseCachedChildren = false newNode.parent = this as T } @@ -551,6 +567,7 @@ internal abstract class IndexTreeNode>(children: MutableLis ?: throw NoSuchElementException("child not found") childrenInternal.removeAt(offset) + canUseCachedChildren = false child.parent = null } @@ -579,8 +596,10 @@ internal abstract class IndexTreeNode>(children: MutableLis val rightChildren = childrenInternal.drop(offset) childrenInternal.clear() childrenInternal.addAll(leftChildren) + canUseCachedChildren = false clone.childrenInternal.clear() clone.childrenInternal.addAll(rightChildren) + clone.canUseCachedChildren = false size = childrenInternal.fold(0) { acc, child -> acc + child.paddedSize } @@ -615,6 +634,10 @@ internal abstract class IndexTreeNode>(children: MutableLis */ abstract fun cloneElement(issueTimeTicket: () -> TimeTicket): T + fun childRemoved() { + canUseCachedChildren = false + } + companion object { /** * [ELEMENT_PADDING_SIZE] is the size of an element node as a child of another element node.