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