@yorkie-js/sdk 0.7.10 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/yorkie-js-sdk.d.ts +19 -0
- package/dist/yorkie-js-sdk.es.js +935 -465
- package/dist/yorkie-js-sdk.es.js.map +1 -1
- package/dist/yorkie-js-sdk.js +935 -465
- package/dist/yorkie-js-sdk.js.map +1 -1
- package/package.json +2 -2
package/dist/yorkie-js-sdk.js
CHANGED
|
@@ -6571,447 +6571,432 @@
|
|
|
6571
6571
|
this.executedAt = executedAt;
|
|
6572
6572
|
}
|
|
6573
6573
|
}
|
|
6574
|
-
class
|
|
6574
|
+
class TreeListNode {
|
|
6575
6575
|
value;
|
|
6576
6576
|
left;
|
|
6577
6577
|
right;
|
|
6578
6578
|
parent;
|
|
6579
6579
|
weight;
|
|
6580
|
+
count;
|
|
6581
|
+
red;
|
|
6580
6582
|
constructor(value) {
|
|
6581
6583
|
this.value = value;
|
|
6582
|
-
this.
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
* `getNodeString` returns a string of weight and value of this node.
|
|
6586
|
-
*/
|
|
6587
|
-
getNodeString() {
|
|
6588
|
-
return `${this.weight}${this.value}`;
|
|
6584
|
+
this.red = true;
|
|
6585
|
+
this.weight = this.size();
|
|
6586
|
+
this.count = 1;
|
|
6589
6587
|
}
|
|
6590
6588
|
/**
|
|
6591
|
-
* `getValue` returns value of this node.
|
|
6589
|
+
* `getValue` returns the value of this node.
|
|
6592
6590
|
*/
|
|
6593
6591
|
getValue() {
|
|
6594
6592
|
return this.value;
|
|
6595
6593
|
}
|
|
6596
6594
|
/**
|
|
6597
|
-
* `
|
|
6595
|
+
* `size` returns 1 if the node is live, 0 if removed (tombstone).
|
|
6598
6596
|
*/
|
|
6599
|
-
|
|
6600
|
-
return
|
|
6597
|
+
size() {
|
|
6598
|
+
return this.value.isRemoved() ? 0 : 1;
|
|
6601
6599
|
}
|
|
6602
6600
|
/**
|
|
6603
|
-
* `
|
|
6601
|
+
* `getLeft` returns the left child.
|
|
6604
6602
|
*/
|
|
6605
|
-
|
|
6606
|
-
return
|
|
6603
|
+
getLeft() {
|
|
6604
|
+
return this.left;
|
|
6607
6605
|
}
|
|
6608
6606
|
/**
|
|
6609
|
-
* `
|
|
6607
|
+
* `setLeft` sets the left child.
|
|
6610
6608
|
*/
|
|
6611
|
-
|
|
6612
|
-
|
|
6609
|
+
setLeft(node) {
|
|
6610
|
+
this.left = node;
|
|
6613
6611
|
}
|
|
6614
6612
|
/**
|
|
6615
|
-
* `
|
|
6613
|
+
* `getRight` returns the right child.
|
|
6616
6614
|
*/
|
|
6617
|
-
|
|
6618
|
-
return this.
|
|
6615
|
+
getRight() {
|
|
6616
|
+
return this.right;
|
|
6619
6617
|
}
|
|
6620
6618
|
/**
|
|
6621
|
-
* `
|
|
6619
|
+
* `setRight` sets the right child.
|
|
6622
6620
|
*/
|
|
6623
|
-
|
|
6624
|
-
|
|
6621
|
+
setRight(node) {
|
|
6622
|
+
this.right = node;
|
|
6625
6623
|
}
|
|
6626
6624
|
/**
|
|
6627
|
-
* `getParent` returns parent
|
|
6625
|
+
* `getParent` returns the parent.
|
|
6628
6626
|
*/
|
|
6629
6627
|
getParent() {
|
|
6630
6628
|
return this.parent;
|
|
6631
6629
|
}
|
|
6632
6630
|
/**
|
|
6633
|
-
* `
|
|
6631
|
+
* `setParent` sets the parent.
|
|
6634
6632
|
*/
|
|
6635
|
-
|
|
6636
|
-
|
|
6633
|
+
setParent(node) {
|
|
6634
|
+
this.parent = node;
|
|
6637
6635
|
}
|
|
6638
6636
|
/**
|
|
6639
|
-
* `
|
|
6637
|
+
* `getWeight` returns the cached live-node weight of this subtree.
|
|
6640
6638
|
*/
|
|
6641
|
-
|
|
6642
|
-
return
|
|
6639
|
+
getWeight() {
|
|
6640
|
+
return this.weight;
|
|
6643
6641
|
}
|
|
6644
6642
|
/**
|
|
6645
|
-
* `
|
|
6643
|
+
* `setWeight` sets the cached live-node weight of this subtree.
|
|
6646
6644
|
*/
|
|
6647
|
-
|
|
6648
|
-
|
|
6645
|
+
setWeight(w) {
|
|
6646
|
+
this.weight = w;
|
|
6649
6647
|
}
|
|
6650
6648
|
/**
|
|
6651
|
-
* `
|
|
6649
|
+
* `getCount` returns the cached total node count of this subtree.
|
|
6652
6650
|
*/
|
|
6653
|
-
|
|
6654
|
-
this.
|
|
6651
|
+
getCount() {
|
|
6652
|
+
return this.count;
|
|
6655
6653
|
}
|
|
6656
6654
|
/**
|
|
6657
|
-
* `
|
|
6655
|
+
* `setCount` sets the cached total node count of this subtree.
|
|
6658
6656
|
*/
|
|
6659
|
-
|
|
6660
|
-
this.
|
|
6657
|
+
setCount(c) {
|
|
6658
|
+
this.count = c;
|
|
6661
6659
|
}
|
|
6662
6660
|
/**
|
|
6663
|
-
* `
|
|
6661
|
+
* `isRed` reports whether this node is colored red.
|
|
6664
6662
|
*/
|
|
6665
|
-
|
|
6666
|
-
this.
|
|
6663
|
+
isRed() {
|
|
6664
|
+
return this.red;
|
|
6667
6665
|
}
|
|
6668
6666
|
/**
|
|
6669
|
-
* `
|
|
6667
|
+
* `setRed` sets the color of this node.
|
|
6670
6668
|
*/
|
|
6671
|
-
|
|
6672
|
-
this.
|
|
6673
|
-
this.right = void 0;
|
|
6674
|
-
this.left = void 0;
|
|
6669
|
+
setRed(red) {
|
|
6670
|
+
this.red = red;
|
|
6675
6671
|
}
|
|
6676
6672
|
/**
|
|
6677
|
-
* `
|
|
6673
|
+
* `leftWeight` returns the live-node weight of the left subtree, or 0 if absent.
|
|
6678
6674
|
*/
|
|
6679
|
-
|
|
6680
|
-
return this.
|
|
6675
|
+
leftWeight() {
|
|
6676
|
+
return this.left ? this.left.weight : 0;
|
|
6681
6677
|
}
|
|
6682
6678
|
/**
|
|
6683
|
-
* `
|
|
6679
|
+
* `rightWeight` returns the live-node weight of the right subtree, or 0 if absent.
|
|
6684
6680
|
*/
|
|
6685
|
-
|
|
6686
|
-
this.weight
|
|
6681
|
+
rightWeight() {
|
|
6682
|
+
return this.right ? this.right.weight : 0;
|
|
6687
6683
|
}
|
|
6688
6684
|
/**
|
|
6689
|
-
* `
|
|
6685
|
+
* `leftCount` returns the total node count of the left subtree, or 0 if absent.
|
|
6690
6686
|
*/
|
|
6691
|
-
|
|
6692
|
-
this.
|
|
6687
|
+
leftCount() {
|
|
6688
|
+
return this.left ? this.left.count : 0;
|
|
6689
|
+
}
|
|
6690
|
+
/**
|
|
6691
|
+
* `rightCount` returns the total node count of the right subtree, or 0 if absent.
|
|
6692
|
+
*/
|
|
6693
|
+
rightCount() {
|
|
6694
|
+
return this.right ? this.right.count : 0;
|
|
6693
6695
|
}
|
|
6694
6696
|
}
|
|
6695
|
-
|
|
6697
|
+
function isRed(node) {
|
|
6698
|
+
return !!node && node.isRed();
|
|
6699
|
+
}
|
|
6700
|
+
function updateNode(node) {
|
|
6701
|
+
node.setWeight(node.leftWeight() + node.size() + node.rightWeight());
|
|
6702
|
+
node.setCount(node.leftCount() + 1 + node.rightCount());
|
|
6703
|
+
}
|
|
6704
|
+
function rotateLeft(node) {
|
|
6705
|
+
const right = node.getRight();
|
|
6706
|
+
node.setRight(right.getLeft());
|
|
6707
|
+
if (node.getRight()) {
|
|
6708
|
+
node.getRight().setParent(node);
|
|
6709
|
+
}
|
|
6710
|
+
right.setLeft(node);
|
|
6711
|
+
right.setParent(node.getParent());
|
|
6712
|
+
node.setParent(right);
|
|
6713
|
+
right.setRed(node.isRed());
|
|
6714
|
+
node.setRed(true);
|
|
6715
|
+
updateNode(node);
|
|
6716
|
+
updateNode(right);
|
|
6717
|
+
return right;
|
|
6718
|
+
}
|
|
6719
|
+
function rotateRight(node) {
|
|
6720
|
+
const left = node.getLeft();
|
|
6721
|
+
node.setLeft(left.getRight());
|
|
6722
|
+
if (node.getLeft()) {
|
|
6723
|
+
node.getLeft().setParent(node);
|
|
6724
|
+
}
|
|
6725
|
+
left.setRight(node);
|
|
6726
|
+
left.setParent(node.getParent());
|
|
6727
|
+
node.setParent(left);
|
|
6728
|
+
left.setRed(node.isRed());
|
|
6729
|
+
node.setRed(true);
|
|
6730
|
+
updateNode(node);
|
|
6731
|
+
updateNode(left);
|
|
6732
|
+
return left;
|
|
6733
|
+
}
|
|
6734
|
+
function flipColors(node) {
|
|
6735
|
+
node.setRed(!node.isRed());
|
|
6736
|
+
node.getLeft().setRed(!node.getLeft().isRed());
|
|
6737
|
+
node.getRight().setRed(!node.getRight().isRed());
|
|
6738
|
+
}
|
|
6739
|
+
function moveRedLeft(node) {
|
|
6740
|
+
flipColors(node);
|
|
6741
|
+
if (isRed(node.getRight().getLeft())) {
|
|
6742
|
+
node.setRight(rotateRight(node.getRight()));
|
|
6743
|
+
node.getRight().setParent(node);
|
|
6744
|
+
node = rotateLeft(node);
|
|
6745
|
+
flipColors(node);
|
|
6746
|
+
}
|
|
6747
|
+
return node;
|
|
6748
|
+
}
|
|
6749
|
+
function moveRedRight(node) {
|
|
6750
|
+
flipColors(node);
|
|
6751
|
+
if (isRed(node.getLeft().getLeft())) {
|
|
6752
|
+
node = rotateRight(node);
|
|
6753
|
+
flipColors(node);
|
|
6754
|
+
}
|
|
6755
|
+
return node;
|
|
6756
|
+
}
|
|
6757
|
+
function removeMin(node) {
|
|
6758
|
+
if (!node.getLeft()) {
|
|
6759
|
+
return void 0;
|
|
6760
|
+
}
|
|
6761
|
+
if (!isRed(node.getLeft()) && !isRed(node.getLeft().getLeft())) {
|
|
6762
|
+
node = moveRedLeft(node);
|
|
6763
|
+
}
|
|
6764
|
+
node.setLeft(removeMin(node.getLeft()));
|
|
6765
|
+
if (node.getLeft()) {
|
|
6766
|
+
node.getLeft().setParent(node);
|
|
6767
|
+
}
|
|
6768
|
+
return fixUp(node);
|
|
6769
|
+
}
|
|
6770
|
+
function minNode(node) {
|
|
6771
|
+
while (node.getLeft()) {
|
|
6772
|
+
node = node.getLeft();
|
|
6773
|
+
}
|
|
6774
|
+
return node;
|
|
6775
|
+
}
|
|
6776
|
+
function fixUp(node) {
|
|
6777
|
+
if (isRed(node.getRight()) && !isRed(node.getLeft())) {
|
|
6778
|
+
node = rotateLeft(node);
|
|
6779
|
+
}
|
|
6780
|
+
if (isRed(node.getLeft()) && isRed(node.getLeft().getLeft())) {
|
|
6781
|
+
node = rotateRight(node);
|
|
6782
|
+
}
|
|
6783
|
+
if (isRed(node.getLeft()) && isRed(node.getRight())) {
|
|
6784
|
+
flipColors(node);
|
|
6785
|
+
}
|
|
6786
|
+
updateNode(node);
|
|
6787
|
+
return node;
|
|
6788
|
+
}
|
|
6789
|
+
function traverseInOrder(node, cb) {
|
|
6790
|
+
if (!node) {
|
|
6791
|
+
return;
|
|
6792
|
+
}
|
|
6793
|
+
traverseInOrder(node.getLeft(), cb);
|
|
6794
|
+
cb(node);
|
|
6795
|
+
traverseInOrder(node.getRight(), cb);
|
|
6796
|
+
}
|
|
6797
|
+
class TreeList {
|
|
6696
6798
|
root;
|
|
6697
6799
|
constructor(root) {
|
|
6800
|
+
if (root) {
|
|
6801
|
+
root.setRed(false);
|
|
6802
|
+
}
|
|
6698
6803
|
this.root = root;
|
|
6699
6804
|
}
|
|
6700
6805
|
/**
|
|
6701
|
-
* `length` returns the
|
|
6806
|
+
* `length` returns the number of non-removed (live) nodes.
|
|
6702
6807
|
*/
|
|
6703
6808
|
get length() {
|
|
6704
6809
|
return this.root ? this.root.getWeight() : 0;
|
|
6705
6810
|
}
|
|
6706
6811
|
/**
|
|
6707
|
-
* `
|
|
6708
|
-
*
|
|
6812
|
+
* `insertAfter` inserts the target node right after prev in the in-order
|
|
6813
|
+
* traversal. It uses structural (count-based) indexing to correctly handle
|
|
6814
|
+
* tombstone nodes.
|
|
6709
6815
|
*/
|
|
6710
|
-
|
|
6711
|
-
if (!
|
|
6712
|
-
return
|
|
6816
|
+
insertAfter(prev, target) {
|
|
6817
|
+
if (!prev || !target) {
|
|
6818
|
+
return;
|
|
6713
6819
|
}
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6820
|
+
target.setLeft(void 0);
|
|
6821
|
+
target.setRight(void 0);
|
|
6822
|
+
target.setParent(void 0);
|
|
6823
|
+
target.setRed(true);
|
|
6824
|
+
target.setWeight(target.size());
|
|
6825
|
+
target.setCount(1);
|
|
6826
|
+
const idx = this.structuralIndexOf(prev);
|
|
6827
|
+
this.root = this.insertByCount(this.root, idx + 1, target);
|
|
6828
|
+
this.root.setRed(false);
|
|
6829
|
+
this.root.setParent(void 0);
|
|
6830
|
+
}
|
|
6831
|
+
/**
|
|
6832
|
+
* `insertByCount` inserts newNode at the given structural index within the
|
|
6833
|
+
* subtree rooted at node, descending the tree using each node's left count
|
|
6834
|
+
* (tombstones included) and rebalancing on the way back up.
|
|
6835
|
+
*/
|
|
6836
|
+
insertByCount(node, index, newNode) {
|
|
6837
|
+
if (!node) {
|
|
6838
|
+
return newNode;
|
|
6725
6839
|
}
|
|
6726
|
-
if (
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6840
|
+
if (index <= node.leftCount()) {
|
|
6841
|
+
node.setLeft(this.insertByCount(node.getLeft(), index, newNode));
|
|
6842
|
+
node.getLeft().setParent(node);
|
|
6843
|
+
} else {
|
|
6844
|
+
node.setRight(
|
|
6845
|
+
this.insertByCount(
|
|
6846
|
+
node.getRight(),
|
|
6847
|
+
index - node.leftCount() - 1,
|
|
6848
|
+
newNode
|
|
6849
|
+
)
|
|
6730
6850
|
);
|
|
6851
|
+
node.getRight().setParent(node);
|
|
6731
6852
|
}
|
|
6732
|
-
|
|
6733
|
-
return [node, pos];
|
|
6853
|
+
return fixUp(node);
|
|
6734
6854
|
}
|
|
6735
6855
|
/**
|
|
6736
|
-
* `
|
|
6737
|
-
*
|
|
6856
|
+
* `find` returns the node at the given logical index (among non-removed
|
|
6857
|
+
* nodes). Throws when the index is out of range.
|
|
6738
6858
|
*/
|
|
6739
|
-
|
|
6740
|
-
if (!this.root) {
|
|
6741
|
-
return void 0;
|
|
6742
|
-
}
|
|
6743
|
-
if (idx < 0 || idx >= this.length) {
|
|
6859
|
+
find(index) {
|
|
6860
|
+
if (!this.root || index < 0 || index >= this.length) {
|
|
6744
6861
|
throw new YorkieError(
|
|
6745
6862
|
Code.ErrInvalidArgument,
|
|
6746
|
-
`out of index
|
|
6863
|
+
`out of index: tree size ${this.length}, index ${index}`
|
|
6747
6864
|
);
|
|
6748
6865
|
}
|
|
6749
6866
|
let node = this.root;
|
|
6750
6867
|
for (; ; ) {
|
|
6751
|
-
if (
|
|
6868
|
+
if (index < node.leftWeight()) {
|
|
6752
6869
|
node = node.getLeft();
|
|
6753
|
-
} else if (
|
|
6754
|
-
idx -= node.getLeftWeight() + node.getLength();
|
|
6755
|
-
node = node.getRight();
|
|
6756
|
-
} else {
|
|
6870
|
+
} else if (index < node.leftWeight() + node.size()) {
|
|
6757
6871
|
break;
|
|
6872
|
+
} else {
|
|
6873
|
+
index -= node.leftWeight() + node.size();
|
|
6874
|
+
node = node.getRight();
|
|
6758
6875
|
}
|
|
6759
6876
|
}
|
|
6760
|
-
this.splayNode(node);
|
|
6761
6877
|
return node;
|
|
6762
6878
|
}
|
|
6763
6879
|
/**
|
|
6764
|
-
*
|
|
6765
|
-
*
|
|
6766
|
-
*
|
|
6767
|
-
*
|
|
6880
|
+
* `delete` physically removes a node from the tree. Unlike tombstoning,
|
|
6881
|
+
* this completely removes the node from the tree structure. It uses
|
|
6882
|
+
* structural (count-based) indexing and swaps the node structure (not
|
|
6883
|
+
* values) with its successor to preserve node identity.
|
|
6768
6884
|
*/
|
|
6769
|
-
|
|
6770
|
-
if (!node ||
|
|
6771
|
-
return
|
|
6885
|
+
delete(node) {
|
|
6886
|
+
if (!node || !this.root) {
|
|
6887
|
+
return;
|
|
6888
|
+
}
|
|
6889
|
+
if (!isRed(this.root.getLeft()) && !isRed(this.root.getRight())) {
|
|
6890
|
+
this.root.setRed(true);
|
|
6891
|
+
}
|
|
6892
|
+
const idx = this.structuralIndexOf(node);
|
|
6893
|
+
this.root = this.deleteByCount(this.root, idx);
|
|
6894
|
+
if (this.root) {
|
|
6895
|
+
this.root.setRed(false);
|
|
6896
|
+
this.root.setParent(void 0);
|
|
6772
6897
|
}
|
|
6773
|
-
this.splayNode(node);
|
|
6774
|
-
return this.root.getLeftWeight();
|
|
6775
|
-
}
|
|
6776
|
-
/**
|
|
6777
|
-
* `getRoot` returns root of this tree.
|
|
6778
|
-
*/
|
|
6779
|
-
getRoot() {
|
|
6780
|
-
return this.root;
|
|
6781
|
-
}
|
|
6782
|
-
/**
|
|
6783
|
-
* `insert` inserts the node at the last.
|
|
6784
|
-
*/
|
|
6785
|
-
insert(newNode) {
|
|
6786
|
-
return this.insertAfter(this.root, newNode);
|
|
6787
6898
|
}
|
|
6788
6899
|
/**
|
|
6789
|
-
* `
|
|
6900
|
+
* `deleteByCount` removes the node at the given structural index within the
|
|
6901
|
+
* subtree rooted at node. When deleting an internal node, it swaps in the
|
|
6902
|
+
* in-order successor by re-parenting rather than copying values so external
|
|
6903
|
+
* references to the surviving node remain valid.
|
|
6790
6904
|
*/
|
|
6791
|
-
|
|
6792
|
-
if (
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6905
|
+
deleteByCount(node, index) {
|
|
6906
|
+
if (index < node.leftCount()) {
|
|
6907
|
+
if (!isRed(node.getLeft()) && !isRed(node.getLeft().getLeft())) {
|
|
6908
|
+
node = moveRedLeft(node);
|
|
6909
|
+
}
|
|
6910
|
+
node.setLeft(this.deleteByCount(node.getLeft(), index));
|
|
6911
|
+
if (node.getLeft()) {
|
|
6912
|
+
node.getLeft().setParent(node);
|
|
6913
|
+
}
|
|
6914
|
+
} else {
|
|
6915
|
+
if (isRed(node.getLeft())) {
|
|
6916
|
+
node = rotateRight(node);
|
|
6917
|
+
}
|
|
6918
|
+
if (index === node.leftCount() && !node.getRight()) {
|
|
6919
|
+
return void 0;
|
|
6920
|
+
}
|
|
6921
|
+
if (!isRed(node.getRight()) && !isRed(node.getRight().getLeft())) {
|
|
6922
|
+
node = moveRedRight(node);
|
|
6923
|
+
}
|
|
6924
|
+
if (index === node.leftCount()) {
|
|
6925
|
+
const successor = minNode(node.getRight());
|
|
6926
|
+
const newRight = removeMin(node.getRight());
|
|
6927
|
+
successor.setLeft(node.getLeft());
|
|
6928
|
+
successor.setRight(newRight);
|
|
6929
|
+
successor.setRed(node.isRed());
|
|
6930
|
+
if (successor.getLeft()) {
|
|
6931
|
+
successor.getLeft().setParent(successor);
|
|
6932
|
+
}
|
|
6933
|
+
if (successor.getRight()) {
|
|
6934
|
+
successor.getRight().setParent(successor);
|
|
6935
|
+
}
|
|
6936
|
+
node.setLeft(void 0);
|
|
6937
|
+
node.setRight(void 0);
|
|
6938
|
+
node.setParent(void 0);
|
|
6939
|
+
node = successor;
|
|
6940
|
+
} else {
|
|
6941
|
+
node.setRight(
|
|
6942
|
+
this.deleteByCount(node.getRight(), index - node.leftCount() - 1)
|
|
6943
|
+
);
|
|
6944
|
+
if (node.getRight()) {
|
|
6945
|
+
node.getRight().setParent(node);
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6801
6948
|
}
|
|
6802
|
-
|
|
6803
|
-
target.setParent(newNode);
|
|
6804
|
-
target.setRight();
|
|
6805
|
-
this.updateWeight(target);
|
|
6806
|
-
this.updateWeight(newNode);
|
|
6807
|
-
return newNode;
|
|
6949
|
+
return fixUp(node);
|
|
6808
6950
|
}
|
|
6809
6951
|
/**
|
|
6810
|
-
* `updateWeight`
|
|
6952
|
+
* `updateWeight` propagates weight changes from the given node up to the
|
|
6953
|
+
* root. Call this after a node's isRemoved() status changes (i.e., after
|
|
6954
|
+
* tombstoning).
|
|
6811
6955
|
*/
|
|
6812
6956
|
updateWeight(node) {
|
|
6813
|
-
node.
|
|
6814
|
-
|
|
6815
|
-
node.increaseWeight(node.getLeftWeight());
|
|
6816
|
-
}
|
|
6817
|
-
if (node.hasRight()) {
|
|
6818
|
-
node.increaseWeight(node.getRightWeight());
|
|
6957
|
+
for (let cur = node; cur !== void 0; cur = cur.getParent()) {
|
|
6958
|
+
cur.setWeight(cur.leftWeight() + cur.size() + cur.rightWeight());
|
|
6819
6959
|
}
|
|
6820
6960
|
}
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6961
|
+
/**
|
|
6962
|
+
* `toTestString` returns a string containing the metadata of the node for
|
|
6963
|
+
* debugging purpose.
|
|
6964
|
+
*/
|
|
6965
|
+
toTestString() {
|
|
6966
|
+
let s = "";
|
|
6967
|
+
traverseInOrder(this.root, (node) => {
|
|
6968
|
+
s += `[${node.getWeight()},${node.size()}]${node.getValue().toString()}`;
|
|
6969
|
+
});
|
|
6970
|
+
return s;
|
|
6826
6971
|
}
|
|
6827
6972
|
/**
|
|
6828
|
-
* `
|
|
6973
|
+
* `indexOf` returns the logical (live-node) index of the given node, or -1
|
|
6974
|
+
* if the node is a tombstone.
|
|
6829
6975
|
*/
|
|
6830
|
-
|
|
6831
|
-
if (
|
|
6832
|
-
return;
|
|
6976
|
+
indexOf(node) {
|
|
6977
|
+
if (node.size() === 0) {
|
|
6978
|
+
return -1;
|
|
6833
6979
|
}
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
} else if (this.isRightChild(node.getParent()) && this.isLeftChild(node)) {
|
|
6839
|
-
this.rotateRight(node);
|
|
6840
|
-
this.rotateLeft(node);
|
|
6841
|
-
} else if (this.isLeftChild(node.getParent()) && this.isLeftChild(node)) {
|
|
6842
|
-
this.rotateRight(node.getParent());
|
|
6843
|
-
this.rotateRight(node);
|
|
6844
|
-
} else if (this.isRightChild(node.getParent()) && this.isRightChild(node)) {
|
|
6845
|
-
this.rotateLeft(node.getParent());
|
|
6846
|
-
this.rotateLeft(node);
|
|
6847
|
-
} else {
|
|
6848
|
-
if (this.isLeftChild(node)) {
|
|
6849
|
-
this.rotateRight(node);
|
|
6850
|
-
} else if (this.isRightChild(node)) {
|
|
6851
|
-
this.rotateLeft(node);
|
|
6852
|
-
}
|
|
6853
|
-
this.updateWeight(node);
|
|
6854
|
-
return;
|
|
6980
|
+
let index = node.leftWeight();
|
|
6981
|
+
for (let cur = node; cur.getParent() !== void 0; cur = cur.getParent()) {
|
|
6982
|
+
if (cur === cur.getParent().getRight()) {
|
|
6983
|
+
index += cur.getParent().leftWeight() + cur.getParent().size();
|
|
6855
6984
|
}
|
|
6856
6985
|
}
|
|
6986
|
+
return index;
|
|
6857
6987
|
}
|
|
6858
6988
|
/**
|
|
6859
|
-
* `
|
|
6989
|
+
* `structuralIndexOf` returns the structural position of the node, counting
|
|
6990
|
+
* all nodes including tombstones.
|
|
6860
6991
|
*/
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
const rightTree = new SplayTree(node.getRight());
|
|
6868
|
-
if (rightTree.root) {
|
|
6869
|
-
rightTree.root.setParent();
|
|
6992
|
+
structuralIndexOf(node) {
|
|
6993
|
+
let index = node.leftCount();
|
|
6994
|
+
for (let cur = node; cur.getParent() !== void 0; cur = cur.getParent()) {
|
|
6995
|
+
if (cur === cur.getParent().getRight()) {
|
|
6996
|
+
index += cur.getParent().leftCount() + 1;
|
|
6997
|
+
}
|
|
6870
6998
|
}
|
|
6871
|
-
|
|
6872
|
-
const rightmostNode = leftTree.getRightmost();
|
|
6873
|
-
leftTree.splayNode(rightmostNode);
|
|
6874
|
-
leftTree.root.setRight(rightTree.root);
|
|
6875
|
-
if (rightTree.root) {
|
|
6876
|
-
rightTree.root.setParent(leftTree.root);
|
|
6877
|
-
}
|
|
6878
|
-
this.root = leftTree.root;
|
|
6879
|
-
} else {
|
|
6880
|
-
this.root = rightTree.root;
|
|
6881
|
-
}
|
|
6882
|
-
node.unlink();
|
|
6883
|
-
if (this.root) {
|
|
6884
|
-
this.updateWeight(this.root);
|
|
6885
|
-
}
|
|
6886
|
-
}
|
|
6887
|
-
/**
|
|
6888
|
-
* `deleteRange` separates the range between given 2 boundaries from this Tree.
|
|
6889
|
-
* This function separates the range to delete as a subtree
|
|
6890
|
-
* by splaying outer boundary nodes.
|
|
6891
|
-
* leftBoundary must exist because of 0-indexed initial dummy node of tree,
|
|
6892
|
-
* but rightBoundary can be nil means range to delete includes the end of tree.
|
|
6893
|
-
* Refer to the design document in https://github.com/yorkie-team/yorkie/tree/main/design
|
|
6894
|
-
*/
|
|
6895
|
-
deleteRange(leftBoundary, rightBoundary) {
|
|
6896
|
-
if (!rightBoundary) {
|
|
6897
|
-
this.splayNode(leftBoundary);
|
|
6898
|
-
this.cutOffRight(leftBoundary);
|
|
6899
|
-
return;
|
|
6900
|
-
}
|
|
6901
|
-
this.splayNode(leftBoundary);
|
|
6902
|
-
this.splayNode(rightBoundary);
|
|
6903
|
-
if (rightBoundary.getLeft() != leftBoundary) {
|
|
6904
|
-
this.rotateRight(leftBoundary);
|
|
6905
|
-
}
|
|
6906
|
-
this.cutOffRight(leftBoundary);
|
|
6907
|
-
}
|
|
6908
|
-
cutOffRight(root) {
|
|
6909
|
-
const nodesToFreeWeight = [];
|
|
6910
|
-
this.traversePostorder(root.getRight(), nodesToFreeWeight);
|
|
6911
|
-
for (const node of nodesToFreeWeight) {
|
|
6912
|
-
node.initWeight();
|
|
6913
|
-
}
|
|
6914
|
-
this.updateTreeWeight(root);
|
|
6915
|
-
}
|
|
6916
|
-
/**
|
|
6917
|
-
* `toTestString` returns a string containing the meta data of the Node
|
|
6918
|
-
* for debugging purpose.
|
|
6919
|
-
*/
|
|
6920
|
-
toTestString() {
|
|
6921
|
-
const metaString = [];
|
|
6922
|
-
this.traverseInorder(this.root, metaString);
|
|
6923
|
-
return metaString.map((n) => `[${n.getWeight()},${n.getLength()}]${n.getValue() || ""}`).join("");
|
|
6924
|
-
}
|
|
6925
|
-
/**
|
|
6926
|
-
* `checkWeight` returns false when there is an incorrect weight node.
|
|
6927
|
-
* for debugging purpose.
|
|
6928
|
-
*/
|
|
6929
|
-
checkWeight() {
|
|
6930
|
-
const nodes = [];
|
|
6931
|
-
this.traverseInorder(this.root, nodes);
|
|
6932
|
-
for (const node of nodes) {
|
|
6933
|
-
if (node.getWeight() != node.getLength() + node.getLeftWeight() + node.getRightWeight()) {
|
|
6934
|
-
return false;
|
|
6935
|
-
}
|
|
6936
|
-
}
|
|
6937
|
-
return true;
|
|
6938
|
-
}
|
|
6939
|
-
getRightmost() {
|
|
6940
|
-
let node = this.root;
|
|
6941
|
-
while (node.hasRight()) {
|
|
6942
|
-
node = node.getRight();
|
|
6943
|
-
}
|
|
6944
|
-
return node;
|
|
6945
|
-
}
|
|
6946
|
-
traverseInorder(node, stack) {
|
|
6947
|
-
if (!node) {
|
|
6948
|
-
return;
|
|
6949
|
-
}
|
|
6950
|
-
this.traverseInorder(node.getLeft(), stack);
|
|
6951
|
-
stack.push(node);
|
|
6952
|
-
this.traverseInorder(node.getRight(), stack);
|
|
6953
|
-
}
|
|
6954
|
-
traversePostorder(node, stack) {
|
|
6955
|
-
if (!node) {
|
|
6956
|
-
return;
|
|
6957
|
-
}
|
|
6958
|
-
this.traversePostorder(node.getLeft(), stack);
|
|
6959
|
-
this.traversePostorder(node.getRight(), stack);
|
|
6960
|
-
stack.push(node);
|
|
6961
|
-
}
|
|
6962
|
-
rotateLeft(pivot) {
|
|
6963
|
-
const root = pivot.getParent();
|
|
6964
|
-
if (root.hasParent()) {
|
|
6965
|
-
if (root === root.getParent().getLeft()) {
|
|
6966
|
-
root.getParent().setLeft(pivot);
|
|
6967
|
-
} else {
|
|
6968
|
-
root.getParent().setRight(pivot);
|
|
6969
|
-
}
|
|
6970
|
-
} else {
|
|
6971
|
-
this.root = pivot;
|
|
6972
|
-
}
|
|
6973
|
-
pivot.setParent(root.getParent());
|
|
6974
|
-
root.setRight(pivot.getLeft());
|
|
6975
|
-
if (root.hasRight()) {
|
|
6976
|
-
root.getRight().setParent(root);
|
|
6977
|
-
}
|
|
6978
|
-
pivot.setLeft(root);
|
|
6979
|
-
pivot.getLeft().setParent(pivot);
|
|
6980
|
-
this.updateWeight(root);
|
|
6981
|
-
this.updateWeight(pivot);
|
|
6982
|
-
}
|
|
6983
|
-
rotateRight(pivot) {
|
|
6984
|
-
const root = pivot.getParent();
|
|
6985
|
-
if (root.hasParent()) {
|
|
6986
|
-
if (root === root.getParent().getLeft()) {
|
|
6987
|
-
root.getParent().setLeft(pivot);
|
|
6988
|
-
} else {
|
|
6989
|
-
root.getParent().setRight(pivot);
|
|
6990
|
-
}
|
|
6991
|
-
} else {
|
|
6992
|
-
this.root = pivot;
|
|
6993
|
-
}
|
|
6994
|
-
pivot.setParent(root.getParent());
|
|
6995
|
-
root.setLeft(pivot.getRight());
|
|
6996
|
-
if (root.hasLeft()) {
|
|
6997
|
-
root.getLeft().setParent(root);
|
|
6998
|
-
}
|
|
6999
|
-
pivot.setRight(root);
|
|
7000
|
-
pivot.getRight().setParent(pivot);
|
|
7001
|
-
this.updateWeight(root);
|
|
7002
|
-
this.updateWeight(pivot);
|
|
7003
|
-
}
|
|
7004
|
-
isLeftChild(node) {
|
|
7005
|
-
if (node && node.hasParent()) {
|
|
7006
|
-
return node.getParent().getLeft() === node;
|
|
7007
|
-
}
|
|
7008
|
-
return false;
|
|
7009
|
-
}
|
|
7010
|
-
isRightChild(node) {
|
|
7011
|
-
if (node && node.hasParent()) {
|
|
7012
|
-
return node.getParent().getRight() === node;
|
|
7013
|
-
}
|
|
7014
|
-
return false;
|
|
6999
|
+
return index;
|
|
7015
7000
|
}
|
|
7016
7001
|
}
|
|
7017
7002
|
const removeDecimal = (number) => number < 0 ? Math.ceil(number) : Math.floor(number);
|
|
@@ -7301,14 +7286,14 @@
|
|
|
7301
7286
|
this.elem = elem;
|
|
7302
7287
|
}
|
|
7303
7288
|
}
|
|
7304
|
-
class RGATreeListNode
|
|
7289
|
+
class RGATreeListNode {
|
|
7290
|
+
indexNode;
|
|
7305
7291
|
_elementEntry;
|
|
7306
7292
|
_createdAt;
|
|
7307
7293
|
_removedAt;
|
|
7308
7294
|
prev;
|
|
7309
7295
|
next;
|
|
7310
|
-
constructor(
|
|
7311
|
-
super(elem);
|
|
7296
|
+
constructor(createdAt) {
|
|
7312
7297
|
this._createdAt = createdAt;
|
|
7313
7298
|
}
|
|
7314
7299
|
/**
|
|
@@ -7316,9 +7301,10 @@
|
|
|
7316
7301
|
*/
|
|
7317
7302
|
static createWithElement(elem) {
|
|
7318
7303
|
const entry = new ElementEntry(elem);
|
|
7319
|
-
const node = new RGATreeListNode(elem
|
|
7320
|
-
entry.positionNode = node;
|
|
7304
|
+
const node = new RGATreeListNode(elem.getCreatedAt());
|
|
7321
7305
|
node._elementEntry = entry;
|
|
7306
|
+
entry.positionNode = node;
|
|
7307
|
+
node.indexNode = new TreeListNode(node);
|
|
7322
7308
|
return node;
|
|
7323
7309
|
}
|
|
7324
7310
|
/**
|
|
@@ -7326,7 +7312,9 @@
|
|
|
7326
7312
|
* (used for move).
|
|
7327
7313
|
*/
|
|
7328
7314
|
static createBarePosition(createdAt) {
|
|
7329
|
-
|
|
7315
|
+
const node = new RGATreeListNode(createdAt);
|
|
7316
|
+
node.indexNode = new TreeListNode(node);
|
|
7317
|
+
return node;
|
|
7330
7318
|
}
|
|
7331
7319
|
/**
|
|
7332
7320
|
* `createAfter` creates a new node with the given element after
|
|
@@ -7398,14 +7386,14 @@
|
|
|
7398
7386
|
this.next = void 0;
|
|
7399
7387
|
}
|
|
7400
7388
|
/**
|
|
7401
|
-
* `
|
|
7402
|
-
*
|
|
7389
|
+
* `toString` returns a string representation of this node's value, used by
|
|
7390
|
+
* TreeList for debugging.
|
|
7403
7391
|
*/
|
|
7404
|
-
|
|
7405
|
-
if (!this._elementEntry
|
|
7406
|
-
return
|
|
7392
|
+
toString() {
|
|
7393
|
+
if (!this._elementEntry) {
|
|
7394
|
+
return "";
|
|
7407
7395
|
}
|
|
7408
|
-
return
|
|
7396
|
+
return this._elementEntry.elem.toJSON();
|
|
7409
7397
|
}
|
|
7410
7398
|
/**
|
|
7411
7399
|
* `getPrev` returns a previous node.
|
|
@@ -7423,9 +7411,6 @@
|
|
|
7423
7411
|
* `getValue` returns the element value.
|
|
7424
7412
|
*/
|
|
7425
7413
|
getValue() {
|
|
7426
|
-
if (!this._elementEntry) {
|
|
7427
|
-
return this.value;
|
|
7428
|
-
}
|
|
7429
7414
|
return this._elementEntry.elem;
|
|
7430
7415
|
}
|
|
7431
7416
|
/**
|
|
@@ -7514,10 +7499,11 @@
|
|
|
7514
7499
|
dummyValue.setRemovedAt(InitialTimeTicket);
|
|
7515
7500
|
this.dummyHead = RGATreeListNode.createWithElement(dummyValue);
|
|
7516
7501
|
this.last = this.dummyHead;
|
|
7517
|
-
this.nodeMapByIndex = new
|
|
7502
|
+
this.nodeMapByIndex = new TreeList(
|
|
7503
|
+
this.dummyHead.indexNode
|
|
7504
|
+
);
|
|
7518
7505
|
this.nodeMapByCreatedAt = /* @__PURE__ */ new Map();
|
|
7519
7506
|
this.elementMapByCreatedAt = /* @__PURE__ */ new Map();
|
|
7520
|
-
this.nodeMapByIndex.insert(this.dummyHead);
|
|
7521
7507
|
this.nodeMapByCreatedAt.set(
|
|
7522
7508
|
this.dummyHead.getCreatedAt().toIDString(),
|
|
7523
7509
|
this.dummyHead
|
|
@@ -7550,7 +7536,7 @@
|
|
|
7550
7536
|
this.last = node.getPrev();
|
|
7551
7537
|
}
|
|
7552
7538
|
node.release();
|
|
7553
|
-
this.nodeMapByIndex.delete(node);
|
|
7539
|
+
this.nodeMapByIndex.delete(node.indexNode);
|
|
7554
7540
|
this.nodeMapByCreatedAt.delete(node.getPositionCreatedAt().toIDString());
|
|
7555
7541
|
}
|
|
7556
7542
|
/**
|
|
@@ -7578,7 +7564,7 @@
|
|
|
7578
7564
|
if (prevNode === this.last) {
|
|
7579
7565
|
this.last = newNode;
|
|
7580
7566
|
}
|
|
7581
|
-
this.nodeMapByIndex.insertAfter(prevNode, newNode);
|
|
7567
|
+
this.nodeMapByIndex.insertAfter(prevNode.indexNode, newNode.indexNode);
|
|
7582
7568
|
this.nodeMapByCreatedAt.set(value.getCreatedAt().toIDString(), newNode);
|
|
7583
7569
|
this.elementMapByCreatedAt.set(
|
|
7584
7570
|
value.getCreatedAt().toIDString(),
|
|
@@ -7605,7 +7591,7 @@
|
|
|
7605
7591
|
if (prevNode === this.last) {
|
|
7606
7592
|
this.last = newNode;
|
|
7607
7593
|
}
|
|
7608
|
-
this.nodeMapByIndex.insertAfter(prevNode, newNode);
|
|
7594
|
+
this.nodeMapByIndex.insertAfter(prevNode.indexNode, newNode.indexNode);
|
|
7609
7595
|
this.nodeMapByCreatedAt.set(executedAt.toIDString(), newNode);
|
|
7610
7596
|
return newNode;
|
|
7611
7597
|
}
|
|
@@ -7634,19 +7620,19 @@
|
|
|
7634
7620
|
}
|
|
7635
7621
|
const deadPosNode = this.insertPositionAfter(prevCreatedAt, executedAt);
|
|
7636
7622
|
deadPosNode.setRemovedAt(executedAt);
|
|
7637
|
-
this.nodeMapByIndex.
|
|
7623
|
+
this.nodeMapByIndex.updateWeight(deadPosNode.indexNode);
|
|
7638
7624
|
return deadPosNode;
|
|
7639
7625
|
}
|
|
7640
7626
|
const newPosNode = this.insertPositionAfter(prevCreatedAt, executedAt);
|
|
7641
7627
|
const oldPosNode = entry.positionNode;
|
|
7642
7628
|
oldPosNode.setElementEntry(void 0);
|
|
7643
7629
|
oldPosNode.setRemovedAt(executedAt);
|
|
7644
|
-
this.nodeMapByIndex.
|
|
7630
|
+
this.nodeMapByIndex.updateWeight(oldPosNode.indexNode);
|
|
7645
7631
|
newPosNode.setElementEntry(entry);
|
|
7646
7632
|
entry.positionNode = newPosNode;
|
|
7647
7633
|
entry.posMovedAt = executedAt;
|
|
7648
7634
|
entry.elem.setMovedAt(executedAt);
|
|
7649
|
-
this.nodeMapByIndex.
|
|
7635
|
+
this.nodeMapByIndex.updateWeight(newPosNode.indexNode);
|
|
7650
7636
|
return oldPosNode;
|
|
7651
7637
|
}
|
|
7652
7638
|
/**
|
|
@@ -7677,9 +7663,9 @@
|
|
|
7677
7663
|
if (!node) {
|
|
7678
7664
|
return;
|
|
7679
7665
|
}
|
|
7680
|
-
return String(this.nodeMapByIndex.indexOf(node));
|
|
7666
|
+
return String(this.nodeMapByIndex.indexOf(node.indexNode));
|
|
7681
7667
|
}
|
|
7682
|
-
return String(this.nodeMapByIndex.indexOf(entry.positionNode));
|
|
7668
|
+
return String(this.nodeMapByIndex.indexOf(entry.positionNode.indexNode));
|
|
7683
7669
|
}
|
|
7684
7670
|
/**
|
|
7685
7671
|
* `purge` physically purges the given child. Handles both dead
|
|
@@ -7712,8 +7698,7 @@
|
|
|
7712
7698
|
if (idx >= this.length) {
|
|
7713
7699
|
return;
|
|
7714
7700
|
}
|
|
7715
|
-
|
|
7716
|
-
return node;
|
|
7701
|
+
return this.nodeMapByIndex.find(idx).getValue();
|
|
7717
7702
|
}
|
|
7718
7703
|
/**
|
|
7719
7704
|
* `findPrevCreatedAt` returns the position node's createdAt of the
|
|
@@ -7761,7 +7746,7 @@
|
|
|
7761
7746
|
const node = entry.positionNode;
|
|
7762
7747
|
const alreadyRemoved = node.isRemoved();
|
|
7763
7748
|
if (entry.elem.remove(editedAt) && !alreadyRemoved) {
|
|
7764
|
-
this.nodeMapByIndex.
|
|
7749
|
+
this.nodeMapByIndex.updateWeight(node.indexNode);
|
|
7765
7750
|
}
|
|
7766
7751
|
return entry.elem;
|
|
7767
7752
|
}
|
|
@@ -7787,7 +7772,7 @@
|
|
|
7787
7772
|
return;
|
|
7788
7773
|
}
|
|
7789
7774
|
if (node.remove(editedAt)) {
|
|
7790
|
-
this.nodeMapByIndex.
|
|
7775
|
+
this.nodeMapByIndex.updateWeight(node.indexNode);
|
|
7791
7776
|
}
|
|
7792
7777
|
return node.getValue();
|
|
7793
7778
|
}
|
|
@@ -7798,10 +7783,15 @@
|
|
|
7798
7783
|
return this.dummyHead.getValue();
|
|
7799
7784
|
}
|
|
7800
7785
|
/**
|
|
7801
|
-
* `getLast` returns the value of last elements.
|
|
7786
|
+
* `getLast` returns the value of last elements. Skips bare position nodes
|
|
7787
|
+
* (created by moveAfter/addDeadPosition) that have no element.
|
|
7802
7788
|
*/
|
|
7803
7789
|
getLast() {
|
|
7804
|
-
|
|
7790
|
+
let node = this.last;
|
|
7791
|
+
while (!node.getElementEntry() && node !== this.dummyHead) {
|
|
7792
|
+
node = node.getPrev();
|
|
7793
|
+
}
|
|
7794
|
+
return node.getValue();
|
|
7805
7795
|
}
|
|
7806
7796
|
/**
|
|
7807
7797
|
* `getLastCreatedAt` returns the position node's createdAt of the
|
|
@@ -7836,7 +7826,7 @@
|
|
|
7836
7826
|
const prevNode = this.last;
|
|
7837
7827
|
RGATreeListNode.insertNodeAfter(prevNode, node);
|
|
7838
7828
|
this.last = node;
|
|
7839
|
-
this.nodeMapByIndex.insertAfter(prevNode, node);
|
|
7829
|
+
this.nodeMapByIndex.insertAfter(prevNode.indexNode, node.indexNode);
|
|
7840
7830
|
this.nodeMapByCreatedAt.set(posCreatedAt.toIDString(), node);
|
|
7841
7831
|
}
|
|
7842
7832
|
/**
|
|
@@ -7852,7 +7842,7 @@
|
|
|
7852
7842
|
const prevNode = this.last;
|
|
7853
7843
|
RGATreeListNode.insertNodeAfter(prevNode, node);
|
|
7854
7844
|
this.last = node;
|
|
7855
|
-
this.nodeMapByIndex.insertAfter(prevNode, node);
|
|
7845
|
+
this.nodeMapByIndex.insertAfter(prevNode.indexNode, node.indexNode);
|
|
7856
7846
|
this.nodeMapByCreatedAt.set(posCreatedAt.toIDString(), node);
|
|
7857
7847
|
this.elementMapByCreatedAt.set(elem.getCreatedAt().toIDString(), entry);
|
|
7858
7848
|
}
|
|
@@ -8018,9 +8008,10 @@
|
|
|
8018
8008
|
*/
|
|
8019
8009
|
*[Symbol.iterator]() {
|
|
8020
8010
|
for (const node of this.elements) {
|
|
8021
|
-
if (node.
|
|
8022
|
-
|
|
8011
|
+
if (node.isRemoved()) {
|
|
8012
|
+
continue;
|
|
8023
8013
|
}
|
|
8014
|
+
yield node.getValue();
|
|
8024
8015
|
}
|
|
8025
8016
|
}
|
|
8026
8017
|
/**
|
|
@@ -8445,127 +8436,570 @@
|
|
|
8445
8436
|
return `${this.getParentCreatedAt().toTestString()}.SET.${this.key}=${this.value.toSortedJSON()}`;
|
|
8446
8437
|
}
|
|
8447
8438
|
/**
|
|
8448
|
-
* `getKey` returns the key of this operation.
|
|
8439
|
+
* `getKey` returns the key of this operation.
|
|
8440
|
+
*/
|
|
8441
|
+
getKey() {
|
|
8442
|
+
return this.key;
|
|
8443
|
+
}
|
|
8444
|
+
/**
|
|
8445
|
+
* `getValue` returns the value of this operation.
|
|
8446
|
+
*/
|
|
8447
|
+
getValue() {
|
|
8448
|
+
return this.value;
|
|
8449
|
+
}
|
|
8450
|
+
}
|
|
8451
|
+
class MoveOperation extends Operation {
|
|
8452
|
+
prevCreatedAt;
|
|
8453
|
+
createdAt;
|
|
8454
|
+
constructor(parentCreatedAt, prevCreatedAt, createdAt, executedAt) {
|
|
8455
|
+
super(parentCreatedAt, executedAt);
|
|
8456
|
+
this.prevCreatedAt = prevCreatedAt;
|
|
8457
|
+
this.createdAt = createdAt;
|
|
8458
|
+
}
|
|
8459
|
+
/**
|
|
8460
|
+
* `create` creates a new instance of MoveOperation.
|
|
8461
|
+
*/
|
|
8462
|
+
static create(parentCreatedAt, prevCreatedAt, createdAt, executedAt) {
|
|
8463
|
+
return new MoveOperation(
|
|
8464
|
+
parentCreatedAt,
|
|
8465
|
+
prevCreatedAt,
|
|
8466
|
+
createdAt,
|
|
8467
|
+
executedAt
|
|
8468
|
+
);
|
|
8469
|
+
}
|
|
8470
|
+
/**
|
|
8471
|
+
* `execute` executes this operation on the given `CRDTRoot`.
|
|
8472
|
+
*/
|
|
8473
|
+
execute(root) {
|
|
8474
|
+
const parentObject = root.findByCreatedAt(this.getParentCreatedAt());
|
|
8475
|
+
if (!parentObject) {
|
|
8476
|
+
throw new YorkieError(
|
|
8477
|
+
Code.ErrInvalidArgument,
|
|
8478
|
+
`fail to find ${this.getParentCreatedAt()}`
|
|
8479
|
+
);
|
|
8480
|
+
}
|
|
8481
|
+
if (!(parentObject instanceof CRDTArray)) {
|
|
8482
|
+
throw new YorkieError(
|
|
8483
|
+
Code.ErrInvalidArgument,
|
|
8484
|
+
`fail to execute, only array can execute move`
|
|
8485
|
+
);
|
|
8486
|
+
}
|
|
8487
|
+
const array = parentObject;
|
|
8488
|
+
const reverseOp = this.toReverseOperation(array);
|
|
8489
|
+
const previousIndex = Number(array.subPathOf(this.createdAt));
|
|
8490
|
+
const deadNode = array.moveAfter(
|
|
8491
|
+
this.prevCreatedAt,
|
|
8492
|
+
this.createdAt,
|
|
8493
|
+
this.getExecutedAt()
|
|
8494
|
+
);
|
|
8495
|
+
if (deadNode) {
|
|
8496
|
+
root.registerGCPair({
|
|
8497
|
+
parent: array.getRGATreeList(),
|
|
8498
|
+
child: deadNode
|
|
8499
|
+
});
|
|
8500
|
+
}
|
|
8501
|
+
const index = Number(array.subPathOf(this.createdAt));
|
|
8502
|
+
return {
|
|
8503
|
+
opInfos: [
|
|
8504
|
+
{
|
|
8505
|
+
type: "move",
|
|
8506
|
+
path: root.createPath(this.getParentCreatedAt()),
|
|
8507
|
+
index,
|
|
8508
|
+
previousIndex
|
|
8509
|
+
}
|
|
8510
|
+
],
|
|
8511
|
+
reverseOp
|
|
8512
|
+
};
|
|
8513
|
+
}
|
|
8514
|
+
toReverseOperation(array) {
|
|
8515
|
+
const preservePrevCreatedAt = array.getPrevCreatedAt(this.createdAt);
|
|
8516
|
+
return MoveOperation.create(
|
|
8517
|
+
this.getParentCreatedAt(),
|
|
8518
|
+
preservePrevCreatedAt,
|
|
8519
|
+
this.createdAt
|
|
8520
|
+
);
|
|
8521
|
+
}
|
|
8522
|
+
/**
|
|
8523
|
+
* `getEffectedCreatedAt` returns the creation time of the
|
|
8524
|
+
* effected element.
|
|
8525
|
+
*/
|
|
8526
|
+
getEffectedCreatedAt() {
|
|
8527
|
+
return this.createdAt;
|
|
8528
|
+
}
|
|
8529
|
+
/**
|
|
8530
|
+
* `toTestString` returns a string containing the meta data.
|
|
8531
|
+
*/
|
|
8532
|
+
toTestString() {
|
|
8533
|
+
return `${this.getParentCreatedAt().toTestString()}.MOVE`;
|
|
8534
|
+
}
|
|
8535
|
+
/**
|
|
8536
|
+
* `getPrevCreatedAt` returns the creation time of previous
|
|
8537
|
+
* element.
|
|
8538
|
+
*/
|
|
8539
|
+
getPrevCreatedAt() {
|
|
8540
|
+
return this.prevCreatedAt;
|
|
8541
|
+
}
|
|
8542
|
+
/**
|
|
8543
|
+
* `getCreatedAt` returns the creation time of the target element.
|
|
8544
|
+
*/
|
|
8545
|
+
getCreatedAt() {
|
|
8546
|
+
return this.createdAt;
|
|
8547
|
+
}
|
|
8548
|
+
/**
|
|
8549
|
+
* `setPrevCreatedAt` sets the creation time of the previous
|
|
8550
|
+
* element.
|
|
8551
|
+
*/
|
|
8552
|
+
setPrevCreatedAt(createdAt) {
|
|
8553
|
+
this.prevCreatedAt = createdAt;
|
|
8554
|
+
}
|
|
8555
|
+
/**
|
|
8556
|
+
* `setCreatedAt` sets the creation time of the target element.
|
|
8557
|
+
*/
|
|
8558
|
+
setCreatedAt(createdAt) {
|
|
8559
|
+
this.createdAt = createdAt;
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
class SplayNode {
|
|
8563
|
+
value;
|
|
8564
|
+
left;
|
|
8565
|
+
right;
|
|
8566
|
+
parent;
|
|
8567
|
+
weight;
|
|
8568
|
+
constructor(value) {
|
|
8569
|
+
this.value = value;
|
|
8570
|
+
this.initWeight();
|
|
8571
|
+
}
|
|
8572
|
+
/**
|
|
8573
|
+
* `getNodeString` returns a string of weight and value of this node.
|
|
8574
|
+
*/
|
|
8575
|
+
getNodeString() {
|
|
8576
|
+
return `${this.weight}${this.value}`;
|
|
8577
|
+
}
|
|
8578
|
+
/**
|
|
8579
|
+
* `getValue` returns value of this node.
|
|
8580
|
+
*/
|
|
8581
|
+
getValue() {
|
|
8582
|
+
return this.value;
|
|
8583
|
+
}
|
|
8584
|
+
/**
|
|
8585
|
+
* `getLeftWeight` returns left weight of this node.
|
|
8586
|
+
*/
|
|
8587
|
+
getLeftWeight() {
|
|
8588
|
+
return !this.hasLeft() ? 0 : this.left.getWeight();
|
|
8589
|
+
}
|
|
8590
|
+
/**
|
|
8591
|
+
* `getRightWeight` returns right weight of this node.
|
|
8592
|
+
*/
|
|
8593
|
+
getRightWeight() {
|
|
8594
|
+
return !this.hasRight() ? 0 : this.right.getWeight();
|
|
8595
|
+
}
|
|
8596
|
+
/**
|
|
8597
|
+
* `getWeight` returns weight of this node.
|
|
8598
|
+
*/
|
|
8599
|
+
getWeight() {
|
|
8600
|
+
return this.weight;
|
|
8601
|
+
}
|
|
8602
|
+
/**
|
|
8603
|
+
* `getLeft` returns a left node.
|
|
8604
|
+
*/
|
|
8605
|
+
getLeft() {
|
|
8606
|
+
return this.left;
|
|
8607
|
+
}
|
|
8608
|
+
/**
|
|
8609
|
+
* `getRight` returns a right node.
|
|
8610
|
+
*/
|
|
8611
|
+
getRight() {
|
|
8612
|
+
return this.right;
|
|
8613
|
+
}
|
|
8614
|
+
/**
|
|
8615
|
+
* `getParent` returns parent of this node.
|
|
8616
|
+
*/
|
|
8617
|
+
getParent() {
|
|
8618
|
+
return this.parent;
|
|
8619
|
+
}
|
|
8620
|
+
/**
|
|
8621
|
+
* `hasLeft` check if the left node exists
|
|
8622
|
+
*/
|
|
8623
|
+
hasLeft() {
|
|
8624
|
+
return !!this.left;
|
|
8625
|
+
}
|
|
8626
|
+
/**
|
|
8627
|
+
* `hasRight` check if the right node exists
|
|
8628
|
+
*/
|
|
8629
|
+
hasRight() {
|
|
8630
|
+
return !!this.right;
|
|
8631
|
+
}
|
|
8632
|
+
/**
|
|
8633
|
+
* `hasParent` check if the parent node exists
|
|
8634
|
+
*/
|
|
8635
|
+
hasParent() {
|
|
8636
|
+
return !!this.parent;
|
|
8637
|
+
}
|
|
8638
|
+
/**
|
|
8639
|
+
* `setLeft` sets a left node.
|
|
8640
|
+
*/
|
|
8641
|
+
setLeft(left) {
|
|
8642
|
+
this.left = left;
|
|
8643
|
+
}
|
|
8644
|
+
/**
|
|
8645
|
+
* `setRight` sets a right node.
|
|
8646
|
+
*/
|
|
8647
|
+
setRight(right) {
|
|
8648
|
+
this.right = right;
|
|
8649
|
+
}
|
|
8650
|
+
/**
|
|
8651
|
+
* `setParent` sets a parent node.
|
|
8652
|
+
*/
|
|
8653
|
+
setParent(parent) {
|
|
8654
|
+
this.parent = parent;
|
|
8655
|
+
}
|
|
8656
|
+
/**
|
|
8657
|
+
* `unlink` unlink parent, right and left node.
|
|
8658
|
+
*/
|
|
8659
|
+
unlink() {
|
|
8660
|
+
this.parent = void 0;
|
|
8661
|
+
this.right = void 0;
|
|
8662
|
+
this.left = void 0;
|
|
8663
|
+
}
|
|
8664
|
+
/**
|
|
8665
|
+
* `hasLinks` checks if parent, right and left node exists.
|
|
8666
|
+
*/
|
|
8667
|
+
hasLinks() {
|
|
8668
|
+
return this.hasParent() || this.hasLeft() || this.hasRight();
|
|
8669
|
+
}
|
|
8670
|
+
/**
|
|
8671
|
+
* `increaseWeight` increases weight.
|
|
8672
|
+
*/
|
|
8673
|
+
increaseWeight(weight) {
|
|
8674
|
+
this.weight += weight;
|
|
8675
|
+
}
|
|
8676
|
+
/**
|
|
8677
|
+
* `initWeight` sets initial weight of this node.
|
|
8678
|
+
*/
|
|
8679
|
+
initWeight() {
|
|
8680
|
+
this.weight = this.getLength();
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
class SplayTree {
|
|
8684
|
+
root;
|
|
8685
|
+
constructor(root) {
|
|
8686
|
+
this.root = root;
|
|
8687
|
+
}
|
|
8688
|
+
/**
|
|
8689
|
+
* `length` returns the size of this tree.
|
|
8690
|
+
*/
|
|
8691
|
+
get length() {
|
|
8692
|
+
return this.root ? this.root.getWeight() : 0;
|
|
8693
|
+
}
|
|
8694
|
+
/**
|
|
8695
|
+
* `findForText` returns the Node and offset of the given position (cursor).
|
|
8696
|
+
* Used for Text where cursor placed between characters.
|
|
8697
|
+
*/
|
|
8698
|
+
findForText(pos) {
|
|
8699
|
+
if (!this.root || pos < 0) {
|
|
8700
|
+
return [void 0, 0];
|
|
8701
|
+
}
|
|
8702
|
+
let node = this.root;
|
|
8703
|
+
for (; ; ) {
|
|
8704
|
+
if (node.hasLeft() && pos <= node.getLeftWeight()) {
|
|
8705
|
+
node = node.getLeft();
|
|
8706
|
+
} else if (node.hasRight() && node.getLeftWeight() + node.getLength() < pos) {
|
|
8707
|
+
pos -= node.getLeftWeight() + node.getLength();
|
|
8708
|
+
node = node.getRight();
|
|
8709
|
+
} else {
|
|
8710
|
+
pos -= node.getLeftWeight();
|
|
8711
|
+
break;
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
if (pos > node.getLength()) {
|
|
8715
|
+
throw new YorkieError(
|
|
8716
|
+
Code.ErrInvalidArgument,
|
|
8717
|
+
`out of index range: pos: ${pos} > node.length: ${node.getLength()}`
|
|
8718
|
+
);
|
|
8719
|
+
}
|
|
8720
|
+
this.splayNode(node);
|
|
8721
|
+
return [node, pos];
|
|
8722
|
+
}
|
|
8723
|
+
/**
|
|
8724
|
+
* `findForArray` returns the Node of the given position (index).
|
|
8725
|
+
* Used for Array where index points to the element.
|
|
8726
|
+
*/
|
|
8727
|
+
findForArray(idx) {
|
|
8728
|
+
if (!this.root) {
|
|
8729
|
+
return void 0;
|
|
8730
|
+
}
|
|
8731
|
+
if (idx < 0 || idx >= this.length) {
|
|
8732
|
+
throw new YorkieError(
|
|
8733
|
+
Code.ErrInvalidArgument,
|
|
8734
|
+
`out of index range: idx: ${idx}, length: ${this.length}`
|
|
8735
|
+
);
|
|
8736
|
+
}
|
|
8737
|
+
let node = this.root;
|
|
8738
|
+
for (; ; ) {
|
|
8739
|
+
if (node.hasLeft() && idx < node.getLeftWeight()) {
|
|
8740
|
+
node = node.getLeft();
|
|
8741
|
+
} else if (node.hasRight() && node.getLeftWeight() + node.getLength() <= idx) {
|
|
8742
|
+
idx -= node.getLeftWeight() + node.getLength();
|
|
8743
|
+
node = node.getRight();
|
|
8744
|
+
} else {
|
|
8745
|
+
break;
|
|
8746
|
+
}
|
|
8747
|
+
}
|
|
8748
|
+
this.splayNode(node);
|
|
8749
|
+
return node;
|
|
8750
|
+
}
|
|
8751
|
+
/**
|
|
8752
|
+
* Find the index of the given node in BST.
|
|
8753
|
+
*
|
|
8754
|
+
* @param node - the given node
|
|
8755
|
+
* @returns the index of given node
|
|
8756
|
+
*/
|
|
8757
|
+
indexOf(node) {
|
|
8758
|
+
if (!node || node !== this.root && !node.hasLinks()) {
|
|
8759
|
+
return -1;
|
|
8760
|
+
}
|
|
8761
|
+
this.splayNode(node);
|
|
8762
|
+
return this.root.getLeftWeight();
|
|
8763
|
+
}
|
|
8764
|
+
/**
|
|
8765
|
+
* `getRoot` returns root of this tree.
|
|
8766
|
+
*/
|
|
8767
|
+
getRoot() {
|
|
8768
|
+
return this.root;
|
|
8769
|
+
}
|
|
8770
|
+
/**
|
|
8771
|
+
* `insert` inserts the node at the last.
|
|
8772
|
+
*/
|
|
8773
|
+
insert(newNode) {
|
|
8774
|
+
return this.insertAfter(this.root, newNode);
|
|
8775
|
+
}
|
|
8776
|
+
/**
|
|
8777
|
+
* `insertAfter` inserts the node after the given previous node.
|
|
8778
|
+
*/
|
|
8779
|
+
insertAfter(target, newNode) {
|
|
8780
|
+
if (!target) {
|
|
8781
|
+
this.root = newNode;
|
|
8782
|
+
return newNode;
|
|
8783
|
+
}
|
|
8784
|
+
this.splayNode(target);
|
|
8785
|
+
this.root = newNode;
|
|
8786
|
+
newNode.setRight(target.getRight());
|
|
8787
|
+
if (target.hasRight()) {
|
|
8788
|
+
target.getRight().setParent(newNode);
|
|
8789
|
+
}
|
|
8790
|
+
newNode.setLeft(target);
|
|
8791
|
+
target.setParent(newNode);
|
|
8792
|
+
target.setRight();
|
|
8793
|
+
this.updateWeight(target);
|
|
8794
|
+
this.updateWeight(newNode);
|
|
8795
|
+
return newNode;
|
|
8796
|
+
}
|
|
8797
|
+
/**
|
|
8798
|
+
* `updateWeight` recalculates the weight of this node with the value and children.
|
|
8799
|
+
*/
|
|
8800
|
+
updateWeight(node) {
|
|
8801
|
+
node.initWeight();
|
|
8802
|
+
if (node.hasLeft()) {
|
|
8803
|
+
node.increaseWeight(node.getLeftWeight());
|
|
8804
|
+
}
|
|
8805
|
+
if (node.hasRight()) {
|
|
8806
|
+
node.increaseWeight(node.getRightWeight());
|
|
8807
|
+
}
|
|
8808
|
+
}
|
|
8809
|
+
updateTreeWeight(node) {
|
|
8810
|
+
while (node) {
|
|
8811
|
+
this.updateWeight(node);
|
|
8812
|
+
node = node.getParent();
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
/**
|
|
8816
|
+
* `splayNode` moves the given node to the root.
|
|
8817
|
+
*/
|
|
8818
|
+
splayNode(node) {
|
|
8819
|
+
if (!node) {
|
|
8820
|
+
return;
|
|
8821
|
+
}
|
|
8822
|
+
for (; ; ) {
|
|
8823
|
+
if (this.isLeftChild(node.getParent()) && this.isRightChild(node)) {
|
|
8824
|
+
this.rotateLeft(node);
|
|
8825
|
+
this.rotateRight(node);
|
|
8826
|
+
} else if (this.isRightChild(node.getParent()) && this.isLeftChild(node)) {
|
|
8827
|
+
this.rotateRight(node);
|
|
8828
|
+
this.rotateLeft(node);
|
|
8829
|
+
} else if (this.isLeftChild(node.getParent()) && this.isLeftChild(node)) {
|
|
8830
|
+
this.rotateRight(node.getParent());
|
|
8831
|
+
this.rotateRight(node);
|
|
8832
|
+
} else if (this.isRightChild(node.getParent()) && this.isRightChild(node)) {
|
|
8833
|
+
this.rotateLeft(node.getParent());
|
|
8834
|
+
this.rotateLeft(node);
|
|
8835
|
+
} else {
|
|
8836
|
+
if (this.isLeftChild(node)) {
|
|
8837
|
+
this.rotateRight(node);
|
|
8838
|
+
} else if (this.isRightChild(node)) {
|
|
8839
|
+
this.rotateLeft(node);
|
|
8840
|
+
}
|
|
8841
|
+
this.updateWeight(node);
|
|
8842
|
+
return;
|
|
8843
|
+
}
|
|
8844
|
+
}
|
|
8845
|
+
}
|
|
8846
|
+
/**
|
|
8847
|
+
* `delete` deletes target node of this tree.
|
|
8449
8848
|
*/
|
|
8450
|
-
|
|
8451
|
-
|
|
8849
|
+
delete(node) {
|
|
8850
|
+
this.splayNode(node);
|
|
8851
|
+
const leftTree = new SplayTree(node.getLeft());
|
|
8852
|
+
if (leftTree.root) {
|
|
8853
|
+
leftTree.root.setParent();
|
|
8854
|
+
}
|
|
8855
|
+
const rightTree = new SplayTree(node.getRight());
|
|
8856
|
+
if (rightTree.root) {
|
|
8857
|
+
rightTree.root.setParent();
|
|
8858
|
+
}
|
|
8859
|
+
if (leftTree.root) {
|
|
8860
|
+
const rightmostNode = leftTree.getRightmost();
|
|
8861
|
+
leftTree.splayNode(rightmostNode);
|
|
8862
|
+
leftTree.root.setRight(rightTree.root);
|
|
8863
|
+
if (rightTree.root) {
|
|
8864
|
+
rightTree.root.setParent(leftTree.root);
|
|
8865
|
+
}
|
|
8866
|
+
this.root = leftTree.root;
|
|
8867
|
+
} else {
|
|
8868
|
+
this.root = rightTree.root;
|
|
8869
|
+
}
|
|
8870
|
+
node.unlink();
|
|
8871
|
+
if (this.root) {
|
|
8872
|
+
this.updateWeight(this.root);
|
|
8873
|
+
}
|
|
8452
8874
|
}
|
|
8453
8875
|
/**
|
|
8454
|
-
* `
|
|
8876
|
+
* `deleteRange` separates the range between given 2 boundaries from this Tree.
|
|
8877
|
+
* This function separates the range to delete as a subtree
|
|
8878
|
+
* by splaying outer boundary nodes.
|
|
8879
|
+
* leftBoundary must exist because of 0-indexed initial dummy node of tree,
|
|
8880
|
+
* but rightBoundary can be nil means range to delete includes the end of tree.
|
|
8881
|
+
* Refer to the design document in https://github.com/yorkie-team/yorkie/tree/main/design
|
|
8455
8882
|
*/
|
|
8456
|
-
|
|
8457
|
-
|
|
8883
|
+
deleteRange(leftBoundary, rightBoundary) {
|
|
8884
|
+
if (!rightBoundary) {
|
|
8885
|
+
this.splayNode(leftBoundary);
|
|
8886
|
+
this.cutOffRight(leftBoundary);
|
|
8887
|
+
return;
|
|
8888
|
+
}
|
|
8889
|
+
this.splayNode(leftBoundary);
|
|
8890
|
+
this.splayNode(rightBoundary);
|
|
8891
|
+
if (rightBoundary.getLeft() != leftBoundary) {
|
|
8892
|
+
this.rotateRight(leftBoundary);
|
|
8893
|
+
}
|
|
8894
|
+
this.cutOffRight(leftBoundary);
|
|
8458
8895
|
}
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
this.
|
|
8466
|
-
this.createdAt = createdAt;
|
|
8896
|
+
cutOffRight(root) {
|
|
8897
|
+
const nodesToFreeWeight = [];
|
|
8898
|
+
this.traversePostorder(root.getRight(), nodesToFreeWeight);
|
|
8899
|
+
for (const node of nodesToFreeWeight) {
|
|
8900
|
+
node.initWeight();
|
|
8901
|
+
}
|
|
8902
|
+
this.updateTreeWeight(root);
|
|
8467
8903
|
}
|
|
8468
8904
|
/**
|
|
8469
|
-
* `
|
|
8905
|
+
* `toTestString` returns a string containing the meta data of the Node
|
|
8906
|
+
* for debugging purpose.
|
|
8470
8907
|
*/
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
createdAt,
|
|
8476
|
-
executedAt
|
|
8477
|
-
);
|
|
8908
|
+
toTestString() {
|
|
8909
|
+
const metaString = [];
|
|
8910
|
+
this.traverseInorder(this.root, metaString);
|
|
8911
|
+
return metaString.map((n) => `[${n.getWeight()},${n.getLength()}]${n.getValue() || ""}`).join("");
|
|
8478
8912
|
}
|
|
8479
8913
|
/**
|
|
8480
|
-
* `
|
|
8914
|
+
* `checkWeight` returns false when there is an incorrect weight node.
|
|
8915
|
+
* for debugging purpose.
|
|
8481
8916
|
*/
|
|
8482
|
-
|
|
8483
|
-
const
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
}
|
|
8490
|
-
if (!(parentObject instanceof CRDTArray)) {
|
|
8491
|
-
throw new YorkieError(
|
|
8492
|
-
Code.ErrInvalidArgument,
|
|
8493
|
-
`fail to execute, only array can execute move`
|
|
8494
|
-
);
|
|
8495
|
-
}
|
|
8496
|
-
const array = parentObject;
|
|
8497
|
-
const reverseOp = this.toReverseOperation(array);
|
|
8498
|
-
const previousIndex = Number(array.subPathOf(this.createdAt));
|
|
8499
|
-
const deadNode = array.moveAfter(
|
|
8500
|
-
this.prevCreatedAt,
|
|
8501
|
-
this.createdAt,
|
|
8502
|
-
this.getExecutedAt()
|
|
8503
|
-
);
|
|
8504
|
-
if (deadNode) {
|
|
8505
|
-
root.registerGCPair({
|
|
8506
|
-
parent: array.getRGATreeList(),
|
|
8507
|
-
child: deadNode
|
|
8508
|
-
});
|
|
8917
|
+
checkWeight() {
|
|
8918
|
+
const nodes = [];
|
|
8919
|
+
this.traverseInorder(this.root, nodes);
|
|
8920
|
+
for (const node of nodes) {
|
|
8921
|
+
if (node.getWeight() != node.getLength() + node.getLeftWeight() + node.getRightWeight()) {
|
|
8922
|
+
return false;
|
|
8923
|
+
}
|
|
8509
8924
|
}
|
|
8510
|
-
|
|
8511
|
-
return {
|
|
8512
|
-
opInfos: [
|
|
8513
|
-
{
|
|
8514
|
-
type: "move",
|
|
8515
|
-
path: root.createPath(this.getParentCreatedAt()),
|
|
8516
|
-
index,
|
|
8517
|
-
previousIndex
|
|
8518
|
-
}
|
|
8519
|
-
],
|
|
8520
|
-
reverseOp
|
|
8521
|
-
};
|
|
8925
|
+
return true;
|
|
8522
8926
|
}
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
);
|
|
8927
|
+
getRightmost() {
|
|
8928
|
+
let node = this.root;
|
|
8929
|
+
while (node.hasRight()) {
|
|
8930
|
+
node = node.getRight();
|
|
8931
|
+
}
|
|
8932
|
+
return node;
|
|
8530
8933
|
}
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8934
|
+
traverseInorder(node, stack) {
|
|
8935
|
+
if (!node) {
|
|
8936
|
+
return;
|
|
8937
|
+
}
|
|
8938
|
+
this.traverseInorder(node.getLeft(), stack);
|
|
8939
|
+
stack.push(node);
|
|
8940
|
+
this.traverseInorder(node.getRight(), stack);
|
|
8537
8941
|
}
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8942
|
+
traversePostorder(node, stack) {
|
|
8943
|
+
if (!node) {
|
|
8944
|
+
return;
|
|
8945
|
+
}
|
|
8946
|
+
this.traversePostorder(node.getLeft(), stack);
|
|
8947
|
+
this.traversePostorder(node.getRight(), stack);
|
|
8948
|
+
stack.push(node);
|
|
8543
8949
|
}
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8950
|
+
rotateLeft(pivot) {
|
|
8951
|
+
const root = pivot.getParent();
|
|
8952
|
+
if (root.hasParent()) {
|
|
8953
|
+
if (root === root.getParent().getLeft()) {
|
|
8954
|
+
root.getParent().setLeft(pivot);
|
|
8955
|
+
} else {
|
|
8956
|
+
root.getParent().setRight(pivot);
|
|
8957
|
+
}
|
|
8958
|
+
} else {
|
|
8959
|
+
this.root = pivot;
|
|
8960
|
+
}
|
|
8961
|
+
pivot.setParent(root.getParent());
|
|
8962
|
+
root.setRight(pivot.getLeft());
|
|
8963
|
+
if (root.hasRight()) {
|
|
8964
|
+
root.getRight().setParent(root);
|
|
8965
|
+
}
|
|
8966
|
+
pivot.setLeft(root);
|
|
8967
|
+
pivot.getLeft().setParent(pivot);
|
|
8968
|
+
this.updateWeight(root);
|
|
8969
|
+
this.updateWeight(pivot);
|
|
8550
8970
|
}
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8971
|
+
rotateRight(pivot) {
|
|
8972
|
+
const root = pivot.getParent();
|
|
8973
|
+
if (root.hasParent()) {
|
|
8974
|
+
if (root === root.getParent().getLeft()) {
|
|
8975
|
+
root.getParent().setLeft(pivot);
|
|
8976
|
+
} else {
|
|
8977
|
+
root.getParent().setRight(pivot);
|
|
8978
|
+
}
|
|
8979
|
+
} else {
|
|
8980
|
+
this.root = pivot;
|
|
8981
|
+
}
|
|
8982
|
+
pivot.setParent(root.getParent());
|
|
8983
|
+
root.setLeft(pivot.getRight());
|
|
8984
|
+
if (root.hasLeft()) {
|
|
8985
|
+
root.getLeft().setParent(root);
|
|
8986
|
+
}
|
|
8987
|
+
pivot.setRight(root);
|
|
8988
|
+
pivot.getRight().setParent(pivot);
|
|
8989
|
+
this.updateWeight(root);
|
|
8990
|
+
this.updateWeight(pivot);
|
|
8556
8991
|
}
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
this.prevCreatedAt = createdAt;
|
|
8992
|
+
isLeftChild(node) {
|
|
8993
|
+
if (node && node.hasParent()) {
|
|
8994
|
+
return node.getParent().getLeft() === node;
|
|
8995
|
+
}
|
|
8996
|
+
return false;
|
|
8563
8997
|
}
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8998
|
+
isRightChild(node) {
|
|
8999
|
+
if (node && node.hasParent()) {
|
|
9000
|
+
return node.getParent().getRight() === node;
|
|
9001
|
+
}
|
|
9002
|
+
return false;
|
|
8569
9003
|
}
|
|
8570
9004
|
}
|
|
8571
9005
|
const DefaultComparator = (a, b) => {
|
|
@@ -8583,10 +9017,10 @@
|
|
|
8583
9017
|
left;
|
|
8584
9018
|
right;
|
|
8585
9019
|
isRed;
|
|
8586
|
-
constructor(key, value,
|
|
9020
|
+
constructor(key, value, isRed2) {
|
|
8587
9021
|
this.key = key;
|
|
8588
9022
|
this.value = value;
|
|
8589
|
-
this.isRed =
|
|
9023
|
+
this.isRed = isRed2;
|
|
8590
9024
|
}
|
|
8591
9025
|
}
|
|
8592
9026
|
class SortedMapIterator {
|
|
@@ -13770,6 +14204,26 @@
|
|
|
13770
14204
|
newID.versionVector.set(this.actor, lamport);
|
|
13771
14205
|
return newID;
|
|
13772
14206
|
}
|
|
14207
|
+
/**
|
|
14208
|
+
* `syncLamport` advances the lamport clock against the given ID without
|
|
14209
|
+
* merging its version vector into the receiver's. It is the counterpart
|
|
14210
|
+
* of `syncClocks` for attachments that have opted out of GC participation
|
|
14211
|
+
* (see docs/design/disable-gc-on-attach.md in the server repo): the
|
|
14212
|
+
* receiver does not need other actors' entries in its VV because it
|
|
14213
|
+
* never produces or consumes tombstones, and dropping them keeps each
|
|
14214
|
+
* subsequent local Change's VV at O(1) instead of O(num_actors).
|
|
14215
|
+
* Lamport must still advance so that TimeTickets produced locally
|
|
14216
|
+
* remain ordered against remote operations.
|
|
14217
|
+
*/
|
|
14218
|
+
syncLamport(other) {
|
|
14219
|
+
if (!other.hasClocks()) {
|
|
14220
|
+
return this;
|
|
14221
|
+
}
|
|
14222
|
+
const lamport = other.lamport > this.lamport ? other.lamport + 1n : this.lamport + 1n;
|
|
14223
|
+
const vector = this.versionVector.deepcopy();
|
|
14224
|
+
vector.set(this.actor, lamport);
|
|
14225
|
+
return new ChangeID(this.clientSeq, lamport, this.actor, vector);
|
|
14226
|
+
}
|
|
13773
14227
|
/**
|
|
13774
14228
|
* `setClocks` sets the given clocks to this ID. This is used when the snapshot
|
|
13775
14229
|
* is given from the server.
|
|
@@ -19646,6 +20100,12 @@
|
|
|
19646
20100
|
onlineClients;
|
|
19647
20101
|
eventStream;
|
|
19648
20102
|
eventStreamObserver;
|
|
20103
|
+
// `disableGC`, when true, declares that this document does not produce or
|
|
20104
|
+
// consume tombstones (see disable-gc-on-attach in the server repo). It is
|
|
20105
|
+
// set by the client on Attach and consumed by applyChange to skip merging
|
|
20106
|
+
// remote actors' version vectors into changeID, keeping each subsequent
|
|
20107
|
+
// local Change's VV at O(1) for high-fan-out Counter workloads.
|
|
20108
|
+
disableGC;
|
|
19649
20109
|
/**
|
|
19650
20110
|
* `history` is exposed to the user to manage undo/redo operations.
|
|
19651
20111
|
*/
|
|
@@ -19659,6 +20119,7 @@
|
|
|
19659
20119
|
this.changeID = InitialChangeID;
|
|
19660
20120
|
this.checkpoint = InitialCheckpoint;
|
|
19661
20121
|
this.localChanges = [];
|
|
20122
|
+
this.disableGC = false;
|
|
19662
20123
|
this.root = CRDTRoot.create();
|
|
19663
20124
|
this.presences = /* @__PURE__ */ new Map();
|
|
19664
20125
|
this.onlineClients = /* @__PURE__ */ new Set();
|
|
@@ -20067,6 +20528,14 @@
|
|
|
20067
20528
|
}
|
|
20068
20529
|
this.changeID = this.changeID.setActor(actorID);
|
|
20069
20530
|
}
|
|
20531
|
+
/**
|
|
20532
|
+
* `setDisableGC` records whether this document participates in GC. The
|
|
20533
|
+
* client calls this on attach so subsequent applyChange runs use the
|
|
20534
|
+
* lamport-only sync path.
|
|
20535
|
+
*/
|
|
20536
|
+
setDisableGC(disableGC) {
|
|
20537
|
+
this.disableGC = disableGC;
|
|
20538
|
+
}
|
|
20070
20539
|
/**
|
|
20071
20540
|
* `isEnableDevtools` returns whether devtools is enabled or not.
|
|
20072
20541
|
*/
|
|
@@ -20305,7 +20774,7 @@
|
|
|
20305
20774
|
);
|
|
20306
20775
|
}
|
|
20307
20776
|
}
|
|
20308
|
-
this.changeID = this.changeID.syncClocks(change.getID());
|
|
20777
|
+
this.changeID = this.disableGC ? this.changeID.syncLamport(change.getID()) : this.changeID.syncClocks(change.getID());
|
|
20309
20778
|
if (opInfos.length) {
|
|
20310
20779
|
const rawChange = this.isEnableDevtools() ? change.toStruct() : void 0;
|
|
20311
20780
|
events.push(
|
|
@@ -20988,7 +21457,7 @@
|
|
|
20988
21457
|
};
|
|
20989
21458
|
}
|
|
20990
21459
|
const name = "@yorkie-js/sdk";
|
|
20991
|
-
const version = "0.7.
|
|
21460
|
+
const version = "0.7.11";
|
|
20992
21461
|
const pkg = {
|
|
20993
21462
|
name,
|
|
20994
21463
|
version
|
|
@@ -21549,6 +22018,7 @@
|
|
|
21549
22018
|
doc.setSchemaRules(converter.fromSchemaRules(res.schemaRules));
|
|
21550
22019
|
}
|
|
21551
22020
|
const pack = converter.fromChangePack(res.changePack);
|
|
22021
|
+
doc.setDisableGC(opts.disableGC ?? false);
|
|
21552
22022
|
doc.applyChangePack(pack);
|
|
21553
22023
|
if (doc.getStatus() === DocStatus.Removed) {
|
|
21554
22024
|
return doc;
|