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