serializable-bptree 6.2.3 → 7.0.1
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/README.md +4 -1
- package/dist/cjs/index.cjs +1109 -658
- package/dist/esm/index.mjs +1109 -658
- package/dist/types/BPTreeAsync.d.ts +9 -44
- package/dist/types/BPTreeSync.d.ts +7 -41
- package/dist/types/SerializeStrategyAsync.d.ts +3 -3
- package/dist/types/SerializeStrategySync.d.ts +3 -3
- package/dist/types/base/BPTree.d.ts +19 -79
- package/dist/types/base/BPTreeAsyncBase.d.ts +41 -0
- package/dist/types/base/BPTreeSyncBase.d.ts +39 -0
- package/dist/types/base/SerializeStrategy.d.ts +9 -8
- package/dist/types/index.d.ts +3 -2
- package/dist/types/transaction/BPTreeAsyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeAsyncTransaction.d.ts +45 -0
- package/dist/types/transaction/BPTreeSyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeSyncTransaction.d.ts +43 -0
- package/dist/types/types/index.d.ts +91 -0
- package/package.json +1 -1
- package/dist/types/utils/types.d.ts +0 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BPTreeAsync: () => BPTreeAsync,
|
|
24
|
+
BPTreeAsyncTransaction: () => BPTreeAsyncTransaction,
|
|
24
25
|
BPTreeSync: () => BPTreeSync,
|
|
26
|
+
BPTreeSyncTransaction: () => BPTreeSyncTransaction,
|
|
25
27
|
InMemoryStoreStrategyAsync: () => InMemoryStoreStrategyAsync,
|
|
26
28
|
InMemoryStoreStrategySync: () => InMemoryStoreStrategySync,
|
|
27
29
|
NumericComparator: () => NumericComparator,
|
|
@@ -473,6 +475,20 @@ var BPTree = class _BPTree {
|
|
|
473
475
|
option;
|
|
474
476
|
order;
|
|
475
477
|
rootId;
|
|
478
|
+
/**
|
|
479
|
+
* Returns the ID of the root node.
|
|
480
|
+
* @returns The root node ID.
|
|
481
|
+
*/
|
|
482
|
+
getRootId() {
|
|
483
|
+
return this.rootId;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Returns the order of the B+Tree.
|
|
487
|
+
* @returns The order of the tree.
|
|
488
|
+
*/
|
|
489
|
+
getOrder() {
|
|
490
|
+
return this.order;
|
|
491
|
+
}
|
|
476
492
|
_strategyDirty;
|
|
477
493
|
_nodeCreateBuffer;
|
|
478
494
|
_nodeUpdateBuffer;
|
|
@@ -705,24 +721,21 @@ var BPTree = class _BPTree {
|
|
|
705
721
|
break;
|
|
706
722
|
}
|
|
707
723
|
keys.push(key);
|
|
708
|
-
this.bufferForNodeUpdate(node);
|
|
709
|
-
break;
|
|
724
|
+
return this.bufferForNodeUpdate(node);
|
|
710
725
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
711
726
|
node.values.splice(i, 0, value);
|
|
712
727
|
node.keys.splice(i, 0, [key]);
|
|
713
|
-
this.bufferForNodeUpdate(node);
|
|
714
|
-
break;
|
|
728
|
+
return this.bufferForNodeUpdate(node);
|
|
715
729
|
} else if (i + 1 === node.values.length) {
|
|
716
730
|
node.values.push(value);
|
|
717
731
|
node.keys.push([key]);
|
|
718
|
-
this.bufferForNodeUpdate(node);
|
|
719
|
-
break;
|
|
732
|
+
return this.bufferForNodeUpdate(node);
|
|
720
733
|
}
|
|
721
734
|
}
|
|
722
735
|
} else {
|
|
723
736
|
node.values = [value];
|
|
724
737
|
node.keys = [[key]];
|
|
725
|
-
this.bufferForNodeUpdate(node);
|
|
738
|
+
return this.bufferForNodeUpdate(node);
|
|
726
739
|
}
|
|
727
740
|
}
|
|
728
741
|
bufferForNodeCreate(node) {
|
|
@@ -761,8 +774,8 @@ var BPTree = class _BPTree {
|
|
|
761
774
|
}
|
|
762
775
|
};
|
|
763
776
|
|
|
764
|
-
// src/
|
|
765
|
-
var
|
|
777
|
+
// src/base/BPTreeSyncBase.ts
|
|
778
|
+
var BPTreeSyncBase = class extends BPTree {
|
|
766
779
|
constructor(strategy, comparator, option) {
|
|
767
780
|
super(strategy, comparator, option);
|
|
768
781
|
this.nodes = this._createCachedNode();
|
|
@@ -838,7 +851,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
838
851
|
}
|
|
839
852
|
return id;
|
|
840
853
|
}
|
|
841
|
-
_createNode(isLeaf, keys, values, leaf =
|
|
854
|
+
_createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
842
855
|
const id = this._createNodeId(isLeaf);
|
|
843
856
|
const node = {
|
|
844
857
|
id,
|
|
@@ -849,29 +862,26 @@ var BPTreeSync = class extends BPTree {
|
|
|
849
862
|
next,
|
|
850
863
|
prev
|
|
851
864
|
};
|
|
852
|
-
this.
|
|
865
|
+
this.bufferForNodeCreate(node);
|
|
853
866
|
return node;
|
|
854
867
|
}
|
|
855
868
|
_deleteEntry(node, key, value) {
|
|
856
869
|
if (!node.leaf) {
|
|
870
|
+
let keyIndex = -1;
|
|
857
871
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
node.keys.splice(i, 1);
|
|
861
|
-
this.bufferForNodeUpdate(node);
|
|
872
|
+
if (node.keys[i] === key) {
|
|
873
|
+
keyIndex = i;
|
|
862
874
|
break;
|
|
863
875
|
}
|
|
864
876
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
break;
|
|
871
|
-
}
|
|
877
|
+
if (keyIndex !== -1) {
|
|
878
|
+
node.keys.splice(keyIndex, 1);
|
|
879
|
+
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
880
|
+
node.values.splice(valueIndex, 1);
|
|
881
|
+
this.bufferForNodeUpdate(node);
|
|
872
882
|
}
|
|
873
883
|
}
|
|
874
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
884
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
875
885
|
const keys = node.keys;
|
|
876
886
|
this.bufferForNodeDelete(node);
|
|
877
887
|
const newRoot = this.getNode(keys[0]);
|
|
@@ -940,20 +950,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
940
950
|
pointer.values.push(guess);
|
|
941
951
|
} else {
|
|
942
952
|
pointer.next = node.next;
|
|
943
|
-
pointer.prev = node.id;
|
|
944
953
|
if (pointer.next) {
|
|
945
|
-
const n = this.getNode(
|
|
954
|
+
const n = this.getNode(pointer.next);
|
|
946
955
|
n.prev = pointer.id;
|
|
947
956
|
this.bufferForNodeUpdate(n);
|
|
948
957
|
}
|
|
949
|
-
if (pointer.prev) {
|
|
950
|
-
const n = this.getNode(node.id);
|
|
951
|
-
n.next = pointer.id;
|
|
952
|
-
this.bufferForNodeUpdate(n);
|
|
953
|
-
}
|
|
954
|
-
if (isPredecessor) {
|
|
955
|
-
pointer.prev = null;
|
|
956
|
-
}
|
|
957
958
|
}
|
|
958
959
|
pointer.values.push(...node.values);
|
|
959
960
|
if (!pointer.leaf) {
|
|
@@ -977,13 +978,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
977
978
|
node.keys = [pointerPm, ...node.keys];
|
|
978
979
|
node.values = [guess, ...node.values];
|
|
979
980
|
parentNode = this.getNode(node.parent);
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
this.bufferForNodeUpdate(parentNode);
|
|
985
|
-
break;
|
|
986
|
-
}
|
|
981
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
982
|
+
if (nodeIndex > 0) {
|
|
983
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
984
|
+
this.bufferForNodeUpdate(parentNode);
|
|
987
985
|
}
|
|
988
986
|
} else {
|
|
989
987
|
pointerPm = pointer.keys.splice(-1)[0];
|
|
@@ -991,13 +989,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
991
989
|
node.keys = [pointerPm, ...node.keys];
|
|
992
990
|
node.values = [pointerKm, ...node.values];
|
|
993
991
|
parentNode = this.getNode(node.parent);
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
this.bufferForNodeUpdate(parentNode);
|
|
999
|
-
break;
|
|
1000
|
-
}
|
|
992
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
993
|
+
if (nodeIndex > 0) {
|
|
994
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
995
|
+
this.bufferForNodeUpdate(parentNode);
|
|
1001
996
|
}
|
|
1002
997
|
}
|
|
1003
998
|
this.bufferForNodeUpdate(node);
|
|
@@ -1011,13 +1006,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
1011
1006
|
node.keys = [...node.keys, pointerP0];
|
|
1012
1007
|
node.values = [...node.values, guess];
|
|
1013
1008
|
parentNode = this.getNode(node.parent);
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1019
|
-
break;
|
|
1020
|
-
}
|
|
1009
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
1010
|
+
if (pointerIndex > 0) {
|
|
1011
|
+
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
1012
|
+
this.bufferForNodeUpdate(parentNode);
|
|
1021
1013
|
}
|
|
1022
1014
|
} else {
|
|
1023
1015
|
pointerP0 = pointer.keys.splice(0, 1)[0];
|
|
@@ -1025,13 +1017,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
1025
1017
|
node.keys = [...node.keys, pointerP0];
|
|
1026
1018
|
node.values = [...node.values, pointerK0];
|
|
1027
1019
|
parentNode = this.getNode(node.parent);
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1033
|
-
break;
|
|
1034
|
-
}
|
|
1020
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
1021
|
+
if (pointerIndex > 0) {
|
|
1022
|
+
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
1023
|
+
this.bufferForNodeUpdate(parentNode);
|
|
1035
1024
|
}
|
|
1036
1025
|
}
|
|
1037
1026
|
this.bufferForNodeUpdate(node);
|
|
@@ -1059,6 +1048,8 @@ var BPTreeSync = class extends BPTree {
|
|
|
1059
1048
|
}
|
|
1060
1049
|
}
|
|
1061
1050
|
}
|
|
1051
|
+
} else {
|
|
1052
|
+
this.bufferForNodeUpdate(node);
|
|
1062
1053
|
}
|
|
1063
1054
|
}
|
|
1064
1055
|
_insertInParent(node, value, pointer) {
|
|
@@ -1077,31 +1068,25 @@ var BPTreeSync = class extends BPTree {
|
|
|
1077
1068
|
return;
|
|
1078
1069
|
}
|
|
1079
1070
|
const parentNode = this.getNode(node.parent);
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
insertIndex = i + 1;
|
|
1084
|
-
} else {
|
|
1085
|
-
break;
|
|
1086
|
-
}
|
|
1071
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
1072
|
+
if (nodeIndex === -1) {
|
|
1073
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
1087
1074
|
}
|
|
1075
|
+
const insertIndex = nodeIndex;
|
|
1088
1076
|
parentNode.values.splice(insertIndex, 0, value);
|
|
1089
1077
|
parentNode.keys.splice(insertIndex + 1, 0, pointer.id);
|
|
1090
1078
|
pointer.parent = parentNode.id;
|
|
1091
1079
|
if (pointer.leaf) {
|
|
1092
|
-
const
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
this.
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const rightSibling = this.getNode(rightSiblingId);
|
|
1103
|
-
rightSibling.prev = pointer.id;
|
|
1104
|
-
this.bufferForNodeUpdate(rightSibling);
|
|
1080
|
+
const leftSibling = node;
|
|
1081
|
+
const oldNextId = leftSibling.next;
|
|
1082
|
+
pointer.prev = leftSibling.id;
|
|
1083
|
+
pointer.next = oldNextId;
|
|
1084
|
+
leftSibling.next = pointer.id;
|
|
1085
|
+
this.bufferForNodeUpdate(leftSibling);
|
|
1086
|
+
if (oldNextId) {
|
|
1087
|
+
const oldNext = this.getNode(oldNextId);
|
|
1088
|
+
oldNext.prev = pointer.id;
|
|
1089
|
+
this.bufferForNodeUpdate(oldNext);
|
|
1105
1090
|
}
|
|
1106
1091
|
}
|
|
1107
1092
|
this.bufferForNodeUpdate(parentNode);
|
|
@@ -1130,6 +1115,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
1130
1115
|
}
|
|
1131
1116
|
}
|
|
1132
1117
|
init() {
|
|
1118
|
+
this.clear();
|
|
1133
1119
|
const head = this.strategy.readHead();
|
|
1134
1120
|
if (head === null) {
|
|
1135
1121
|
this.order = this.strategy.order;
|
|
@@ -1160,42 +1146,72 @@ var BPTreeSync = class extends BPTree {
|
|
|
1160
1146
|
}
|
|
1161
1147
|
insertableNode(value) {
|
|
1162
1148
|
let node = this.getNode(this.rootId);
|
|
1149
|
+
if (node.parent !== null) {
|
|
1150
|
+
node.parent = null;
|
|
1151
|
+
this.bufferForNodeUpdate(node);
|
|
1152
|
+
}
|
|
1163
1153
|
while (!node.leaf) {
|
|
1154
|
+
const parentId = node.id;
|
|
1164
1155
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1165
1156
|
const nValue = node.values[i];
|
|
1166
1157
|
const k = node.keys;
|
|
1167
1158
|
if (this.comparator.isSame(value, nValue)) {
|
|
1168
1159
|
node = this.getNode(k[i + 1]);
|
|
1160
|
+
if (node.parent !== parentId) {
|
|
1161
|
+
node.parent = parentId;
|
|
1162
|
+
this.bufferForNodeUpdate(node);
|
|
1163
|
+
}
|
|
1169
1164
|
break;
|
|
1170
1165
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
1171
1166
|
node = this.getNode(k[i]);
|
|
1167
|
+
if (node.parent !== parentId) {
|
|
1168
|
+
node.parent = parentId;
|
|
1169
|
+
this.bufferForNodeUpdate(node);
|
|
1170
|
+
}
|
|
1172
1171
|
break;
|
|
1173
1172
|
} else if (i + 1 === node.values.length) {
|
|
1174
1173
|
node = this.getNode(k[i + 1]);
|
|
1174
|
+
if (node.parent !== parentId) {
|
|
1175
|
+
node.parent = parentId;
|
|
1176
|
+
this.bufferForNodeUpdate(node);
|
|
1177
|
+
}
|
|
1175
1178
|
break;
|
|
1176
1179
|
}
|
|
1177
1180
|
}
|
|
1178
1181
|
}
|
|
1179
1182
|
return node;
|
|
1180
1183
|
}
|
|
1181
|
-
/**
|
|
1182
|
-
* Find the insertable node using primaryAsc comparison.
|
|
1183
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
1184
|
-
*/
|
|
1185
1184
|
insertableNodeByPrimary(value) {
|
|
1186
1185
|
let node = this.getNode(this.rootId);
|
|
1186
|
+
if (node.parent !== null) {
|
|
1187
|
+
node.parent = null;
|
|
1188
|
+
this.bufferForNodeUpdate(node);
|
|
1189
|
+
}
|
|
1187
1190
|
while (!node.leaf) {
|
|
1191
|
+
const parentId = node.id;
|
|
1188
1192
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1189
1193
|
const nValue = node.values[i];
|
|
1190
1194
|
const k = node.keys;
|
|
1191
1195
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
1192
1196
|
node = this.getNode(k[i]);
|
|
1197
|
+
if (node.parent !== parentId) {
|
|
1198
|
+
node.parent = parentId;
|
|
1199
|
+
this.bufferForNodeUpdate(node);
|
|
1200
|
+
}
|
|
1193
1201
|
break;
|
|
1194
1202
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1195
1203
|
node = this.getNode(k[i]);
|
|
1204
|
+
if (node.parent !== parentId) {
|
|
1205
|
+
node.parent = parentId;
|
|
1206
|
+
this.bufferForNodeUpdate(node);
|
|
1207
|
+
}
|
|
1196
1208
|
break;
|
|
1197
1209
|
} else if (i + 1 === node.values.length) {
|
|
1198
1210
|
node = this.getNode(k[i + 1]);
|
|
1211
|
+
if (node.parent !== parentId) {
|
|
1212
|
+
node.parent = parentId;
|
|
1213
|
+
this.bufferForNodeUpdate(node);
|
|
1214
|
+
}
|
|
1199
1215
|
break;
|
|
1200
1216
|
}
|
|
1201
1217
|
}
|
|
@@ -1204,16 +1220,29 @@ var BPTreeSync = class extends BPTree {
|
|
|
1204
1220
|
}
|
|
1205
1221
|
insertableRightestNodeByPrimary(value) {
|
|
1206
1222
|
let node = this.getNode(this.rootId);
|
|
1223
|
+
if (node.parent !== null) {
|
|
1224
|
+
node.parent = null;
|
|
1225
|
+
this.bufferForNodeUpdate(node);
|
|
1226
|
+
}
|
|
1207
1227
|
while (!node.leaf) {
|
|
1228
|
+
const parentId = node.id;
|
|
1208
1229
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1209
1230
|
const nValue = node.values[i];
|
|
1210
1231
|
const k = node.keys;
|
|
1211
1232
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1212
1233
|
node = this.getNode(k[i]);
|
|
1234
|
+
if (node.parent !== parentId) {
|
|
1235
|
+
node.parent = parentId;
|
|
1236
|
+
this.bufferForNodeUpdate(node);
|
|
1237
|
+
}
|
|
1213
1238
|
break;
|
|
1214
1239
|
}
|
|
1215
1240
|
if (i + 1 === node.values.length) {
|
|
1216
1241
|
node = this.getNode(k[i + 1]);
|
|
1242
|
+
if (node.parent !== parentId) {
|
|
1243
|
+
node.parent = parentId;
|
|
1244
|
+
this.bufferForNodeUpdate(node);
|
|
1245
|
+
}
|
|
1217
1246
|
break;
|
|
1218
1247
|
}
|
|
1219
1248
|
}
|
|
@@ -1248,20 +1277,65 @@ var BPTreeSync = class extends BPTree {
|
|
|
1248
1277
|
}
|
|
1249
1278
|
leftestNode() {
|
|
1250
1279
|
let node = this.getNode(this.rootId);
|
|
1280
|
+
if (node.parent !== null) {
|
|
1281
|
+
node.parent = null;
|
|
1282
|
+
this.bufferForNodeUpdate(node);
|
|
1283
|
+
}
|
|
1251
1284
|
while (!node.leaf) {
|
|
1285
|
+
const parentId = node.id;
|
|
1252
1286
|
const keys = node.keys;
|
|
1253
1287
|
node = this.getNode(keys[0]);
|
|
1288
|
+
if (node.parent !== parentId) {
|
|
1289
|
+
node.parent = parentId;
|
|
1290
|
+
this.bufferForNodeUpdate(node);
|
|
1291
|
+
}
|
|
1254
1292
|
}
|
|
1255
1293
|
return node;
|
|
1256
1294
|
}
|
|
1257
1295
|
rightestNode() {
|
|
1258
1296
|
let node = this.getNode(this.rootId);
|
|
1297
|
+
if (node.parent !== null) {
|
|
1298
|
+
node.parent = null;
|
|
1299
|
+
this.bufferForNodeUpdate(node);
|
|
1300
|
+
}
|
|
1259
1301
|
while (!node.leaf) {
|
|
1302
|
+
const parentId = node.id;
|
|
1260
1303
|
const keys = node.keys;
|
|
1261
1304
|
node = this.getNode(keys[keys.length - 1]);
|
|
1305
|
+
if (node.parent !== parentId) {
|
|
1306
|
+
node.parent = parentId;
|
|
1307
|
+
this.bufferForNodeUpdate(node);
|
|
1308
|
+
}
|
|
1262
1309
|
}
|
|
1263
1310
|
return node;
|
|
1264
1311
|
}
|
|
1312
|
+
exists(key, value) {
|
|
1313
|
+
const node = this.insertableNode(value);
|
|
1314
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1315
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
1316
|
+
const keys = node.keys[i];
|
|
1317
|
+
if (keys.includes(key)) {
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
forceUpdate(id) {
|
|
1325
|
+
if (id) {
|
|
1326
|
+
this.nodes.delete(id);
|
|
1327
|
+
this.getNode(id);
|
|
1328
|
+
return 1;
|
|
1329
|
+
}
|
|
1330
|
+
const keys = Array.from(this.nodes.keys());
|
|
1331
|
+
for (const key of keys) {
|
|
1332
|
+
this.nodes.delete(key);
|
|
1333
|
+
}
|
|
1334
|
+
for (const key of keys) {
|
|
1335
|
+
this.getNode(key);
|
|
1336
|
+
}
|
|
1337
|
+
return keys.length;
|
|
1338
|
+
}
|
|
1265
1339
|
commitHeadBuffer() {
|
|
1266
1340
|
if (!this._strategyDirty) {
|
|
1267
1341
|
return;
|
|
@@ -1292,24 +1366,14 @@ var BPTreeSync = class extends BPTree {
|
|
|
1292
1366
|
}
|
|
1293
1367
|
this._nodeDeleteBuffer.clear();
|
|
1294
1368
|
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Retrieves the value associated with the given key (PK).
|
|
1297
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
1298
|
-
*
|
|
1299
|
-
* @param key The key to search for.
|
|
1300
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1301
|
-
*/
|
|
1302
1369
|
get(key) {
|
|
1303
1370
|
let node = this.leftestNode();
|
|
1304
1371
|
while (true) {
|
|
1305
|
-
|
|
1306
|
-
const
|
|
1307
|
-
for (let
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
if (keys[j] === key) {
|
|
1311
|
-
return node.values[i];
|
|
1312
|
-
}
|
|
1372
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1373
|
+
const keys = node.keys[i];
|
|
1374
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
1375
|
+
if (keys[j] === key) {
|
|
1376
|
+
return node.values[i];
|
|
1313
1377
|
}
|
|
1314
1378
|
}
|
|
1315
1379
|
}
|
|
@@ -1428,21 +1492,15 @@ var BPTreeSync = class extends BPTree {
|
|
|
1428
1492
|
const nValue = node.values[i];
|
|
1429
1493
|
if (this.comparator.isSame(value, nValue)) {
|
|
1430
1494
|
const keys = node.keys[i];
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
} else if (node.id === this.rootId) {
|
|
1436
|
-
node.values.splice(i, 1);
|
|
1495
|
+
const keyIndex = keys.indexOf(key);
|
|
1496
|
+
if (keyIndex !== -1) {
|
|
1497
|
+
keys.splice(keyIndex, 1);
|
|
1498
|
+
if (keys.length === 0) {
|
|
1437
1499
|
node.keys.splice(i, 1);
|
|
1438
|
-
|
|
1439
|
-
} else {
|
|
1440
|
-
keys.splice(keys.indexOf(key), 1);
|
|
1441
|
-
node.keys.splice(i, 1);
|
|
1442
|
-
node.values.splice(node.values.indexOf(value), 1);
|
|
1443
|
-
this._deleteEntry(node, key, value);
|
|
1444
|
-
this.bufferForNodeUpdate(node);
|
|
1500
|
+
node.values.splice(i, 1);
|
|
1445
1501
|
}
|
|
1502
|
+
this._deleteEntry(node, key, value);
|
|
1503
|
+
break;
|
|
1446
1504
|
}
|
|
1447
1505
|
}
|
|
1448
1506
|
}
|
|
@@ -1451,327 +1509,424 @@ var BPTreeSync = class extends BPTree {
|
|
|
1451
1509
|
this.commitNodeUpdateBuffer();
|
|
1452
1510
|
this.commitNodeDeleteBuffer();
|
|
1453
1511
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1457
|
-
const nValue = node.values[i];
|
|
1458
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
1459
|
-
const keys = node.keys[i];
|
|
1460
|
-
return keys.includes(key);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
return false;
|
|
1512
|
+
getHeadData() {
|
|
1513
|
+
return this.strategy.head.data;
|
|
1464
1514
|
}
|
|
1465
1515
|
setHeadData(data) {
|
|
1466
1516
|
this.strategy.head.data = data;
|
|
1467
|
-
this.
|
|
1468
|
-
this.commitHeadBuffer();
|
|
1517
|
+
this.strategy.writeHead(this.strategy.head);
|
|
1469
1518
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1521
|
+
// src/base/SerializeStrategy.ts
|
|
1522
|
+
var SerializeStrategy = class {
|
|
1523
|
+
order;
|
|
1524
|
+
head;
|
|
1525
|
+
constructor(order) {
|
|
1526
|
+
this.order = order;
|
|
1527
|
+
this.head = {
|
|
1528
|
+
order,
|
|
1529
|
+
root: null,
|
|
1530
|
+
data: {}
|
|
1531
|
+
};
|
|
1478
1532
|
}
|
|
1479
1533
|
};
|
|
1480
1534
|
|
|
1481
|
-
//
|
|
1482
|
-
var
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
writeQueue;
|
|
1487
|
-
static async CatchError(promise) {
|
|
1488
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
1489
|
-
}
|
|
1490
|
-
static IsRangeOverlap(a, b) {
|
|
1491
|
-
const [start1, end1] = a;
|
|
1492
|
-
const [start2, end2] = b;
|
|
1493
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
1494
|
-
return false;
|
|
1535
|
+
// src/SerializeStrategySync.ts
|
|
1536
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
1537
|
+
getHeadData(key, defaultValue) {
|
|
1538
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
1539
|
+
this.setHeadData(key, defaultValue);
|
|
1495
1540
|
}
|
|
1496
|
-
return
|
|
1541
|
+
return this.head.data[key];
|
|
1497
1542
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1543
|
+
setHeadData(key, data) {
|
|
1544
|
+
this.head.data[key] = data;
|
|
1545
|
+
this.writeHead(this.head);
|
|
1500
1546
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1547
|
+
autoIncrement(key, defaultValue) {
|
|
1548
|
+
const current = this.getHeadData(key, defaultValue);
|
|
1549
|
+
const next = current + 1;
|
|
1550
|
+
this.setHeadData(key, next);
|
|
1551
|
+
return current;
|
|
1503
1552
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1553
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1554
|
+
if (this.head.root !== oldRoot) {
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
this.head.root = newRoot;
|
|
1558
|
+
this.writeHead(this.head);
|
|
1559
|
+
return true;
|
|
1506
1560
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
constructor() {
|
|
1511
|
-
|
|
1512
|
-
this.
|
|
1513
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
1514
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
1561
|
+
};
|
|
1562
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
1563
|
+
node;
|
|
1564
|
+
constructor(order) {
|
|
1565
|
+
super(order);
|
|
1566
|
+
this.node = {};
|
|
1515
1567
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
* @param start - The starting value of the range.
|
|
1519
|
-
* @param length - The length of the range.
|
|
1520
|
-
* @returns A range tuple [start, start + length].
|
|
1521
|
-
*/
|
|
1522
|
-
range(start, length) {
|
|
1523
|
-
return [start, start + length];
|
|
1568
|
+
id(isLeaf) {
|
|
1569
|
+
return this.autoIncrement("index", 1).toString();
|
|
1524
1570
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1571
|
+
read(id) {
|
|
1572
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
1573
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
1574
|
+
}
|
|
1575
|
+
const node = this.node[id];
|
|
1576
|
+
return JSON.parse(JSON.stringify(node));
|
|
1527
1577
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const [b1, b2] = b;
|
|
1531
|
-
return a1 === b1 && a2 === b2;
|
|
1578
|
+
write(id, node) {
|
|
1579
|
+
this.node[id] = node;
|
|
1532
1580
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
if (!unit.condition()) {
|
|
1536
|
-
continue;
|
|
1537
|
-
}
|
|
1538
|
-
this._alloc(queue, workspaces, id);
|
|
1539
|
-
}
|
|
1581
|
+
delete(id) {
|
|
1582
|
+
delete this.node[id];
|
|
1540
1583
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
return handlers[key](...args);
|
|
1545
|
-
}
|
|
1584
|
+
readHead() {
|
|
1585
|
+
if (this.head.root === null) {
|
|
1586
|
+
return null;
|
|
1546
1587
|
}
|
|
1547
|
-
|
|
1588
|
+
return this.head;
|
|
1548
1589
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
const expectedType = pattern[index];
|
|
1552
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
1553
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
1554
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
1555
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
1556
|
-
return false;
|
|
1557
|
-
});
|
|
1590
|
+
writeHead(head) {
|
|
1591
|
+
this.head = head;
|
|
1558
1592
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
const unit = workspaces.get(lockId);
|
|
1575
|
-
if (!unit) {
|
|
1576
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1577
|
-
}
|
|
1578
|
-
workspaces.delete(lockId);
|
|
1579
|
-
unit.free();
|
|
1580
|
-
}
|
|
1581
|
-
_lock(queue, range, timeout, task, condition) {
|
|
1582
|
-
return new Promise((resolve, reject) => {
|
|
1583
|
-
let timeoutId = null;
|
|
1584
|
-
if (timeout >= 0) {
|
|
1585
|
-
timeoutId = setTimeout(() => {
|
|
1586
|
-
reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
|
|
1587
|
-
}, timeout);
|
|
1588
|
-
}
|
|
1589
|
-
const id = this._createRandomId();
|
|
1590
|
-
const alloc = async () => {
|
|
1591
|
-
if (timeoutId !== null) {
|
|
1592
|
-
clearTimeout(timeoutId);
|
|
1593
|
-
}
|
|
1594
|
-
const [err, v] = await _Ryoiki.CatchError(task(id));
|
|
1595
|
-
if (err) reject(err);
|
|
1596
|
-
else resolve(v);
|
|
1597
|
-
};
|
|
1598
|
-
const fetch = () => {
|
|
1599
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
1600
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
1601
|
-
};
|
|
1602
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
1603
|
-
fetch();
|
|
1604
|
-
});
|
|
1593
|
+
};
|
|
1594
|
+
|
|
1595
|
+
// src/transaction/BPTreeSyncSnapshotStrategy.ts
|
|
1596
|
+
var BPTreeSyncSnapshotStrategy = class extends SerializeStrategySync {
|
|
1597
|
+
baseStrategy;
|
|
1598
|
+
snapshotHead;
|
|
1599
|
+
constructor(baseStrategy, root) {
|
|
1600
|
+
super(baseStrategy.order);
|
|
1601
|
+
this.baseStrategy = baseStrategy;
|
|
1602
|
+
this.snapshotHead = {
|
|
1603
|
+
...baseStrategy.head,
|
|
1604
|
+
root,
|
|
1605
|
+
data: { ...baseStrategy.head.data }
|
|
1606
|
+
};
|
|
1607
|
+
this.head = this.snapshotHead;
|
|
1605
1608
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
for (const lock of workspaces.values()) {
|
|
1609
|
-
if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
|
|
1610
|
-
isLocked = true;
|
|
1611
|
-
break;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return isLocked;
|
|
1609
|
+
id(isLeaf) {
|
|
1610
|
+
return this.baseStrategy.id(isLeaf);
|
|
1615
1611
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
* @param range The range to check for active read locks.
|
|
1619
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
1620
|
-
*/
|
|
1621
|
-
isReading(range) {
|
|
1622
|
-
return this._checkWorking(range, this.readings);
|
|
1612
|
+
read(id) {
|
|
1613
|
+
return this.baseStrategy.read(id);
|
|
1623
1614
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
* @param range The range to check for active write locks.
|
|
1627
|
-
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
1628
|
-
*/
|
|
1629
|
-
isWriting(range) {
|
|
1630
|
-
return this._checkWorking(range, this.writings);
|
|
1615
|
+
write(id, node) {
|
|
1616
|
+
this.baseStrategy.write(id, node);
|
|
1631
1617
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
* @param range The range to check for read lock availability.
|
|
1635
|
-
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
1636
|
-
*/
|
|
1637
|
-
canRead(range) {
|
|
1638
|
-
const writing = this.isWriting(range);
|
|
1639
|
-
return !writing;
|
|
1618
|
+
delete(id) {
|
|
1619
|
+
this.baseStrategy.delete(id);
|
|
1640
1620
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
* @param range The range to check for write lock availability.
|
|
1644
|
-
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
1645
|
-
*/
|
|
1646
|
-
canWrite(range) {
|
|
1647
|
-
const reading = this.isReading(range);
|
|
1648
|
-
const writing = this.isWriting(range);
|
|
1649
|
-
return !reading && !writing;
|
|
1621
|
+
readHead() {
|
|
1622
|
+
return this.snapshotHead;
|
|
1650
1623
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
* @param arg0 - Either a range or a task callback.
|
|
1655
|
-
* If a range is provided, the task is the second argument.
|
|
1656
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1657
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1658
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1659
|
-
* If this value is not provided, no timeout will be set.
|
|
1660
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1661
|
-
*/
|
|
1662
|
-
readLock(arg0, arg1, arg2) {
|
|
1663
|
-
const [range, task, timeout] = this._handleOverload(
|
|
1664
|
-
[arg0, arg1, arg2],
|
|
1665
|
-
{
|
|
1666
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
1667
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
1668
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
1669
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
1670
|
-
},
|
|
1671
|
-
{
|
|
1672
|
-
task: [Function],
|
|
1673
|
-
taskTimeout: [Function, Number],
|
|
1674
|
-
rangeTask: [Array, Function],
|
|
1675
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1676
|
-
}
|
|
1677
|
-
);
|
|
1678
|
-
return this._lock(
|
|
1679
|
-
this.readQueue,
|
|
1680
|
-
range,
|
|
1681
|
-
timeout,
|
|
1682
|
-
task,
|
|
1683
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
1684
|
-
);
|
|
1624
|
+
writeHead(head) {
|
|
1625
|
+
this.snapshotHead.root = head.root;
|
|
1626
|
+
this.snapshotHead.data = { ...head.data };
|
|
1685
1627
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
* @template T - The return type of the task.
|
|
1689
|
-
* @param arg0 - Either a range or a task callback.
|
|
1690
|
-
* If a range is provided, the task is the second argument.
|
|
1691
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1692
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1693
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1694
|
-
* If this value is not provided, no timeout will be set.
|
|
1695
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1696
|
-
*/
|
|
1697
|
-
writeLock(arg0, arg1, arg2) {
|
|
1698
|
-
const [range, task, timeout] = this._handleOverload(
|
|
1699
|
-
[arg0, arg1, arg2],
|
|
1700
|
-
{
|
|
1701
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
1702
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
1703
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
1704
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
1705
|
-
},
|
|
1706
|
-
{
|
|
1707
|
-
task: [Function],
|
|
1708
|
-
taskTimeout: [Function, Number],
|
|
1709
|
-
rangeTask: [Array, Function],
|
|
1710
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1711
|
-
}
|
|
1712
|
-
);
|
|
1713
|
-
return this._lock(
|
|
1714
|
-
this.writeQueue,
|
|
1715
|
-
range,
|
|
1716
|
-
timeout,
|
|
1717
|
-
task,
|
|
1718
|
-
() => {
|
|
1719
|
-
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
1720
|
-
}
|
|
1721
|
-
);
|
|
1628
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1629
|
+
return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
1722
1630
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
* @param lockId - The unique identifier for the lock to release.
|
|
1726
|
-
*/
|
|
1727
|
-
readUnlock(lockId) {
|
|
1728
|
-
this._free(this.readings, lockId);
|
|
1631
|
+
getHeadData(key, defaultValue) {
|
|
1632
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1729
1633
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
this._free(this.writings, lockId);
|
|
1634
|
+
setHeadData(key, data) {
|
|
1635
|
+
this.snapshotHead.data[key] = data;
|
|
1636
|
+
}
|
|
1637
|
+
autoIncrement(key, defaultValue) {
|
|
1638
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1736
1639
|
}
|
|
1737
1640
|
};
|
|
1738
1641
|
|
|
1739
|
-
// src/
|
|
1740
|
-
var
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1642
|
+
// src/transaction/BPTreeSyncTransaction.ts
|
|
1643
|
+
var BPTreeSyncTransaction = class extends BPTreeSyncBase {
|
|
1644
|
+
realBaseTree;
|
|
1645
|
+
realBaseStrategy;
|
|
1646
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
1647
|
+
dirtyIds;
|
|
1648
|
+
createdInTx;
|
|
1649
|
+
deletedIds;
|
|
1650
|
+
initialRootId;
|
|
1651
|
+
transactionRootId;
|
|
1652
|
+
constructor(baseTree) {
|
|
1653
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
1654
|
+
this.realBaseTree = baseTree;
|
|
1655
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
1656
|
+
this.order = baseTree.getOrder();
|
|
1657
|
+
this.initialRootId = "";
|
|
1658
|
+
this.transactionRootId = "";
|
|
1659
|
+
this.dirtyIds = /* @__PURE__ */ new Set();
|
|
1660
|
+
this.createdInTx = /* @__PURE__ */ new Set();
|
|
1661
|
+
this.deletedIds = /* @__PURE__ */ new Set();
|
|
1746
1662
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1663
|
+
/**
|
|
1664
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
1665
|
+
*/
|
|
1666
|
+
initTransaction() {
|
|
1667
|
+
const head = this.realBaseStrategy.readHead();
|
|
1668
|
+
if (head) {
|
|
1669
|
+
this.order = head.order;
|
|
1670
|
+
this.initialRootId = head.root;
|
|
1671
|
+
} else {
|
|
1672
|
+
this.initialRootId = this.realBaseTree.getRootId();
|
|
1673
|
+
}
|
|
1674
|
+
if (!this.initialRootId) {
|
|
1675
|
+
const root = this._createNode(true, [], [], true);
|
|
1676
|
+
this.initialRootId = root.id;
|
|
1677
|
+
}
|
|
1678
|
+
this.transactionRootId = this.initialRootId;
|
|
1679
|
+
this.rootId = this.transactionRootId;
|
|
1680
|
+
const snapshotStrategy = new BPTreeSyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
1681
|
+
this.strategy = snapshotStrategy;
|
|
1682
|
+
this.txNodes.clear();
|
|
1683
|
+
this.dirtyIds.clear();
|
|
1684
|
+
this.createdInTx.clear();
|
|
1685
|
+
this.deletedIds.clear();
|
|
1753
1686
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1687
|
+
getNode(id) {
|
|
1688
|
+
if (this.txNodes.has(id)) {
|
|
1689
|
+
return this.txNodes.get(id);
|
|
1690
|
+
}
|
|
1691
|
+
if (this.deletedIds.has(id)) {
|
|
1692
|
+
throw new Error(`The tree attempted to reference deleted node '${id}'`);
|
|
1693
|
+
}
|
|
1694
|
+
const baseNode = this.realBaseStrategy.read(id);
|
|
1695
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
1696
|
+
this.txNodes.set(id, clone);
|
|
1697
|
+
return clone;
|
|
1762
1698
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1699
|
+
bufferForNodeUpdate(node) {
|
|
1700
|
+
if (this.dirtyIds.has(node.id) && this.txNodes.has(node.id) && node._p) {
|
|
1701
|
+
this.txNodes.set(node.id, node);
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
node._p = true;
|
|
1705
|
+
this.txNodes.set(node.id, node);
|
|
1706
|
+
this.dirtyIds.add(node.id);
|
|
1707
|
+
if (node.leaf) {
|
|
1708
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
1709
|
+
try {
|
|
1710
|
+
this.bufferForNodeUpdate(this.getNode(node.next));
|
|
1711
|
+
} catch (e) {
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
1715
|
+
try {
|
|
1716
|
+
this.bufferForNodeUpdate(this.getNode(node.prev));
|
|
1717
|
+
} catch (e) {
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
this.markPathDirty(node);
|
|
1722
|
+
delete node._p;
|
|
1771
1723
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1724
|
+
bufferForNodeCreate(node) {
|
|
1725
|
+
this.txNodes.set(node.id, node);
|
|
1726
|
+
this.dirtyIds.add(node.id);
|
|
1727
|
+
this.createdInTx.add(node.id);
|
|
1728
|
+
if (node.leaf) {
|
|
1729
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
1730
|
+
try {
|
|
1731
|
+
this.bufferForNodeUpdate(this.getNode(node.next));
|
|
1732
|
+
} catch (e) {
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
1736
|
+
try {
|
|
1737
|
+
this.bufferForNodeUpdate(this.getNode(node.prev));
|
|
1738
|
+
} catch (e) {
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
this.markPathDirty(node);
|
|
1743
|
+
}
|
|
1744
|
+
bufferForNodeDelete(node) {
|
|
1745
|
+
this.txNodes.delete(node.id);
|
|
1746
|
+
this.dirtyIds.add(node.id);
|
|
1747
|
+
this.deletedIds.add(node.id);
|
|
1748
|
+
}
|
|
1749
|
+
markPathDirty(node) {
|
|
1750
|
+
let curr = node;
|
|
1751
|
+
while (curr.parent) {
|
|
1752
|
+
if (this.deletedIds.has(curr.parent)) {
|
|
1753
|
+
break;
|
|
1754
|
+
}
|
|
1755
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
1756
|
+
break;
|
|
1757
|
+
}
|
|
1758
|
+
const parent = this.getNode(curr.parent);
|
|
1759
|
+
this.dirtyIds.add(parent.id);
|
|
1760
|
+
curr = parent;
|
|
1761
|
+
}
|
|
1762
|
+
if (!curr.parent) {
|
|
1763
|
+
this.transactionRootId = curr.id;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
_createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
1767
|
+
const id = this.strategy.id(isLeaf);
|
|
1768
|
+
const node = {
|
|
1769
|
+
id,
|
|
1770
|
+
keys,
|
|
1771
|
+
values,
|
|
1772
|
+
leaf,
|
|
1773
|
+
parent,
|
|
1774
|
+
next,
|
|
1775
|
+
prev
|
|
1776
|
+
};
|
|
1777
|
+
this.bufferForNodeCreate(node);
|
|
1778
|
+
return node;
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Attempts to commit the transaction.
|
|
1782
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
1783
|
+
*
|
|
1784
|
+
* @returns The transaction result.
|
|
1785
|
+
*/
|
|
1786
|
+
commit() {
|
|
1787
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
1788
|
+
const finalNodes = [];
|
|
1789
|
+
for (const oldId of this.dirtyIds) {
|
|
1790
|
+
if (this.createdInTx.has(oldId)) {
|
|
1791
|
+
idMapping.set(oldId, oldId);
|
|
1792
|
+
} else {
|
|
1793
|
+
const node = this.txNodes.get(oldId);
|
|
1794
|
+
if (node) {
|
|
1795
|
+
const newId = this.realBaseStrategy.id(node.leaf);
|
|
1796
|
+
idMapping.set(oldId, newId);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
const newCreatedIds = [];
|
|
1801
|
+
for (const oldId of this.dirtyIds) {
|
|
1802
|
+
const node = this.txNodes.get(oldId);
|
|
1803
|
+
if (!node) continue;
|
|
1804
|
+
const newId = idMapping.get(oldId);
|
|
1805
|
+
node.id = newId;
|
|
1806
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
1807
|
+
node.parent = idMapping.get(node.parent);
|
|
1808
|
+
}
|
|
1809
|
+
if (!node.leaf) {
|
|
1810
|
+
const internal = node;
|
|
1811
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
1812
|
+
const childId = internal.keys[i];
|
|
1813
|
+
if (idMapping.has(childId)) {
|
|
1814
|
+
internal.keys[i] = idMapping.get(childId);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
if (node.leaf) {
|
|
1819
|
+
const leaf = node;
|
|
1820
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
1821
|
+
leaf.next = idMapping.get(leaf.next);
|
|
1822
|
+
}
|
|
1823
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
1824
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
finalNodes.push(node);
|
|
1828
|
+
newCreatedIds.push(newId);
|
|
1829
|
+
}
|
|
1830
|
+
let newRootId = this.rootId;
|
|
1831
|
+
if (idMapping.has(this.rootId)) {
|
|
1832
|
+
newRootId = idMapping.get(this.rootId);
|
|
1833
|
+
}
|
|
1834
|
+
for (const node of finalNodes) {
|
|
1835
|
+
this.realBaseStrategy.write(node.id, node);
|
|
1836
|
+
}
|
|
1837
|
+
const success = this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
1838
|
+
if (success) {
|
|
1839
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
1840
|
+
for (const oldId of this.dirtyIds) {
|
|
1841
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
1842
|
+
distinctObsolete.add(oldId);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return {
|
|
1846
|
+
success: true,
|
|
1847
|
+
createdIds: newCreatedIds,
|
|
1848
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
1849
|
+
};
|
|
1850
|
+
} else {
|
|
1851
|
+
this.rollback();
|
|
1852
|
+
return {
|
|
1853
|
+
success: false,
|
|
1854
|
+
createdIds: newCreatedIds,
|
|
1855
|
+
obsoleteIds: []
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
1861
|
+
* Internal use only.
|
|
1862
|
+
*/
|
|
1863
|
+
rollback() {
|
|
1864
|
+
this.txNodes.clear();
|
|
1865
|
+
this.dirtyIds.clear();
|
|
1866
|
+
this.createdInTx.clear();
|
|
1867
|
+
}
|
|
1868
|
+
// Override to do nothing, as transaction handles its own commits
|
|
1869
|
+
commitHeadBuffer() {
|
|
1870
|
+
}
|
|
1871
|
+
commitNodeCreateBuffer() {
|
|
1872
|
+
}
|
|
1873
|
+
commitNodeUpdateBuffer() {
|
|
1874
|
+
}
|
|
1875
|
+
commitNodeDeleteBuffer() {
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
|
|
1879
|
+
// src/BPTreeSync.ts
|
|
1880
|
+
var BPTreeSync = class extends BPTreeSyncBase {
|
|
1881
|
+
constructor(strategy, comparator, option) {
|
|
1882
|
+
super(strategy, comparator, option);
|
|
1883
|
+
this.init();
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Creates a new synchronous transaction.
|
|
1887
|
+
* @returns A new BPTreeSyncTransaction.
|
|
1888
|
+
*/
|
|
1889
|
+
createTransaction() {
|
|
1890
|
+
const tx = new BPTreeSyncTransaction(this);
|
|
1891
|
+
tx.initTransaction();
|
|
1892
|
+
return tx;
|
|
1893
|
+
}
|
|
1894
|
+
insert(key, value) {
|
|
1895
|
+
const tx = this.createTransaction();
|
|
1896
|
+
tx.insert(key, value);
|
|
1897
|
+
const { success } = tx.commit();
|
|
1898
|
+
this.init();
|
|
1899
|
+
if (!success) {
|
|
1900
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
delete(key, value) {
|
|
1904
|
+
const tx = this.createTransaction();
|
|
1905
|
+
tx.delete(key, value);
|
|
1906
|
+
const { success } = tx.commit();
|
|
1907
|
+
this.init();
|
|
1908
|
+
if (!success) {
|
|
1909
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
// src/base/BPTreeAsyncBase.ts
|
|
1915
|
+
var BPTreeAsyncBase = class extends BPTree {
|
|
1916
|
+
constructor(strategy, comparator, option) {
|
|
1917
|
+
super(strategy, comparator, option);
|
|
1918
|
+
this.nodes = this._createCachedNode();
|
|
1919
|
+
}
|
|
1920
|
+
_createCachedNode() {
|
|
1921
|
+
return new CacheEntanglementAsync(async (key) => {
|
|
1922
|
+
return await this.strategy.read(key);
|
|
1923
|
+
}, {
|
|
1924
|
+
capacity: this.option.capacity ?? 1e3
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
1928
|
+
let node = startNode;
|
|
1929
|
+
let done = false;
|
|
1775
1930
|
let hasMatched = false;
|
|
1776
1931
|
while (!done) {
|
|
1777
1932
|
if (endNode && node.id === endNode.id) {
|
|
@@ -1833,7 +1988,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1833
1988
|
}
|
|
1834
1989
|
return id;
|
|
1835
1990
|
}
|
|
1836
|
-
async _createNode(isLeaf, keys, values, leaf =
|
|
1991
|
+
async _createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
1837
1992
|
const id = await this._createNodeId(isLeaf);
|
|
1838
1993
|
const node = {
|
|
1839
1994
|
id,
|
|
@@ -1844,40 +1999,37 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1844
1999
|
next,
|
|
1845
2000
|
prev
|
|
1846
2001
|
};
|
|
1847
|
-
this.
|
|
2002
|
+
await this.bufferForNodeCreate(node);
|
|
1848
2003
|
return node;
|
|
1849
2004
|
}
|
|
1850
2005
|
async _deleteEntry(node, key, value) {
|
|
1851
2006
|
if (!node.leaf) {
|
|
2007
|
+
let keyIndex = -1;
|
|
1852
2008
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
node.keys.splice(i, 1);
|
|
1856
|
-
this.bufferForNodeUpdate(node);
|
|
2009
|
+
if (node.keys[i] === key) {
|
|
2010
|
+
keyIndex = i;
|
|
1857
2011
|
break;
|
|
1858
2012
|
}
|
|
1859
2013
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
break;
|
|
1866
|
-
}
|
|
2014
|
+
if (keyIndex !== -1) {
|
|
2015
|
+
node.keys.splice(keyIndex, 1);
|
|
2016
|
+
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
2017
|
+
node.values.splice(valueIndex, 1);
|
|
2018
|
+
await this.bufferForNodeUpdate(node);
|
|
1867
2019
|
}
|
|
1868
2020
|
}
|
|
1869
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
2021
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
1870
2022
|
const keys = node.keys;
|
|
1871
|
-
this.bufferForNodeDelete(node);
|
|
2023
|
+
await this.bufferForNodeDelete(node);
|
|
1872
2024
|
const newRoot = await this.getNode(keys[0]);
|
|
1873
2025
|
this.rootId = newRoot.id;
|
|
1874
2026
|
newRoot.parent = null;
|
|
1875
2027
|
this.strategy.head.root = this.rootId;
|
|
1876
|
-
this.bufferForNodeUpdate(newRoot);
|
|
2028
|
+
await this.bufferForNodeUpdate(newRoot);
|
|
1877
2029
|
return;
|
|
1878
2030
|
} else if (this.rootId === node.id) {
|
|
1879
2031
|
const root = await this.getNode(this.rootId);
|
|
1880
|
-
this.bufferForNodeUpdate(root);
|
|
2032
|
+
await this.bufferForNodeUpdate(root);
|
|
1881
2033
|
return;
|
|
1882
2034
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
1883
2035
|
if (node.parent === null) {
|
|
@@ -1935,33 +2087,24 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1935
2087
|
pointer.values.push(guess);
|
|
1936
2088
|
} else {
|
|
1937
2089
|
pointer.next = node.next;
|
|
1938
|
-
pointer.prev = node.id;
|
|
1939
2090
|
if (pointer.next) {
|
|
1940
|
-
const n = await this.getNode(
|
|
2091
|
+
const n = await this.getNode(pointer.next);
|
|
1941
2092
|
n.prev = pointer.id;
|
|
1942
|
-
this.bufferForNodeUpdate(n);
|
|
1943
|
-
}
|
|
1944
|
-
if (pointer.prev) {
|
|
1945
|
-
const n = await this.getNode(node.id);
|
|
1946
|
-
n.next = pointer.id;
|
|
1947
|
-
this.bufferForNodeUpdate(n);
|
|
1948
|
-
}
|
|
1949
|
-
if (isPredecessor) {
|
|
1950
|
-
pointer.prev = null;
|
|
2093
|
+
await this.bufferForNodeUpdate(n);
|
|
1951
2094
|
}
|
|
1952
2095
|
}
|
|
1953
2096
|
pointer.values.push(...node.values);
|
|
1954
2097
|
if (!pointer.leaf) {
|
|
1955
2098
|
const keys = pointer.keys;
|
|
1956
2099
|
for (const key2 of keys) {
|
|
1957
|
-
const
|
|
1958
|
-
|
|
1959
|
-
this.bufferForNodeUpdate(
|
|
2100
|
+
const n = await this.getNode(key2);
|
|
2101
|
+
n.parent = pointer.id;
|
|
2102
|
+
await this.bufferForNodeUpdate(n);
|
|
1960
2103
|
}
|
|
1961
2104
|
}
|
|
1962
2105
|
await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
|
|
1963
|
-
this.bufferForNodeUpdate(pointer);
|
|
1964
|
-
this.bufferForNodeDelete(node);
|
|
2106
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2107
|
+
await this.bufferForNodeDelete(node);
|
|
1965
2108
|
} else {
|
|
1966
2109
|
if (isPredecessor) {
|
|
1967
2110
|
let pointerPm;
|
|
@@ -1972,13 +2115,10 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1972
2115
|
node.keys = [pointerPm, ...node.keys];
|
|
1973
2116
|
node.values = [guess, ...node.values];
|
|
1974
2117
|
parentNode = await this.getNode(node.parent);
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1980
|
-
break;
|
|
1981
|
-
}
|
|
2118
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2119
|
+
if (nodeIndex > 0) {
|
|
2120
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
2121
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1982
2122
|
}
|
|
1983
2123
|
} else {
|
|
1984
2124
|
pointerPm = pointer.keys.splice(-1)[0];
|
|
@@ -1986,17 +2126,14 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1986
2126
|
node.keys = [pointerPm, ...node.keys];
|
|
1987
2127
|
node.values = [pointerKm, ...node.values];
|
|
1988
2128
|
parentNode = await this.getNode(node.parent);
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1994
|
-
break;
|
|
1995
|
-
}
|
|
2129
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2130
|
+
if (nodeIndex > 0) {
|
|
2131
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
2132
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1996
2133
|
}
|
|
1997
2134
|
}
|
|
1998
|
-
this.bufferForNodeUpdate(node);
|
|
1999
|
-
this.bufferForNodeUpdate(pointer);
|
|
2135
|
+
await this.bufferForNodeUpdate(node);
|
|
2136
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2000
2137
|
} else {
|
|
2001
2138
|
let pointerP0;
|
|
2002
2139
|
let pointerK0;
|
|
@@ -2006,13 +2143,10 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2006
2143
|
node.keys = [...node.keys, pointerP0];
|
|
2007
2144
|
node.values = [...node.values, guess];
|
|
2008
2145
|
parentNode = await this.getNode(node.parent);
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2014
|
-
break;
|
|
2015
|
-
}
|
|
2146
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2147
|
+
if (pointerIndex > 0) {
|
|
2148
|
+
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
2149
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2016
2150
|
}
|
|
2017
2151
|
} else {
|
|
2018
2152
|
pointerP0 = pointer.keys.splice(0, 1)[0];
|
|
@@ -2020,40 +2154,42 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2020
2154
|
node.keys = [...node.keys, pointerP0];
|
|
2021
2155
|
node.values = [...node.values, pointerK0];
|
|
2022
2156
|
parentNode = await this.getNode(node.parent);
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2028
|
-
break;
|
|
2029
|
-
}
|
|
2157
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2158
|
+
if (pointerIndex > 0) {
|
|
2159
|
+
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
2160
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2030
2161
|
}
|
|
2031
2162
|
}
|
|
2032
|
-
this.bufferForNodeUpdate(node);
|
|
2033
|
-
this.bufferForNodeUpdate(pointer);
|
|
2163
|
+
await this.bufferForNodeUpdate(node);
|
|
2164
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2034
2165
|
}
|
|
2035
2166
|
if (!pointer.leaf) {
|
|
2036
|
-
|
|
2167
|
+
const keys = pointer.keys;
|
|
2168
|
+
for (const key2 of keys) {
|
|
2037
2169
|
const n = await this.getNode(key2);
|
|
2038
2170
|
n.parent = pointer.id;
|
|
2039
|
-
this.bufferForNodeUpdate(n);
|
|
2171
|
+
await this.bufferForNodeUpdate(n);
|
|
2040
2172
|
}
|
|
2041
2173
|
}
|
|
2042
2174
|
if (!node.leaf) {
|
|
2043
|
-
|
|
2175
|
+
const keys = node.keys;
|
|
2176
|
+
for (const key2 of keys) {
|
|
2044
2177
|
const n = await this.getNode(key2);
|
|
2045
2178
|
n.parent = node.id;
|
|
2046
|
-
this.bufferForNodeUpdate(n);
|
|
2179
|
+
await this.bufferForNodeUpdate(n);
|
|
2047
2180
|
}
|
|
2048
2181
|
}
|
|
2049
2182
|
if (!parentNode.leaf) {
|
|
2050
|
-
|
|
2183
|
+
const keys = parentNode.keys;
|
|
2184
|
+
for (const key2 of keys) {
|
|
2051
2185
|
const n = await this.getNode(key2);
|
|
2052
2186
|
n.parent = parentNode.id;
|
|
2053
|
-
this.bufferForNodeUpdate(n);
|
|
2187
|
+
await this.bufferForNodeUpdate(n);
|
|
2054
2188
|
}
|
|
2055
2189
|
}
|
|
2056
2190
|
}
|
|
2191
|
+
} else {
|
|
2192
|
+
await this.bufferForNodeUpdate(node);
|
|
2057
2193
|
}
|
|
2058
2194
|
}
|
|
2059
2195
|
async _insertInParent(node, value, pointer) {
|
|
@@ -2064,43 +2200,39 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2064
2200
|
node.parent = root.id;
|
|
2065
2201
|
pointer.parent = root.id;
|
|
2066
2202
|
if (pointer.leaf) {
|
|
2067
|
-
|
|
2068
|
-
|
|
2203
|
+
const nNode = node;
|
|
2204
|
+
nNode.next = pointer.id;
|
|
2205
|
+
const nPointer = pointer;
|
|
2206
|
+
nPointer.prev = node.id;
|
|
2069
2207
|
}
|
|
2070
|
-
this.bufferForNodeUpdate(node);
|
|
2071
|
-
this.bufferForNodeUpdate(pointer);
|
|
2208
|
+
await this.bufferForNodeUpdate(node);
|
|
2209
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2072
2210
|
return;
|
|
2073
2211
|
}
|
|
2074
2212
|
const parentNode = await this.getNode(node.parent);
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
insertIndex = i + 1;
|
|
2079
|
-
} else {
|
|
2080
|
-
break;
|
|
2081
|
-
}
|
|
2213
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2214
|
+
if (nodeIndex === -1) {
|
|
2215
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2082
2216
|
}
|
|
2217
|
+
const insertIndex = nodeIndex;
|
|
2083
2218
|
parentNode.values.splice(insertIndex, 0, value);
|
|
2084
2219
|
parentNode.keys.splice(insertIndex + 1, 0, pointer.id);
|
|
2085
2220
|
pointer.parent = parentNode.id;
|
|
2086
2221
|
if (pointer.leaf) {
|
|
2087
|
-
const
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
this.
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
}
|
|
2102
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2103
|
-
this.bufferForNodeUpdate(pointer);
|
|
2222
|
+
const leftSibling = node;
|
|
2223
|
+
const oldNextId = leftSibling.next;
|
|
2224
|
+
pointer.prev = leftSibling.id;
|
|
2225
|
+
pointer.next = oldNextId;
|
|
2226
|
+
leftSibling.next = pointer.id;
|
|
2227
|
+
await this.bufferForNodeUpdate(leftSibling);
|
|
2228
|
+
if (oldNextId) {
|
|
2229
|
+
const oldNext = await this.getNode(oldNextId);
|
|
2230
|
+
oldNext.prev = pointer.id;
|
|
2231
|
+
await this.bufferForNodeUpdate(oldNext);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2235
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2104
2236
|
if (parentNode.keys.length > this.order) {
|
|
2105
2237
|
const parentPointer = await this._createNode(false, [], []);
|
|
2106
2238
|
parentPointer.parent = parentNode.parent;
|
|
@@ -2111,20 +2243,21 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2111
2243
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2112
2244
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2113
2245
|
for (const k of parentNode.keys) {
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
this.bufferForNodeUpdate(
|
|
2246
|
+
const n = await this.getNode(k);
|
|
2247
|
+
n.parent = parentNode.id;
|
|
2248
|
+
await this.bufferForNodeUpdate(n);
|
|
2117
2249
|
}
|
|
2118
2250
|
for (const k of parentPointer.keys) {
|
|
2119
|
-
const
|
|
2120
|
-
|
|
2121
|
-
this.bufferForNodeUpdate(
|
|
2251
|
+
const n = await this.getNode(k);
|
|
2252
|
+
n.parent = parentPointer.id;
|
|
2253
|
+
await this.bufferForNodeUpdate(n);
|
|
2122
2254
|
}
|
|
2123
2255
|
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2124
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2256
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2125
2257
|
}
|
|
2126
2258
|
}
|
|
2127
2259
|
async init() {
|
|
2260
|
+
this.clear();
|
|
2128
2261
|
const head = await this.strategy.readHead();
|
|
2129
2262
|
if (head === null) {
|
|
2130
2263
|
this.order = this.strategy.order;
|
|
@@ -2155,42 +2288,72 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2155
2288
|
}
|
|
2156
2289
|
async insertableNode(value) {
|
|
2157
2290
|
let node = await this.getNode(this.rootId);
|
|
2291
|
+
if (node.parent !== null) {
|
|
2292
|
+
node.parent = null;
|
|
2293
|
+
await this.bufferForNodeUpdate(node);
|
|
2294
|
+
}
|
|
2158
2295
|
while (!node.leaf) {
|
|
2296
|
+
const parentId = node.id;
|
|
2159
2297
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2160
2298
|
const nValue = node.values[i];
|
|
2161
2299
|
const k = node.keys;
|
|
2162
2300
|
if (this.comparator.isSame(value, nValue)) {
|
|
2163
|
-
node = await this.getNode(
|
|
2301
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2302
|
+
if (node.parent !== parentId) {
|
|
2303
|
+
node.parent = parentId;
|
|
2304
|
+
await this.bufferForNodeUpdate(node);
|
|
2305
|
+
}
|
|
2164
2306
|
break;
|
|
2165
2307
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
2166
|
-
node = await this.getNode(
|
|
2308
|
+
node = await this.getNode(node.keys[i]);
|
|
2309
|
+
if (node.parent !== parentId) {
|
|
2310
|
+
node.parent = parentId;
|
|
2311
|
+
await this.bufferForNodeUpdate(node);
|
|
2312
|
+
}
|
|
2167
2313
|
break;
|
|
2168
2314
|
} else if (i + 1 === node.values.length) {
|
|
2169
|
-
node = await this.getNode(
|
|
2315
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2316
|
+
if (node.parent !== parentId) {
|
|
2317
|
+
node.parent = parentId;
|
|
2318
|
+
await this.bufferForNodeUpdate(node);
|
|
2319
|
+
}
|
|
2170
2320
|
break;
|
|
2171
2321
|
}
|
|
2172
2322
|
}
|
|
2173
2323
|
}
|
|
2174
2324
|
return node;
|
|
2175
2325
|
}
|
|
2176
|
-
/**
|
|
2177
|
-
* Find the insertable node using primaryAsc comparison.
|
|
2178
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
2179
|
-
*/
|
|
2180
2326
|
async insertableNodeByPrimary(value) {
|
|
2181
2327
|
let node = await this.getNode(this.rootId);
|
|
2328
|
+
if (node.parent !== null) {
|
|
2329
|
+
node.parent = null;
|
|
2330
|
+
await this.bufferForNodeUpdate(node);
|
|
2331
|
+
}
|
|
2182
2332
|
while (!node.leaf) {
|
|
2333
|
+
const parentId = node.id;
|
|
2183
2334
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2184
2335
|
const nValue = node.values[i];
|
|
2185
2336
|
const k = node.keys;
|
|
2186
2337
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
2187
|
-
node = await this.getNode(
|
|
2338
|
+
node = await this.getNode(node.keys[i]);
|
|
2339
|
+
if (node.parent !== parentId) {
|
|
2340
|
+
node.parent = parentId;
|
|
2341
|
+
await this.bufferForNodeUpdate(node);
|
|
2342
|
+
}
|
|
2188
2343
|
break;
|
|
2189
2344
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2190
|
-
node = await this.getNode(
|
|
2345
|
+
node = await this.getNode(node.keys[i]);
|
|
2346
|
+
if (node.parent !== parentId) {
|
|
2347
|
+
node.parent = parentId;
|
|
2348
|
+
await this.bufferForNodeUpdate(node);
|
|
2349
|
+
}
|
|
2191
2350
|
break;
|
|
2192
2351
|
} else if (i + 1 === node.values.length) {
|
|
2193
|
-
node = await this.getNode(
|
|
2352
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2353
|
+
if (node.parent !== parentId) {
|
|
2354
|
+
node.parent = parentId;
|
|
2355
|
+
await this.bufferForNodeUpdate(node);
|
|
2356
|
+
}
|
|
2194
2357
|
break;
|
|
2195
2358
|
}
|
|
2196
2359
|
}
|
|
@@ -2199,16 +2362,29 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2199
2362
|
}
|
|
2200
2363
|
async insertableRightestNodeByPrimary(value) {
|
|
2201
2364
|
let node = await this.getNode(this.rootId);
|
|
2365
|
+
if (node.parent !== null) {
|
|
2366
|
+
node.parent = null;
|
|
2367
|
+
await this.bufferForNodeUpdate(node);
|
|
2368
|
+
}
|
|
2202
2369
|
while (!node.leaf) {
|
|
2370
|
+
const parentId = node.id;
|
|
2203
2371
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2204
2372
|
const nValue = node.values[i];
|
|
2205
2373
|
const k = node.keys;
|
|
2206
2374
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2207
|
-
node = await this.getNode(
|
|
2375
|
+
node = await this.getNode(node.keys[i]);
|
|
2376
|
+
if (node.parent !== parentId) {
|
|
2377
|
+
node.parent = parentId;
|
|
2378
|
+
await this.bufferForNodeUpdate(node);
|
|
2379
|
+
}
|
|
2208
2380
|
break;
|
|
2209
2381
|
}
|
|
2210
2382
|
if (i + 1 === node.values.length) {
|
|
2211
|
-
node = await this.getNode(
|
|
2383
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2384
|
+
if (node.parent !== parentId) {
|
|
2385
|
+
node.parent = parentId;
|
|
2386
|
+
await this.bufferForNodeUpdate(node);
|
|
2387
|
+
}
|
|
2212
2388
|
break;
|
|
2213
2389
|
}
|
|
2214
2390
|
}
|
|
@@ -2243,20 +2419,65 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2243
2419
|
}
|
|
2244
2420
|
async leftestNode() {
|
|
2245
2421
|
let node = await this.getNode(this.rootId);
|
|
2422
|
+
if (node.parent !== null) {
|
|
2423
|
+
node.parent = null;
|
|
2424
|
+
await this.bufferForNodeUpdate(node);
|
|
2425
|
+
}
|
|
2246
2426
|
while (!node.leaf) {
|
|
2427
|
+
const parentId = node.id;
|
|
2247
2428
|
const keys = node.keys;
|
|
2248
|
-
node = await this.getNode(keys[0]);
|
|
2429
|
+
node = await this.getNode(node.keys[0]);
|
|
2430
|
+
if (node.parent !== parentId) {
|
|
2431
|
+
node.parent = parentId;
|
|
2432
|
+
await this.bufferForNodeUpdate(node);
|
|
2433
|
+
}
|
|
2249
2434
|
}
|
|
2250
2435
|
return node;
|
|
2251
2436
|
}
|
|
2252
2437
|
async rightestNode() {
|
|
2253
2438
|
let node = await this.getNode(this.rootId);
|
|
2439
|
+
if (node.parent !== null) {
|
|
2440
|
+
node.parent = null;
|
|
2441
|
+
await this.bufferForNodeUpdate(node);
|
|
2442
|
+
}
|
|
2254
2443
|
while (!node.leaf) {
|
|
2444
|
+
const parentId = node.id;
|
|
2255
2445
|
const keys = node.keys;
|
|
2256
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2446
|
+
node = await this.getNode(node.keys[node.keys.length - 1]);
|
|
2447
|
+
if (node.parent !== parentId) {
|
|
2448
|
+
node.parent = parentId;
|
|
2449
|
+
await this.bufferForNodeUpdate(node);
|
|
2450
|
+
}
|
|
2257
2451
|
}
|
|
2258
2452
|
return node;
|
|
2259
2453
|
}
|
|
2454
|
+
async exists(key, value) {
|
|
2455
|
+
const node = await this.insertableNode(value);
|
|
2456
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2457
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
2458
|
+
const keys = node.keys[i];
|
|
2459
|
+
if (keys.includes(key)) {
|
|
2460
|
+
return true;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return false;
|
|
2465
|
+
}
|
|
2466
|
+
async forceUpdate(id) {
|
|
2467
|
+
if (id) {
|
|
2468
|
+
this.nodes.delete(id);
|
|
2469
|
+
await this.getNode(id);
|
|
2470
|
+
return 1;
|
|
2471
|
+
}
|
|
2472
|
+
const keys = Array.from(this.nodes.keys());
|
|
2473
|
+
for (const key of keys) {
|
|
2474
|
+
this.nodes.delete(key);
|
|
2475
|
+
}
|
|
2476
|
+
for (const key of keys) {
|
|
2477
|
+
await this.getNode(key);
|
|
2478
|
+
}
|
|
2479
|
+
return keys.length;
|
|
2480
|
+
}
|
|
2260
2481
|
async commitHeadBuffer() {
|
|
2261
2482
|
if (!this._strategyDirty) {
|
|
2262
2483
|
return;
|
|
@@ -2287,25 +2508,15 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2287
2508
|
}
|
|
2288
2509
|
this._nodeDeleteBuffer.clear();
|
|
2289
2510
|
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Retrieves the value associated with the given key (PK).
|
|
2292
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
2293
|
-
*
|
|
2294
|
-
* @param key The key to search for.
|
|
2295
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
2296
|
-
*/
|
|
2297
2511
|
async get(key) {
|
|
2298
|
-
return this.readLock(async () => {
|
|
2512
|
+
return await this.readLock(async () => {
|
|
2299
2513
|
let node = await this.leftestNode();
|
|
2300
2514
|
while (true) {
|
|
2301
|
-
|
|
2302
|
-
const
|
|
2303
|
-
for (let
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
if (keys[j] === key) {
|
|
2307
|
-
return node.values[i];
|
|
2308
|
-
}
|
|
2515
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2516
|
+
const keys = node.keys[i];
|
|
2517
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
2518
|
+
if (keys[j] === key) {
|
|
2519
|
+
return node.values[i];
|
|
2309
2520
|
}
|
|
2310
2521
|
}
|
|
2311
2522
|
}
|
|
@@ -2380,27 +2591,23 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2380
2591
|
}
|
|
2381
2592
|
}
|
|
2382
2593
|
async keys(condition, filterValues) {
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
return set;
|
|
2389
|
-
});
|
|
2594
|
+
const set = /* @__PURE__ */ new Set();
|
|
2595
|
+
for await (const key of this.keysStream(condition, filterValues)) {
|
|
2596
|
+
set.add(key);
|
|
2597
|
+
}
|
|
2598
|
+
return set;
|
|
2390
2599
|
}
|
|
2391
2600
|
async where(condition) {
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
return map;
|
|
2398
|
-
});
|
|
2601
|
+
const map = /* @__PURE__ */ new Map();
|
|
2602
|
+
for await (const [key, value] of this.whereStream(condition)) {
|
|
2603
|
+
map.set(key, value);
|
|
2604
|
+
}
|
|
2605
|
+
return map;
|
|
2399
2606
|
}
|
|
2400
2607
|
async insert(key, value) {
|
|
2401
|
-
|
|
2608
|
+
await this.writeLock(async () => {
|
|
2402
2609
|
const before = await this.insertableNode(value);
|
|
2403
|
-
this._insertAtLeaf(before, key, value);
|
|
2610
|
+
await this._insertAtLeaf(before, key, value);
|
|
2404
2611
|
if (before.values.length === this.order) {
|
|
2405
2612
|
const after = await this._createNode(
|
|
2406
2613
|
true,
|
|
@@ -2417,7 +2624,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2417
2624
|
before.values = before.values.slice(0, mid + 1);
|
|
2418
2625
|
before.keys = before.keys.slice(0, mid + 1);
|
|
2419
2626
|
await this._insertInParent(before, after.values[0], after);
|
|
2420
|
-
this.bufferForNodeUpdate(before);
|
|
2627
|
+
await this.bufferForNodeUpdate(before);
|
|
2421
2628
|
}
|
|
2422
2629
|
await this.commitHeadBuffer();
|
|
2423
2630
|
await this.commitNodeCreateBuffer();
|
|
@@ -2425,28 +2632,23 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2425
2632
|
});
|
|
2426
2633
|
}
|
|
2427
2634
|
async delete(key, value) {
|
|
2428
|
-
|
|
2635
|
+
await this.writeLock(async () => {
|
|
2429
2636
|
const node = await this.insertableNode(value);
|
|
2430
2637
|
let i = node.values.length;
|
|
2431
2638
|
while (i--) {
|
|
2432
2639
|
const nValue = node.values[i];
|
|
2433
2640
|
if (this.comparator.isSame(value, nValue)) {
|
|
2434
2641
|
const keys = node.keys[i];
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
} else if (node.id === this.rootId) {
|
|
2440
|
-
node.values.splice(i, 1);
|
|
2642
|
+
const keyIndex = keys.indexOf(key);
|
|
2643
|
+
if (keyIndex !== -1) {
|
|
2644
|
+
keys.splice(keyIndex, 1);
|
|
2645
|
+
if (keys.length === 0) {
|
|
2441
2646
|
node.keys.splice(i, 1);
|
|
2442
|
-
|
|
2443
|
-
} else {
|
|
2444
|
-
keys.splice(keys.indexOf(key), 1);
|
|
2445
|
-
node.keys.splice(i, 1);
|
|
2446
|
-
node.values.splice(node.values.indexOf(value), 1);
|
|
2447
|
-
await this._deleteEntry(node, key, value);
|
|
2448
|
-
this.bufferForNodeUpdate(node);
|
|
2647
|
+
node.values.splice(i, 1);
|
|
2449
2648
|
}
|
|
2649
|
+
await this._deleteEntry(node, key, value);
|
|
2650
|
+
await this.bufferForNodeUpdate(node);
|
|
2651
|
+
break;
|
|
2450
2652
|
}
|
|
2451
2653
|
}
|
|
2452
2654
|
}
|
|
@@ -2456,151 +2658,400 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2456
2658
|
await this.commitNodeDeleteBuffer();
|
|
2457
2659
|
});
|
|
2458
2660
|
}
|
|
2459
|
-
|
|
2460
|
-
return this.
|
|
2461
|
-
const node = await this.insertableNode(value);
|
|
2462
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2463
|
-
const nValue = node.values[i];
|
|
2464
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2465
|
-
const keys = node.keys[i];
|
|
2466
|
-
return keys.includes(key);
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
return false;
|
|
2470
|
-
});
|
|
2661
|
+
getHeadData() {
|
|
2662
|
+
return this.strategy.head.data;
|
|
2471
2663
|
}
|
|
2472
2664
|
async setHeadData(data) {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
this._strategyDirty = true;
|
|
2476
|
-
await this.commitHeadBuffer();
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
async forceUpdate() {
|
|
2480
|
-
return this.readLock(async () => {
|
|
2481
|
-
const keys = [...this.nodes.keys()];
|
|
2482
|
-
this.nodes.clear();
|
|
2483
|
-
await this.init();
|
|
2484
|
-
for (const key of keys) {
|
|
2485
|
-
await this.getNode(key);
|
|
2486
|
-
}
|
|
2487
|
-
return keys.length;
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
};
|
|
2491
|
-
|
|
2492
|
-
// src/base/SerializeStrategy.ts
|
|
2493
|
-
var SerializeStrategy = class {
|
|
2494
|
-
order;
|
|
2495
|
-
head;
|
|
2496
|
-
constructor(order) {
|
|
2497
|
-
this.order = order;
|
|
2498
|
-
this.head = {
|
|
2499
|
-
order,
|
|
2500
|
-
root: null,
|
|
2501
|
-
data: {}
|
|
2502
|
-
};
|
|
2665
|
+
this.strategy.head.data = data;
|
|
2666
|
+
await this.strategy.writeHead(this.strategy.head);
|
|
2503
2667
|
}
|
|
2504
2668
|
};
|
|
2505
2669
|
|
|
2506
|
-
// src/
|
|
2507
|
-
var
|
|
2508
|
-
getHeadData(key, defaultValue) {
|
|
2670
|
+
// src/SerializeStrategyAsync.ts
|
|
2671
|
+
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
2672
|
+
async getHeadData(key, defaultValue) {
|
|
2509
2673
|
if (!Object.hasOwn(this.head.data, key)) {
|
|
2510
|
-
this.setHeadData(key, defaultValue);
|
|
2674
|
+
await this.setHeadData(key, defaultValue);
|
|
2511
2675
|
}
|
|
2512
2676
|
return this.head.data[key];
|
|
2513
2677
|
}
|
|
2514
|
-
setHeadData(key, data) {
|
|
2678
|
+
async setHeadData(key, data) {
|
|
2515
2679
|
this.head.data[key] = data;
|
|
2516
|
-
this.writeHead(this.head);
|
|
2680
|
+
await this.writeHead(this.head);
|
|
2517
2681
|
}
|
|
2518
|
-
autoIncrement(key, defaultValue) {
|
|
2519
|
-
const current = this.getHeadData(key, defaultValue);
|
|
2682
|
+
async autoIncrement(key, defaultValue) {
|
|
2683
|
+
const current = await this.getHeadData(key, defaultValue);
|
|
2520
2684
|
const next = current + 1;
|
|
2521
|
-
this.setHeadData(key, next);
|
|
2685
|
+
await this.setHeadData(key, next);
|
|
2522
2686
|
return current;
|
|
2523
2687
|
}
|
|
2688
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2689
|
+
if (this.head.root !== oldRoot) {
|
|
2690
|
+
return false;
|
|
2691
|
+
}
|
|
2692
|
+
this.head.root = newRoot;
|
|
2693
|
+
await this.writeHead(this.head);
|
|
2694
|
+
return true;
|
|
2695
|
+
}
|
|
2524
2696
|
};
|
|
2525
|
-
var
|
|
2697
|
+
var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
|
|
2526
2698
|
node;
|
|
2527
2699
|
constructor(order) {
|
|
2528
2700
|
super(order);
|
|
2529
2701
|
this.node = {};
|
|
2530
2702
|
}
|
|
2531
|
-
id(isLeaf) {
|
|
2532
|
-
return this.autoIncrement("index", 1).toString();
|
|
2703
|
+
async id(isLeaf) {
|
|
2704
|
+
return (await this.autoIncrement("index", 1)).toString();
|
|
2533
2705
|
}
|
|
2534
|
-
read(id) {
|
|
2706
|
+
async read(id) {
|
|
2535
2707
|
if (!Object.hasOwn(this.node, id)) {
|
|
2536
2708
|
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
2537
2709
|
}
|
|
2538
|
-
|
|
2710
|
+
const node = this.node[id];
|
|
2711
|
+
return JSON.parse(JSON.stringify(node));
|
|
2539
2712
|
}
|
|
2540
|
-
write(id, node) {
|
|
2713
|
+
async write(id, node) {
|
|
2541
2714
|
this.node[id] = node;
|
|
2542
2715
|
}
|
|
2543
|
-
delete(id) {
|
|
2716
|
+
async delete(id) {
|
|
2544
2717
|
delete this.node[id];
|
|
2545
2718
|
}
|
|
2546
|
-
readHead() {
|
|
2719
|
+
async readHead() {
|
|
2547
2720
|
if (this.head.root === null) {
|
|
2548
2721
|
return null;
|
|
2549
2722
|
}
|
|
2550
2723
|
return this.head;
|
|
2551
2724
|
}
|
|
2552
|
-
writeHead(head) {
|
|
2725
|
+
async writeHead(head) {
|
|
2553
2726
|
this.head = head;
|
|
2554
2727
|
}
|
|
2555
2728
|
};
|
|
2556
2729
|
|
|
2557
|
-
// src/
|
|
2558
|
-
var
|
|
2730
|
+
// src/transaction/BPTreeAsyncSnapshotStrategy.ts
|
|
2731
|
+
var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
|
|
2732
|
+
baseStrategy;
|
|
2733
|
+
snapshotHead;
|
|
2734
|
+
constructor(baseStrategy, root) {
|
|
2735
|
+
super(baseStrategy.order);
|
|
2736
|
+
this.baseStrategy = baseStrategy;
|
|
2737
|
+
this.snapshotHead = {
|
|
2738
|
+
...baseStrategy.head,
|
|
2739
|
+
root,
|
|
2740
|
+
data: { ...baseStrategy.head.data }
|
|
2741
|
+
};
|
|
2742
|
+
this.head = this.snapshotHead;
|
|
2743
|
+
}
|
|
2744
|
+
async id(isLeaf) {
|
|
2745
|
+
return await this.baseStrategy.id(isLeaf);
|
|
2746
|
+
}
|
|
2747
|
+
async read(id) {
|
|
2748
|
+
return await this.baseStrategy.read(id);
|
|
2749
|
+
}
|
|
2750
|
+
async write(id, node) {
|
|
2751
|
+
await this.baseStrategy.write(id, node);
|
|
2752
|
+
}
|
|
2753
|
+
async delete(id) {
|
|
2754
|
+
await this.baseStrategy.delete(id);
|
|
2755
|
+
}
|
|
2756
|
+
async readHead() {
|
|
2757
|
+
return this.snapshotHead;
|
|
2758
|
+
}
|
|
2759
|
+
async writeHead(head) {
|
|
2760
|
+
this.snapshotHead.root = head.root;
|
|
2761
|
+
this.snapshotHead.data = { ...head.data };
|
|
2762
|
+
}
|
|
2763
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2764
|
+
return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
2765
|
+
}
|
|
2559
2766
|
async getHeadData(key, defaultValue) {
|
|
2560
|
-
|
|
2561
|
-
await this.setHeadData(key, defaultValue);
|
|
2562
|
-
}
|
|
2563
|
-
return this.head.data[key];
|
|
2767
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2564
2768
|
}
|
|
2565
2769
|
async setHeadData(key, data) {
|
|
2566
|
-
this.
|
|
2567
|
-
await this.writeHead(this.head);
|
|
2770
|
+
this.snapshotHead.data[key] = data;
|
|
2568
2771
|
}
|
|
2569
2772
|
async autoIncrement(key, defaultValue) {
|
|
2570
|
-
|
|
2571
|
-
const next = current + 1;
|
|
2572
|
-
await this.setHeadData(key, next);
|
|
2573
|
-
return current;
|
|
2773
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2574
2774
|
}
|
|
2575
2775
|
};
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2776
|
+
|
|
2777
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2778
|
+
var BPTreeAsyncTransaction = class extends BPTreeAsyncBase {
|
|
2779
|
+
realBaseTree;
|
|
2780
|
+
realBaseStrategy;
|
|
2781
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
2782
|
+
dirtyIds;
|
|
2783
|
+
createdInTx;
|
|
2784
|
+
deletedIds;
|
|
2785
|
+
initialRootId;
|
|
2786
|
+
transactionRootId;
|
|
2787
|
+
constructor(baseTree) {
|
|
2788
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
2789
|
+
this.realBaseTree = baseTree;
|
|
2790
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
2791
|
+
this.order = baseTree.getOrder();
|
|
2792
|
+
this.initialRootId = "";
|
|
2793
|
+
this.transactionRootId = "";
|
|
2794
|
+
this.dirtyIds = /* @__PURE__ */ new Set();
|
|
2795
|
+
this.createdInTx = /* @__PURE__ */ new Set();
|
|
2796
|
+
this.deletedIds = /* @__PURE__ */ new Set();
|
|
2581
2797
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2798
|
+
/**
|
|
2799
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
2800
|
+
*/
|
|
2801
|
+
async initTransaction() {
|
|
2802
|
+
const head = await this.realBaseStrategy.readHead();
|
|
2803
|
+
if (head) {
|
|
2804
|
+
this.order = head.order;
|
|
2805
|
+
this.initialRootId = head.root;
|
|
2806
|
+
} else {
|
|
2807
|
+
this.initialRootId = this.realBaseTree.getRootId();
|
|
2808
|
+
}
|
|
2809
|
+
if (!this.initialRootId) {
|
|
2810
|
+
const root = await this._createNode(true, [], [], true);
|
|
2811
|
+
this.initialRootId = root.id;
|
|
2812
|
+
}
|
|
2813
|
+
this.transactionRootId = this.initialRootId;
|
|
2814
|
+
this.rootId = this.transactionRootId;
|
|
2815
|
+
const snapshotStrategy = new BPTreeAsyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
2816
|
+
this.strategy = snapshotStrategy;
|
|
2817
|
+
this.txNodes.clear();
|
|
2818
|
+
this.dirtyIds.clear();
|
|
2819
|
+
this.createdInTx.clear();
|
|
2820
|
+
this.deletedIds.clear();
|
|
2584
2821
|
}
|
|
2585
|
-
async
|
|
2586
|
-
if (
|
|
2587
|
-
|
|
2822
|
+
async getNode(id) {
|
|
2823
|
+
if (this.txNodes.has(id)) {
|
|
2824
|
+
return this.txNodes.get(id);
|
|
2825
|
+
}
|
|
2826
|
+
if (this.deletedIds.has(id)) {
|
|
2827
|
+
throw new Error(`The tree attempted to reference deleted node '${id}'`);
|
|
2588
2828
|
}
|
|
2589
|
-
|
|
2829
|
+
const baseNode = await this.realBaseStrategy.read(id);
|
|
2830
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
2831
|
+
this.txNodes.set(id, clone);
|
|
2832
|
+
return clone;
|
|
2590
2833
|
}
|
|
2591
|
-
async
|
|
2592
|
-
this.node
|
|
2834
|
+
async bufferForNodeUpdate(node) {
|
|
2835
|
+
if (this.dirtyIds.has(node.id) && this.txNodes.has(node.id) && node._p) {
|
|
2836
|
+
this.txNodes.set(node.id, node);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
node._p = true;
|
|
2840
|
+
this.txNodes.set(node.id, node);
|
|
2841
|
+
this.dirtyIds.add(node.id);
|
|
2842
|
+
if (node.leaf) {
|
|
2843
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
2844
|
+
try {
|
|
2845
|
+
await this.bufferForNodeUpdate(await this.getNode(node.next));
|
|
2846
|
+
} catch (e) {
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
2850
|
+
try {
|
|
2851
|
+
await this.bufferForNodeUpdate(await this.getNode(node.prev));
|
|
2852
|
+
} catch (e) {
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
await this.markPathDirty(node);
|
|
2857
|
+
delete node._p;
|
|
2593
2858
|
}
|
|
2594
|
-
async
|
|
2595
|
-
|
|
2859
|
+
async bufferForNodeCreate(node) {
|
|
2860
|
+
this.txNodes.set(node.id, node);
|
|
2861
|
+
this.dirtyIds.add(node.id);
|
|
2862
|
+
this.createdInTx.add(node.id);
|
|
2863
|
+
if (node.leaf) {
|
|
2864
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
2865
|
+
try {
|
|
2866
|
+
await this.bufferForNodeUpdate(await this.getNode(node.next));
|
|
2867
|
+
} catch (e) {
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
2871
|
+
try {
|
|
2872
|
+
await this.bufferForNodeUpdate(await this.getNode(node.prev));
|
|
2873
|
+
} catch (e) {
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
await this.markPathDirty(node);
|
|
2596
2878
|
}
|
|
2597
|
-
async
|
|
2598
|
-
|
|
2599
|
-
|
|
2879
|
+
async bufferForNodeDelete(node) {
|
|
2880
|
+
this.txNodes.delete(node.id);
|
|
2881
|
+
this.dirtyIds.add(node.id);
|
|
2882
|
+
this.deletedIds.add(node.id);
|
|
2883
|
+
}
|
|
2884
|
+
async markPathDirty(node) {
|
|
2885
|
+
let curr = node;
|
|
2886
|
+
while (curr.parent) {
|
|
2887
|
+
if (this.deletedIds.has(curr.parent)) {
|
|
2888
|
+
break;
|
|
2889
|
+
}
|
|
2890
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
2891
|
+
break;
|
|
2892
|
+
}
|
|
2893
|
+
const parent = await this.getNode(curr.parent);
|
|
2894
|
+
this.dirtyIds.add(parent.id);
|
|
2895
|
+
curr = parent;
|
|
2896
|
+
}
|
|
2897
|
+
if (!curr.parent) {
|
|
2898
|
+
this.transactionRootId = curr.id;
|
|
2600
2899
|
}
|
|
2601
|
-
return this.head;
|
|
2602
2900
|
}
|
|
2603
|
-
async
|
|
2604
|
-
|
|
2901
|
+
async _createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
2902
|
+
const id = await this.strategy.id(isLeaf);
|
|
2903
|
+
const node = {
|
|
2904
|
+
id,
|
|
2905
|
+
keys,
|
|
2906
|
+
values,
|
|
2907
|
+
leaf,
|
|
2908
|
+
parent,
|
|
2909
|
+
next,
|
|
2910
|
+
prev
|
|
2911
|
+
};
|
|
2912
|
+
await this.bufferForNodeCreate(node);
|
|
2913
|
+
return node;
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Attempts to commit the transaction.
|
|
2917
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
2918
|
+
*
|
|
2919
|
+
* @returns A promise that resolves to the transaction result.
|
|
2920
|
+
*/
|
|
2921
|
+
async commit() {
|
|
2922
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
2923
|
+
const finalNodes = [];
|
|
2924
|
+
for (const oldId of this.dirtyIds) {
|
|
2925
|
+
if (this.createdInTx.has(oldId)) {
|
|
2926
|
+
idMapping.set(oldId, oldId);
|
|
2927
|
+
} else {
|
|
2928
|
+
const node = this.txNodes.get(oldId);
|
|
2929
|
+
if (node) {
|
|
2930
|
+
const newId = await this.realBaseStrategy.id(node.leaf);
|
|
2931
|
+
idMapping.set(oldId, newId);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
const newCreatedIds = [];
|
|
2936
|
+
for (const oldId of this.dirtyIds) {
|
|
2937
|
+
const node = this.txNodes.get(oldId);
|
|
2938
|
+
if (!node) continue;
|
|
2939
|
+
const newId = idMapping.get(oldId);
|
|
2940
|
+
node.id = newId;
|
|
2941
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
2942
|
+
node.parent = idMapping.get(node.parent);
|
|
2943
|
+
}
|
|
2944
|
+
if (!node.leaf) {
|
|
2945
|
+
const internal = node;
|
|
2946
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
2947
|
+
const childId = internal.keys[i];
|
|
2948
|
+
if (idMapping.has(childId)) {
|
|
2949
|
+
internal.keys[i] = idMapping.get(childId);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
if (node.leaf) {
|
|
2954
|
+
const leaf = node;
|
|
2955
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
2956
|
+
leaf.next = idMapping.get(leaf.next);
|
|
2957
|
+
}
|
|
2958
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
2959
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
finalNodes.push(node);
|
|
2963
|
+
newCreatedIds.push(newId);
|
|
2964
|
+
}
|
|
2965
|
+
let newRootId = this.rootId;
|
|
2966
|
+
if (idMapping.has(this.rootId)) {
|
|
2967
|
+
newRootId = idMapping.get(this.rootId);
|
|
2968
|
+
}
|
|
2969
|
+
for (const node of finalNodes) {
|
|
2970
|
+
await this.realBaseStrategy.write(node.id, node);
|
|
2971
|
+
}
|
|
2972
|
+
const success = await this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
2973
|
+
if (success) {
|
|
2974
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
2975
|
+
for (const oldId of this.dirtyIds) {
|
|
2976
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
2977
|
+
distinctObsolete.add(oldId);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return {
|
|
2981
|
+
success: true,
|
|
2982
|
+
createdIds: newCreatedIds,
|
|
2983
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
2984
|
+
};
|
|
2985
|
+
} else {
|
|
2986
|
+
await this.rollback();
|
|
2987
|
+
return {
|
|
2988
|
+
success: false,
|
|
2989
|
+
createdIds: newCreatedIds,
|
|
2990
|
+
obsoleteIds: []
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
/**
|
|
2995
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
2996
|
+
* Internal use only.
|
|
2997
|
+
*/
|
|
2998
|
+
async rollback() {
|
|
2999
|
+
this.txNodes.clear();
|
|
3000
|
+
this.dirtyIds.clear();
|
|
3001
|
+
this.createdInTx.clear();
|
|
3002
|
+
}
|
|
3003
|
+
async readLock(fn) {
|
|
3004
|
+
return await fn();
|
|
3005
|
+
}
|
|
3006
|
+
async writeLock(fn) {
|
|
3007
|
+
return await fn();
|
|
3008
|
+
}
|
|
3009
|
+
async commitHeadBuffer() {
|
|
3010
|
+
}
|
|
3011
|
+
async commitNodeCreateBuffer() {
|
|
3012
|
+
}
|
|
3013
|
+
async commitNodeUpdateBuffer() {
|
|
3014
|
+
}
|
|
3015
|
+
async commitNodeDeleteBuffer() {
|
|
3016
|
+
}
|
|
3017
|
+
};
|
|
3018
|
+
|
|
3019
|
+
// src/BPTreeAsync.ts
|
|
3020
|
+
var BPTreeAsync = class extends BPTreeAsyncBase {
|
|
3021
|
+
constructor(strategy, comparator, option) {
|
|
3022
|
+
super(strategy, comparator, option);
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Creates a new asynchronous transaction.
|
|
3026
|
+
* @returns A promise that resolves to a new BPTreeAsyncTransaction.
|
|
3027
|
+
*/
|
|
3028
|
+
async createTransaction() {
|
|
3029
|
+
const tx = new BPTreeAsyncTransaction(this);
|
|
3030
|
+
await tx.initTransaction();
|
|
3031
|
+
return tx;
|
|
3032
|
+
}
|
|
3033
|
+
async insert(key, value) {
|
|
3034
|
+
const tx = await this.createTransaction();
|
|
3035
|
+
await tx.insert(key, value);
|
|
3036
|
+
const { success } = await tx.commit();
|
|
3037
|
+
await this.init();
|
|
3038
|
+
if (!success) {
|
|
3039
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
async delete(key, value) {
|
|
3043
|
+
const tx = await this.createTransaction();
|
|
3044
|
+
await tx.delete(key, value);
|
|
3045
|
+
const { success } = await tx.commit();
|
|
3046
|
+
await this.init();
|
|
3047
|
+
if (!success) {
|
|
3048
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
async readLock(fn) {
|
|
3052
|
+
return await fn();
|
|
3053
|
+
}
|
|
3054
|
+
async writeLock(fn) {
|
|
3055
|
+
return await fn();
|
|
2605
3056
|
}
|
|
2606
3057
|
};
|