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/esm/index.mjs
CHANGED
|
@@ -439,6 +439,20 @@ var BPTree = class _BPTree {
|
|
|
439
439
|
option;
|
|
440
440
|
order;
|
|
441
441
|
rootId;
|
|
442
|
+
/**
|
|
443
|
+
* Returns the ID of the root node.
|
|
444
|
+
* @returns The root node ID.
|
|
445
|
+
*/
|
|
446
|
+
getRootId() {
|
|
447
|
+
return this.rootId;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Returns the order of the B+Tree.
|
|
451
|
+
* @returns The order of the tree.
|
|
452
|
+
*/
|
|
453
|
+
getOrder() {
|
|
454
|
+
return this.order;
|
|
455
|
+
}
|
|
442
456
|
_strategyDirty;
|
|
443
457
|
_nodeCreateBuffer;
|
|
444
458
|
_nodeUpdateBuffer;
|
|
@@ -671,24 +685,21 @@ var BPTree = class _BPTree {
|
|
|
671
685
|
break;
|
|
672
686
|
}
|
|
673
687
|
keys.push(key);
|
|
674
|
-
this.bufferForNodeUpdate(node);
|
|
675
|
-
break;
|
|
688
|
+
return this.bufferForNodeUpdate(node);
|
|
676
689
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
677
690
|
node.values.splice(i, 0, value);
|
|
678
691
|
node.keys.splice(i, 0, [key]);
|
|
679
|
-
this.bufferForNodeUpdate(node);
|
|
680
|
-
break;
|
|
692
|
+
return this.bufferForNodeUpdate(node);
|
|
681
693
|
} else if (i + 1 === node.values.length) {
|
|
682
694
|
node.values.push(value);
|
|
683
695
|
node.keys.push([key]);
|
|
684
|
-
this.bufferForNodeUpdate(node);
|
|
685
|
-
break;
|
|
696
|
+
return this.bufferForNodeUpdate(node);
|
|
686
697
|
}
|
|
687
698
|
}
|
|
688
699
|
} else {
|
|
689
700
|
node.values = [value];
|
|
690
701
|
node.keys = [[key]];
|
|
691
|
-
this.bufferForNodeUpdate(node);
|
|
702
|
+
return this.bufferForNodeUpdate(node);
|
|
692
703
|
}
|
|
693
704
|
}
|
|
694
705
|
bufferForNodeCreate(node) {
|
|
@@ -727,8 +738,8 @@ var BPTree = class _BPTree {
|
|
|
727
738
|
}
|
|
728
739
|
};
|
|
729
740
|
|
|
730
|
-
// src/
|
|
731
|
-
var
|
|
741
|
+
// src/base/BPTreeSyncBase.ts
|
|
742
|
+
var BPTreeSyncBase = class extends BPTree {
|
|
732
743
|
constructor(strategy, comparator, option) {
|
|
733
744
|
super(strategy, comparator, option);
|
|
734
745
|
this.nodes = this._createCachedNode();
|
|
@@ -804,7 +815,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
804
815
|
}
|
|
805
816
|
return id;
|
|
806
817
|
}
|
|
807
|
-
_createNode(isLeaf, keys, values, leaf =
|
|
818
|
+
_createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
808
819
|
const id = this._createNodeId(isLeaf);
|
|
809
820
|
const node = {
|
|
810
821
|
id,
|
|
@@ -815,29 +826,26 @@ var BPTreeSync = class extends BPTree {
|
|
|
815
826
|
next,
|
|
816
827
|
prev
|
|
817
828
|
};
|
|
818
|
-
this.
|
|
829
|
+
this.bufferForNodeCreate(node);
|
|
819
830
|
return node;
|
|
820
831
|
}
|
|
821
832
|
_deleteEntry(node, key, value) {
|
|
822
833
|
if (!node.leaf) {
|
|
834
|
+
let keyIndex = -1;
|
|
823
835
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
node.keys.splice(i, 1);
|
|
827
|
-
this.bufferForNodeUpdate(node);
|
|
836
|
+
if (node.keys[i] === key) {
|
|
837
|
+
keyIndex = i;
|
|
828
838
|
break;
|
|
829
839
|
}
|
|
830
840
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
841
|
+
if (keyIndex !== -1) {
|
|
842
|
+
node.keys.splice(keyIndex, 1);
|
|
843
|
+
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
844
|
+
node.values.splice(valueIndex, 1);
|
|
845
|
+
this.bufferForNodeUpdate(node);
|
|
838
846
|
}
|
|
839
847
|
}
|
|
840
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
848
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
841
849
|
const keys = node.keys;
|
|
842
850
|
this.bufferForNodeDelete(node);
|
|
843
851
|
const newRoot = this.getNode(keys[0]);
|
|
@@ -906,20 +914,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
906
914
|
pointer.values.push(guess);
|
|
907
915
|
} else {
|
|
908
916
|
pointer.next = node.next;
|
|
909
|
-
pointer.prev = node.id;
|
|
910
917
|
if (pointer.next) {
|
|
911
|
-
const n = this.getNode(
|
|
918
|
+
const n = this.getNode(pointer.next);
|
|
912
919
|
n.prev = pointer.id;
|
|
913
920
|
this.bufferForNodeUpdate(n);
|
|
914
921
|
}
|
|
915
|
-
if (pointer.prev) {
|
|
916
|
-
const n = this.getNode(node.id);
|
|
917
|
-
n.next = pointer.id;
|
|
918
|
-
this.bufferForNodeUpdate(n);
|
|
919
|
-
}
|
|
920
|
-
if (isPredecessor) {
|
|
921
|
-
pointer.prev = null;
|
|
922
|
-
}
|
|
923
922
|
}
|
|
924
923
|
pointer.values.push(...node.values);
|
|
925
924
|
if (!pointer.leaf) {
|
|
@@ -943,13 +942,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
943
942
|
node.keys = [pointerPm, ...node.keys];
|
|
944
943
|
node.values = [guess, ...node.values];
|
|
945
944
|
parentNode = this.getNode(node.parent);
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
this.bufferForNodeUpdate(parentNode);
|
|
951
|
-
break;
|
|
952
|
-
}
|
|
945
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
946
|
+
if (nodeIndex > 0) {
|
|
947
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
948
|
+
this.bufferForNodeUpdate(parentNode);
|
|
953
949
|
}
|
|
954
950
|
} else {
|
|
955
951
|
pointerPm = pointer.keys.splice(-1)[0];
|
|
@@ -957,13 +953,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
957
953
|
node.keys = [pointerPm, ...node.keys];
|
|
958
954
|
node.values = [pointerKm, ...node.values];
|
|
959
955
|
parentNode = this.getNode(node.parent);
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
this.bufferForNodeUpdate(parentNode);
|
|
965
|
-
break;
|
|
966
|
-
}
|
|
956
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
957
|
+
if (nodeIndex > 0) {
|
|
958
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
959
|
+
this.bufferForNodeUpdate(parentNode);
|
|
967
960
|
}
|
|
968
961
|
}
|
|
969
962
|
this.bufferForNodeUpdate(node);
|
|
@@ -977,13 +970,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
977
970
|
node.keys = [...node.keys, pointerP0];
|
|
978
971
|
node.values = [...node.values, guess];
|
|
979
972
|
parentNode = this.getNode(node.parent);
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
this.bufferForNodeUpdate(parentNode);
|
|
985
|
-
break;
|
|
986
|
-
}
|
|
973
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
974
|
+
if (pointerIndex > 0) {
|
|
975
|
+
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
976
|
+
this.bufferForNodeUpdate(parentNode);
|
|
987
977
|
}
|
|
988
978
|
} else {
|
|
989
979
|
pointerP0 = pointer.keys.splice(0, 1)[0];
|
|
@@ -991,13 +981,10 @@ var BPTreeSync = class extends BPTree {
|
|
|
991
981
|
node.keys = [...node.keys, pointerP0];
|
|
992
982
|
node.values = [...node.values, pointerK0];
|
|
993
983
|
parentNode = this.getNode(node.parent);
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
this.bufferForNodeUpdate(parentNode);
|
|
999
|
-
break;
|
|
1000
|
-
}
|
|
984
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
985
|
+
if (pointerIndex > 0) {
|
|
986
|
+
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
987
|
+
this.bufferForNodeUpdate(parentNode);
|
|
1001
988
|
}
|
|
1002
989
|
}
|
|
1003
990
|
this.bufferForNodeUpdate(node);
|
|
@@ -1025,6 +1012,8 @@ var BPTreeSync = class extends BPTree {
|
|
|
1025
1012
|
}
|
|
1026
1013
|
}
|
|
1027
1014
|
}
|
|
1015
|
+
} else {
|
|
1016
|
+
this.bufferForNodeUpdate(node);
|
|
1028
1017
|
}
|
|
1029
1018
|
}
|
|
1030
1019
|
_insertInParent(node, value, pointer) {
|
|
@@ -1043,31 +1032,25 @@ var BPTreeSync = class extends BPTree {
|
|
|
1043
1032
|
return;
|
|
1044
1033
|
}
|
|
1045
1034
|
const parentNode = this.getNode(node.parent);
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
insertIndex = i + 1;
|
|
1050
|
-
} else {
|
|
1051
|
-
break;
|
|
1052
|
-
}
|
|
1035
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
1036
|
+
if (nodeIndex === -1) {
|
|
1037
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
1053
1038
|
}
|
|
1039
|
+
const insertIndex = nodeIndex;
|
|
1054
1040
|
parentNode.values.splice(insertIndex, 0, value);
|
|
1055
1041
|
parentNode.keys.splice(insertIndex + 1, 0, pointer.id);
|
|
1056
1042
|
pointer.parent = parentNode.id;
|
|
1057
1043
|
if (pointer.leaf) {
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
this.
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
const rightSibling = this.getNode(rightSiblingId);
|
|
1069
|
-
rightSibling.prev = pointer.id;
|
|
1070
|
-
this.bufferForNodeUpdate(rightSibling);
|
|
1044
|
+
const leftSibling = node;
|
|
1045
|
+
const oldNextId = leftSibling.next;
|
|
1046
|
+
pointer.prev = leftSibling.id;
|
|
1047
|
+
pointer.next = oldNextId;
|
|
1048
|
+
leftSibling.next = pointer.id;
|
|
1049
|
+
this.bufferForNodeUpdate(leftSibling);
|
|
1050
|
+
if (oldNextId) {
|
|
1051
|
+
const oldNext = this.getNode(oldNextId);
|
|
1052
|
+
oldNext.prev = pointer.id;
|
|
1053
|
+
this.bufferForNodeUpdate(oldNext);
|
|
1071
1054
|
}
|
|
1072
1055
|
}
|
|
1073
1056
|
this.bufferForNodeUpdate(parentNode);
|
|
@@ -1096,6 +1079,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
1096
1079
|
}
|
|
1097
1080
|
}
|
|
1098
1081
|
init() {
|
|
1082
|
+
this.clear();
|
|
1099
1083
|
const head = this.strategy.readHead();
|
|
1100
1084
|
if (head === null) {
|
|
1101
1085
|
this.order = this.strategy.order;
|
|
@@ -1126,42 +1110,72 @@ var BPTreeSync = class extends BPTree {
|
|
|
1126
1110
|
}
|
|
1127
1111
|
insertableNode(value) {
|
|
1128
1112
|
let node = this.getNode(this.rootId);
|
|
1113
|
+
if (node.parent !== null) {
|
|
1114
|
+
node.parent = null;
|
|
1115
|
+
this.bufferForNodeUpdate(node);
|
|
1116
|
+
}
|
|
1129
1117
|
while (!node.leaf) {
|
|
1118
|
+
const parentId = node.id;
|
|
1130
1119
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1131
1120
|
const nValue = node.values[i];
|
|
1132
1121
|
const k = node.keys;
|
|
1133
1122
|
if (this.comparator.isSame(value, nValue)) {
|
|
1134
1123
|
node = this.getNode(k[i + 1]);
|
|
1124
|
+
if (node.parent !== parentId) {
|
|
1125
|
+
node.parent = parentId;
|
|
1126
|
+
this.bufferForNodeUpdate(node);
|
|
1127
|
+
}
|
|
1135
1128
|
break;
|
|
1136
1129
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
1137
1130
|
node = this.getNode(k[i]);
|
|
1131
|
+
if (node.parent !== parentId) {
|
|
1132
|
+
node.parent = parentId;
|
|
1133
|
+
this.bufferForNodeUpdate(node);
|
|
1134
|
+
}
|
|
1138
1135
|
break;
|
|
1139
1136
|
} else if (i + 1 === node.values.length) {
|
|
1140
1137
|
node = this.getNode(k[i + 1]);
|
|
1138
|
+
if (node.parent !== parentId) {
|
|
1139
|
+
node.parent = parentId;
|
|
1140
|
+
this.bufferForNodeUpdate(node);
|
|
1141
|
+
}
|
|
1141
1142
|
break;
|
|
1142
1143
|
}
|
|
1143
1144
|
}
|
|
1144
1145
|
}
|
|
1145
1146
|
return node;
|
|
1146
1147
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Find the insertable node using primaryAsc comparison.
|
|
1149
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
1150
|
-
*/
|
|
1151
1148
|
insertableNodeByPrimary(value) {
|
|
1152
1149
|
let node = this.getNode(this.rootId);
|
|
1150
|
+
if (node.parent !== null) {
|
|
1151
|
+
node.parent = null;
|
|
1152
|
+
this.bufferForNodeUpdate(node);
|
|
1153
|
+
}
|
|
1153
1154
|
while (!node.leaf) {
|
|
1155
|
+
const parentId = node.id;
|
|
1154
1156
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1155
1157
|
const nValue = node.values[i];
|
|
1156
1158
|
const k = node.keys;
|
|
1157
1159
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
1158
1160
|
node = this.getNode(k[i]);
|
|
1161
|
+
if (node.parent !== parentId) {
|
|
1162
|
+
node.parent = parentId;
|
|
1163
|
+
this.bufferForNodeUpdate(node);
|
|
1164
|
+
}
|
|
1159
1165
|
break;
|
|
1160
1166
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1161
1167
|
node = this.getNode(k[i]);
|
|
1168
|
+
if (node.parent !== parentId) {
|
|
1169
|
+
node.parent = parentId;
|
|
1170
|
+
this.bufferForNodeUpdate(node);
|
|
1171
|
+
}
|
|
1162
1172
|
break;
|
|
1163
1173
|
} else if (i + 1 === node.values.length) {
|
|
1164
1174
|
node = this.getNode(k[i + 1]);
|
|
1175
|
+
if (node.parent !== parentId) {
|
|
1176
|
+
node.parent = parentId;
|
|
1177
|
+
this.bufferForNodeUpdate(node);
|
|
1178
|
+
}
|
|
1165
1179
|
break;
|
|
1166
1180
|
}
|
|
1167
1181
|
}
|
|
@@ -1170,16 +1184,29 @@ var BPTreeSync = class extends BPTree {
|
|
|
1170
1184
|
}
|
|
1171
1185
|
insertableRightestNodeByPrimary(value) {
|
|
1172
1186
|
let node = this.getNode(this.rootId);
|
|
1187
|
+
if (node.parent !== null) {
|
|
1188
|
+
node.parent = null;
|
|
1189
|
+
this.bufferForNodeUpdate(node);
|
|
1190
|
+
}
|
|
1173
1191
|
while (!node.leaf) {
|
|
1192
|
+
const parentId = node.id;
|
|
1174
1193
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1175
1194
|
const nValue = node.values[i];
|
|
1176
1195
|
const k = node.keys;
|
|
1177
1196
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
1178
1197
|
node = this.getNode(k[i]);
|
|
1198
|
+
if (node.parent !== parentId) {
|
|
1199
|
+
node.parent = parentId;
|
|
1200
|
+
this.bufferForNodeUpdate(node);
|
|
1201
|
+
}
|
|
1179
1202
|
break;
|
|
1180
1203
|
}
|
|
1181
1204
|
if (i + 1 === node.values.length) {
|
|
1182
1205
|
node = this.getNode(k[i + 1]);
|
|
1206
|
+
if (node.parent !== parentId) {
|
|
1207
|
+
node.parent = parentId;
|
|
1208
|
+
this.bufferForNodeUpdate(node);
|
|
1209
|
+
}
|
|
1183
1210
|
break;
|
|
1184
1211
|
}
|
|
1185
1212
|
}
|
|
@@ -1214,20 +1241,65 @@ var BPTreeSync = class extends BPTree {
|
|
|
1214
1241
|
}
|
|
1215
1242
|
leftestNode() {
|
|
1216
1243
|
let node = this.getNode(this.rootId);
|
|
1244
|
+
if (node.parent !== null) {
|
|
1245
|
+
node.parent = null;
|
|
1246
|
+
this.bufferForNodeUpdate(node);
|
|
1247
|
+
}
|
|
1217
1248
|
while (!node.leaf) {
|
|
1249
|
+
const parentId = node.id;
|
|
1218
1250
|
const keys = node.keys;
|
|
1219
1251
|
node = this.getNode(keys[0]);
|
|
1252
|
+
if (node.parent !== parentId) {
|
|
1253
|
+
node.parent = parentId;
|
|
1254
|
+
this.bufferForNodeUpdate(node);
|
|
1255
|
+
}
|
|
1220
1256
|
}
|
|
1221
1257
|
return node;
|
|
1222
1258
|
}
|
|
1223
1259
|
rightestNode() {
|
|
1224
1260
|
let node = this.getNode(this.rootId);
|
|
1261
|
+
if (node.parent !== null) {
|
|
1262
|
+
node.parent = null;
|
|
1263
|
+
this.bufferForNodeUpdate(node);
|
|
1264
|
+
}
|
|
1225
1265
|
while (!node.leaf) {
|
|
1266
|
+
const parentId = node.id;
|
|
1226
1267
|
const keys = node.keys;
|
|
1227
1268
|
node = this.getNode(keys[keys.length - 1]);
|
|
1269
|
+
if (node.parent !== parentId) {
|
|
1270
|
+
node.parent = parentId;
|
|
1271
|
+
this.bufferForNodeUpdate(node);
|
|
1272
|
+
}
|
|
1228
1273
|
}
|
|
1229
1274
|
return node;
|
|
1230
1275
|
}
|
|
1276
|
+
exists(key, value) {
|
|
1277
|
+
const node = this.insertableNode(value);
|
|
1278
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1279
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
1280
|
+
const keys = node.keys[i];
|
|
1281
|
+
if (keys.includes(key)) {
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
forceUpdate(id) {
|
|
1289
|
+
if (id) {
|
|
1290
|
+
this.nodes.delete(id);
|
|
1291
|
+
this.getNode(id);
|
|
1292
|
+
return 1;
|
|
1293
|
+
}
|
|
1294
|
+
const keys = Array.from(this.nodes.keys());
|
|
1295
|
+
for (const key of keys) {
|
|
1296
|
+
this.nodes.delete(key);
|
|
1297
|
+
}
|
|
1298
|
+
for (const key of keys) {
|
|
1299
|
+
this.getNode(key);
|
|
1300
|
+
}
|
|
1301
|
+
return keys.length;
|
|
1302
|
+
}
|
|
1231
1303
|
commitHeadBuffer() {
|
|
1232
1304
|
if (!this._strategyDirty) {
|
|
1233
1305
|
return;
|
|
@@ -1258,24 +1330,14 @@ var BPTreeSync = class extends BPTree {
|
|
|
1258
1330
|
}
|
|
1259
1331
|
this._nodeDeleteBuffer.clear();
|
|
1260
1332
|
}
|
|
1261
|
-
/**
|
|
1262
|
-
* Retrieves the value associated with the given key (PK).
|
|
1263
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
1264
|
-
*
|
|
1265
|
-
* @param key The key to search for.
|
|
1266
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1267
|
-
*/
|
|
1268
1333
|
get(key) {
|
|
1269
1334
|
let node = this.leftestNode();
|
|
1270
1335
|
while (true) {
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1273
|
-
for (let
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (keys[j] === key) {
|
|
1277
|
-
return node.values[i];
|
|
1278
|
-
}
|
|
1336
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1337
|
+
const keys = node.keys[i];
|
|
1338
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
1339
|
+
if (keys[j] === key) {
|
|
1340
|
+
return node.values[i];
|
|
1279
1341
|
}
|
|
1280
1342
|
}
|
|
1281
1343
|
}
|
|
@@ -1394,21 +1456,15 @@ var BPTreeSync = class extends BPTree {
|
|
|
1394
1456
|
const nValue = node.values[i];
|
|
1395
1457
|
if (this.comparator.isSame(value, nValue)) {
|
|
1396
1458
|
const keys = node.keys[i];
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
} else if (node.id === this.rootId) {
|
|
1402
|
-
node.values.splice(i, 1);
|
|
1459
|
+
const keyIndex = keys.indexOf(key);
|
|
1460
|
+
if (keyIndex !== -1) {
|
|
1461
|
+
keys.splice(keyIndex, 1);
|
|
1462
|
+
if (keys.length === 0) {
|
|
1403
1463
|
node.keys.splice(i, 1);
|
|
1404
|
-
|
|
1405
|
-
} else {
|
|
1406
|
-
keys.splice(keys.indexOf(key), 1);
|
|
1407
|
-
node.keys.splice(i, 1);
|
|
1408
|
-
node.values.splice(node.values.indexOf(value), 1);
|
|
1409
|
-
this._deleteEntry(node, key, value);
|
|
1410
|
-
this.bufferForNodeUpdate(node);
|
|
1464
|
+
node.values.splice(i, 1);
|
|
1411
1465
|
}
|
|
1466
|
+
this._deleteEntry(node, key, value);
|
|
1467
|
+
break;
|
|
1412
1468
|
}
|
|
1413
1469
|
}
|
|
1414
1470
|
}
|
|
@@ -1417,327 +1473,424 @@ var BPTreeSync = class extends BPTree {
|
|
|
1417
1473
|
this.commitNodeUpdateBuffer();
|
|
1418
1474
|
this.commitNodeDeleteBuffer();
|
|
1419
1475
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1423
|
-
const nValue = node.values[i];
|
|
1424
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
1425
|
-
const keys = node.keys[i];
|
|
1426
|
-
return keys.includes(key);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
return false;
|
|
1476
|
+
getHeadData() {
|
|
1477
|
+
return this.strategy.head.data;
|
|
1430
1478
|
}
|
|
1431
1479
|
setHeadData(data) {
|
|
1432
1480
|
this.strategy.head.data = data;
|
|
1433
|
-
this.
|
|
1434
|
-
this.commitHeadBuffer();
|
|
1481
|
+
this.strategy.writeHead(this.strategy.head);
|
|
1435
1482
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1483
|
+
};
|
|
1484
|
+
|
|
1485
|
+
// src/base/SerializeStrategy.ts
|
|
1486
|
+
var SerializeStrategy = class {
|
|
1487
|
+
order;
|
|
1488
|
+
head;
|
|
1489
|
+
constructor(order) {
|
|
1490
|
+
this.order = order;
|
|
1491
|
+
this.head = {
|
|
1492
|
+
order,
|
|
1493
|
+
root: null,
|
|
1494
|
+
data: {}
|
|
1495
|
+
};
|
|
1444
1496
|
}
|
|
1445
1497
|
};
|
|
1446
1498
|
|
|
1447
|
-
//
|
|
1448
|
-
var
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
writeQueue;
|
|
1453
|
-
static async CatchError(promise) {
|
|
1454
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
1455
|
-
}
|
|
1456
|
-
static IsRangeOverlap(a, b) {
|
|
1457
|
-
const [start1, end1] = a;
|
|
1458
|
-
const [start2, end2] = b;
|
|
1459
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
1460
|
-
return false;
|
|
1499
|
+
// src/SerializeStrategySync.ts
|
|
1500
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
1501
|
+
getHeadData(key, defaultValue) {
|
|
1502
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
1503
|
+
this.setHeadData(key, defaultValue);
|
|
1461
1504
|
}
|
|
1462
|
-
return
|
|
1505
|
+
return this.head.data[key];
|
|
1463
1506
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1507
|
+
setHeadData(key, data) {
|
|
1508
|
+
this.head.data[key] = data;
|
|
1509
|
+
this.writeHead(this.head);
|
|
1466
1510
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1511
|
+
autoIncrement(key, defaultValue) {
|
|
1512
|
+
const current = this.getHeadData(key, defaultValue);
|
|
1513
|
+
const next = current + 1;
|
|
1514
|
+
this.setHeadData(key, next);
|
|
1515
|
+
return current;
|
|
1469
1516
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1517
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1518
|
+
if (this.head.root !== oldRoot) {
|
|
1519
|
+
return false;
|
|
1520
|
+
}
|
|
1521
|
+
this.head.root = newRoot;
|
|
1522
|
+
this.writeHead(this.head);
|
|
1523
|
+
return true;
|
|
1472
1524
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
constructor() {
|
|
1477
|
-
|
|
1478
|
-
this.
|
|
1479
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
1480
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
1525
|
+
};
|
|
1526
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
1527
|
+
node;
|
|
1528
|
+
constructor(order) {
|
|
1529
|
+
super(order);
|
|
1530
|
+
this.node = {};
|
|
1481
1531
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
* @param start - The starting value of the range.
|
|
1485
|
-
* @param length - The length of the range.
|
|
1486
|
-
* @returns A range tuple [start, start + length].
|
|
1487
|
-
*/
|
|
1488
|
-
range(start, length) {
|
|
1489
|
-
return [start, start + length];
|
|
1532
|
+
id(isLeaf) {
|
|
1533
|
+
return this.autoIncrement("index", 1).toString();
|
|
1490
1534
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1535
|
+
read(id) {
|
|
1536
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
1537
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
1538
|
+
}
|
|
1539
|
+
const node = this.node[id];
|
|
1540
|
+
return JSON.parse(JSON.stringify(node));
|
|
1493
1541
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
const [b1, b2] = b;
|
|
1497
|
-
return a1 === b1 && a2 === b2;
|
|
1542
|
+
write(id, node) {
|
|
1543
|
+
this.node[id] = node;
|
|
1498
1544
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
if (!unit.condition()) {
|
|
1502
|
-
continue;
|
|
1503
|
-
}
|
|
1504
|
-
this._alloc(queue, workspaces, id);
|
|
1505
|
-
}
|
|
1545
|
+
delete(id) {
|
|
1546
|
+
delete this.node[id];
|
|
1506
1547
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
return handlers[key](...args);
|
|
1511
|
-
}
|
|
1548
|
+
readHead() {
|
|
1549
|
+
if (this.head.root === null) {
|
|
1550
|
+
return null;
|
|
1512
1551
|
}
|
|
1513
|
-
|
|
1552
|
+
return this.head;
|
|
1514
1553
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
const expectedType = pattern[index];
|
|
1518
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
1519
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
1520
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
1521
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
1522
|
-
return false;
|
|
1523
|
-
});
|
|
1554
|
+
writeHead(head) {
|
|
1555
|
+
this.head = head;
|
|
1524
1556
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
const unit = workspaces.get(lockId);
|
|
1541
|
-
if (!unit) {
|
|
1542
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1543
|
-
}
|
|
1544
|
-
workspaces.delete(lockId);
|
|
1545
|
-
unit.free();
|
|
1546
|
-
}
|
|
1547
|
-
_lock(queue, range, timeout, task, condition) {
|
|
1548
|
-
return new Promise((resolve, reject) => {
|
|
1549
|
-
let timeoutId = null;
|
|
1550
|
-
if (timeout >= 0) {
|
|
1551
|
-
timeoutId = setTimeout(() => {
|
|
1552
|
-
reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
|
|
1553
|
-
}, timeout);
|
|
1554
|
-
}
|
|
1555
|
-
const id = this._createRandomId();
|
|
1556
|
-
const alloc = async () => {
|
|
1557
|
-
if (timeoutId !== null) {
|
|
1558
|
-
clearTimeout(timeoutId);
|
|
1559
|
-
}
|
|
1560
|
-
const [err, v] = await _Ryoiki.CatchError(task(id));
|
|
1561
|
-
if (err) reject(err);
|
|
1562
|
-
else resolve(v);
|
|
1563
|
-
};
|
|
1564
|
-
const fetch = () => {
|
|
1565
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
1566
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
1567
|
-
};
|
|
1568
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
1569
|
-
fetch();
|
|
1570
|
-
});
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
// src/transaction/BPTreeSyncSnapshotStrategy.ts
|
|
1560
|
+
var BPTreeSyncSnapshotStrategy = class extends SerializeStrategySync {
|
|
1561
|
+
baseStrategy;
|
|
1562
|
+
snapshotHead;
|
|
1563
|
+
constructor(baseStrategy, root) {
|
|
1564
|
+
super(baseStrategy.order);
|
|
1565
|
+
this.baseStrategy = baseStrategy;
|
|
1566
|
+
this.snapshotHead = {
|
|
1567
|
+
...baseStrategy.head,
|
|
1568
|
+
root,
|
|
1569
|
+
data: { ...baseStrategy.head.data }
|
|
1570
|
+
};
|
|
1571
|
+
this.head = this.snapshotHead;
|
|
1571
1572
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
for (const lock of workspaces.values()) {
|
|
1575
|
-
if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
|
|
1576
|
-
isLocked = true;
|
|
1577
|
-
break;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
return isLocked;
|
|
1573
|
+
id(isLeaf) {
|
|
1574
|
+
return this.baseStrategy.id(isLeaf);
|
|
1581
1575
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
* @param range The range to check for active read locks.
|
|
1585
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
1586
|
-
*/
|
|
1587
|
-
isReading(range) {
|
|
1588
|
-
return this._checkWorking(range, this.readings);
|
|
1576
|
+
read(id) {
|
|
1577
|
+
return this.baseStrategy.read(id);
|
|
1589
1578
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
* @param range The range to check for active write locks.
|
|
1593
|
-
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
1594
|
-
*/
|
|
1595
|
-
isWriting(range) {
|
|
1596
|
-
return this._checkWorking(range, this.writings);
|
|
1579
|
+
write(id, node) {
|
|
1580
|
+
this.baseStrategy.write(id, node);
|
|
1597
1581
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
* @param range The range to check for read lock availability.
|
|
1601
|
-
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
1602
|
-
*/
|
|
1603
|
-
canRead(range) {
|
|
1604
|
-
const writing = this.isWriting(range);
|
|
1605
|
-
return !writing;
|
|
1582
|
+
delete(id) {
|
|
1583
|
+
this.baseStrategy.delete(id);
|
|
1606
1584
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
* @param range The range to check for write lock availability.
|
|
1610
|
-
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
1611
|
-
*/
|
|
1612
|
-
canWrite(range) {
|
|
1613
|
-
const reading = this.isReading(range);
|
|
1614
|
-
const writing = this.isWriting(range);
|
|
1615
|
-
return !reading && !writing;
|
|
1585
|
+
readHead() {
|
|
1586
|
+
return this.snapshotHead;
|
|
1616
1587
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
* @param arg0 - Either a range or a task callback.
|
|
1621
|
-
* If a range is provided, the task is the second argument.
|
|
1622
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1623
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1624
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1625
|
-
* If this value is not provided, no timeout will be set.
|
|
1626
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1627
|
-
*/
|
|
1628
|
-
readLock(arg0, arg1, arg2) {
|
|
1629
|
-
const [range, task, timeout] = this._handleOverload(
|
|
1630
|
-
[arg0, arg1, arg2],
|
|
1631
|
-
{
|
|
1632
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
1633
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
1634
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
1635
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
1636
|
-
},
|
|
1637
|
-
{
|
|
1638
|
-
task: [Function],
|
|
1639
|
-
taskTimeout: [Function, Number],
|
|
1640
|
-
rangeTask: [Array, Function],
|
|
1641
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1642
|
-
}
|
|
1643
|
-
);
|
|
1644
|
-
return this._lock(
|
|
1645
|
-
this.readQueue,
|
|
1646
|
-
range,
|
|
1647
|
-
timeout,
|
|
1648
|
-
task,
|
|
1649
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
1650
|
-
);
|
|
1588
|
+
writeHead(head) {
|
|
1589
|
+
this.snapshotHead.root = head.root;
|
|
1590
|
+
this.snapshotHead.data = { ...head.data };
|
|
1651
1591
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
* @template T - The return type of the task.
|
|
1655
|
-
* @param arg0 - Either a range or a task callback.
|
|
1656
|
-
* If a range is provided, the task is the second argument.
|
|
1657
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1658
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1659
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1660
|
-
* If this value is not provided, no timeout will be set.
|
|
1661
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1662
|
-
*/
|
|
1663
|
-
writeLock(arg0, arg1, arg2) {
|
|
1664
|
-
const [range, task, timeout] = this._handleOverload(
|
|
1665
|
-
[arg0, arg1, arg2],
|
|
1666
|
-
{
|
|
1667
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
1668
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
1669
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
1670
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
1671
|
-
},
|
|
1672
|
-
{
|
|
1673
|
-
task: [Function],
|
|
1674
|
-
taskTimeout: [Function, Number],
|
|
1675
|
-
rangeTask: [Array, Function],
|
|
1676
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1677
|
-
}
|
|
1678
|
-
);
|
|
1679
|
-
return this._lock(
|
|
1680
|
-
this.writeQueue,
|
|
1681
|
-
range,
|
|
1682
|
-
timeout,
|
|
1683
|
-
task,
|
|
1684
|
-
() => {
|
|
1685
|
-
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
1686
|
-
}
|
|
1687
|
-
);
|
|
1592
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1593
|
+
return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
1688
1594
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
* @param lockId - The unique identifier for the lock to release.
|
|
1692
|
-
*/
|
|
1693
|
-
readUnlock(lockId) {
|
|
1694
|
-
this._free(this.readings, lockId);
|
|
1595
|
+
getHeadData(key, defaultValue) {
|
|
1596
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1695
1597
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
this._free(this.writings, lockId);
|
|
1598
|
+
setHeadData(key, data) {
|
|
1599
|
+
this.snapshotHead.data[key] = data;
|
|
1600
|
+
}
|
|
1601
|
+
autoIncrement(key, defaultValue) {
|
|
1602
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1702
1603
|
}
|
|
1703
1604
|
};
|
|
1704
1605
|
|
|
1705
|
-
// src/
|
|
1706
|
-
var
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1606
|
+
// src/transaction/BPTreeSyncTransaction.ts
|
|
1607
|
+
var BPTreeSyncTransaction = class extends BPTreeSyncBase {
|
|
1608
|
+
realBaseTree;
|
|
1609
|
+
realBaseStrategy;
|
|
1610
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
1611
|
+
dirtyIds;
|
|
1612
|
+
createdInTx;
|
|
1613
|
+
deletedIds;
|
|
1614
|
+
initialRootId;
|
|
1615
|
+
transactionRootId;
|
|
1616
|
+
constructor(baseTree) {
|
|
1617
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
1618
|
+
this.realBaseTree = baseTree;
|
|
1619
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
1620
|
+
this.order = baseTree.getOrder();
|
|
1621
|
+
this.initialRootId = "";
|
|
1622
|
+
this.transactionRootId = "";
|
|
1623
|
+
this.dirtyIds = /* @__PURE__ */ new Set();
|
|
1624
|
+
this.createdInTx = /* @__PURE__ */ new Set();
|
|
1625
|
+
this.deletedIds = /* @__PURE__ */ new Set();
|
|
1712
1626
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1627
|
+
/**
|
|
1628
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
1629
|
+
*/
|
|
1630
|
+
initTransaction() {
|
|
1631
|
+
const head = this.realBaseStrategy.readHead();
|
|
1632
|
+
if (head) {
|
|
1633
|
+
this.order = head.order;
|
|
1634
|
+
this.initialRootId = head.root;
|
|
1635
|
+
} else {
|
|
1636
|
+
this.initialRootId = this.realBaseTree.getRootId();
|
|
1637
|
+
}
|
|
1638
|
+
if (!this.initialRootId) {
|
|
1639
|
+
const root = this._createNode(true, [], [], true);
|
|
1640
|
+
this.initialRootId = root.id;
|
|
1641
|
+
}
|
|
1642
|
+
this.transactionRootId = this.initialRootId;
|
|
1643
|
+
this.rootId = this.transactionRootId;
|
|
1644
|
+
const snapshotStrategy = new BPTreeSyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
1645
|
+
this.strategy = snapshotStrategy;
|
|
1646
|
+
this.txNodes.clear();
|
|
1647
|
+
this.dirtyIds.clear();
|
|
1648
|
+
this.createdInTx.clear();
|
|
1649
|
+
this.deletedIds.clear();
|
|
1719
1650
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1651
|
+
getNode(id) {
|
|
1652
|
+
if (this.txNodes.has(id)) {
|
|
1653
|
+
return this.txNodes.get(id);
|
|
1654
|
+
}
|
|
1655
|
+
if (this.deletedIds.has(id)) {
|
|
1656
|
+
throw new Error(`The tree attempted to reference deleted node '${id}'`);
|
|
1657
|
+
}
|
|
1658
|
+
const baseNode = this.realBaseStrategy.read(id);
|
|
1659
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
1660
|
+
this.txNodes.set(id, clone);
|
|
1661
|
+
return clone;
|
|
1728
1662
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1663
|
+
bufferForNodeUpdate(node) {
|
|
1664
|
+
if (this.dirtyIds.has(node.id) && this.txNodes.has(node.id) && node._p) {
|
|
1665
|
+
this.txNodes.set(node.id, node);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
node._p = true;
|
|
1669
|
+
this.txNodes.set(node.id, node);
|
|
1670
|
+
this.dirtyIds.add(node.id);
|
|
1671
|
+
if (node.leaf) {
|
|
1672
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
1673
|
+
try {
|
|
1674
|
+
this.bufferForNodeUpdate(this.getNode(node.next));
|
|
1675
|
+
} catch (e) {
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
1679
|
+
try {
|
|
1680
|
+
this.bufferForNodeUpdate(this.getNode(node.prev));
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
this.markPathDirty(node);
|
|
1686
|
+
delete node._p;
|
|
1737
1687
|
}
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1688
|
+
bufferForNodeCreate(node) {
|
|
1689
|
+
this.txNodes.set(node.id, node);
|
|
1690
|
+
this.dirtyIds.add(node.id);
|
|
1691
|
+
this.createdInTx.add(node.id);
|
|
1692
|
+
if (node.leaf) {
|
|
1693
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
1694
|
+
try {
|
|
1695
|
+
this.bufferForNodeUpdate(this.getNode(node.next));
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
1700
|
+
try {
|
|
1701
|
+
this.bufferForNodeUpdate(this.getNode(node.prev));
|
|
1702
|
+
} catch (e) {
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
this.markPathDirty(node);
|
|
1707
|
+
}
|
|
1708
|
+
bufferForNodeDelete(node) {
|
|
1709
|
+
this.txNodes.delete(node.id);
|
|
1710
|
+
this.dirtyIds.add(node.id);
|
|
1711
|
+
this.deletedIds.add(node.id);
|
|
1712
|
+
}
|
|
1713
|
+
markPathDirty(node) {
|
|
1714
|
+
let curr = node;
|
|
1715
|
+
while (curr.parent) {
|
|
1716
|
+
if (this.deletedIds.has(curr.parent)) {
|
|
1717
|
+
break;
|
|
1718
|
+
}
|
|
1719
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
const parent = this.getNode(curr.parent);
|
|
1723
|
+
this.dirtyIds.add(parent.id);
|
|
1724
|
+
curr = parent;
|
|
1725
|
+
}
|
|
1726
|
+
if (!curr.parent) {
|
|
1727
|
+
this.transactionRootId = curr.id;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
_createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
1731
|
+
const id = this.strategy.id(isLeaf);
|
|
1732
|
+
const node = {
|
|
1733
|
+
id,
|
|
1734
|
+
keys,
|
|
1735
|
+
values,
|
|
1736
|
+
leaf,
|
|
1737
|
+
parent,
|
|
1738
|
+
next,
|
|
1739
|
+
prev
|
|
1740
|
+
};
|
|
1741
|
+
this.bufferForNodeCreate(node);
|
|
1742
|
+
return node;
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Attempts to commit the transaction.
|
|
1746
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
1747
|
+
*
|
|
1748
|
+
* @returns The transaction result.
|
|
1749
|
+
*/
|
|
1750
|
+
commit() {
|
|
1751
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
1752
|
+
const finalNodes = [];
|
|
1753
|
+
for (const oldId of this.dirtyIds) {
|
|
1754
|
+
if (this.createdInTx.has(oldId)) {
|
|
1755
|
+
idMapping.set(oldId, oldId);
|
|
1756
|
+
} else {
|
|
1757
|
+
const node = this.txNodes.get(oldId);
|
|
1758
|
+
if (node) {
|
|
1759
|
+
const newId = this.realBaseStrategy.id(node.leaf);
|
|
1760
|
+
idMapping.set(oldId, newId);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
const newCreatedIds = [];
|
|
1765
|
+
for (const oldId of this.dirtyIds) {
|
|
1766
|
+
const node = this.txNodes.get(oldId);
|
|
1767
|
+
if (!node) continue;
|
|
1768
|
+
const newId = idMapping.get(oldId);
|
|
1769
|
+
node.id = newId;
|
|
1770
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
1771
|
+
node.parent = idMapping.get(node.parent);
|
|
1772
|
+
}
|
|
1773
|
+
if (!node.leaf) {
|
|
1774
|
+
const internal = node;
|
|
1775
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
1776
|
+
const childId = internal.keys[i];
|
|
1777
|
+
if (idMapping.has(childId)) {
|
|
1778
|
+
internal.keys[i] = idMapping.get(childId);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (node.leaf) {
|
|
1783
|
+
const leaf = node;
|
|
1784
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
1785
|
+
leaf.next = idMapping.get(leaf.next);
|
|
1786
|
+
}
|
|
1787
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
1788
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
finalNodes.push(node);
|
|
1792
|
+
newCreatedIds.push(newId);
|
|
1793
|
+
}
|
|
1794
|
+
let newRootId = this.rootId;
|
|
1795
|
+
if (idMapping.has(this.rootId)) {
|
|
1796
|
+
newRootId = idMapping.get(this.rootId);
|
|
1797
|
+
}
|
|
1798
|
+
for (const node of finalNodes) {
|
|
1799
|
+
this.realBaseStrategy.write(node.id, node);
|
|
1800
|
+
}
|
|
1801
|
+
const success = this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
1802
|
+
if (success) {
|
|
1803
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
1804
|
+
for (const oldId of this.dirtyIds) {
|
|
1805
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
1806
|
+
distinctObsolete.add(oldId);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
return {
|
|
1810
|
+
success: true,
|
|
1811
|
+
createdIds: newCreatedIds,
|
|
1812
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
1813
|
+
};
|
|
1814
|
+
} else {
|
|
1815
|
+
this.rollback();
|
|
1816
|
+
return {
|
|
1817
|
+
success: false,
|
|
1818
|
+
createdIds: newCreatedIds,
|
|
1819
|
+
obsoleteIds: []
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
1825
|
+
* Internal use only.
|
|
1826
|
+
*/
|
|
1827
|
+
rollback() {
|
|
1828
|
+
this.txNodes.clear();
|
|
1829
|
+
this.dirtyIds.clear();
|
|
1830
|
+
this.createdInTx.clear();
|
|
1831
|
+
}
|
|
1832
|
+
// Override to do nothing, as transaction handles its own commits
|
|
1833
|
+
commitHeadBuffer() {
|
|
1834
|
+
}
|
|
1835
|
+
commitNodeCreateBuffer() {
|
|
1836
|
+
}
|
|
1837
|
+
commitNodeUpdateBuffer() {
|
|
1838
|
+
}
|
|
1839
|
+
commitNodeDeleteBuffer() {
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
|
|
1843
|
+
// src/BPTreeSync.ts
|
|
1844
|
+
var BPTreeSync = class extends BPTreeSyncBase {
|
|
1845
|
+
constructor(strategy, comparator, option) {
|
|
1846
|
+
super(strategy, comparator, option);
|
|
1847
|
+
this.init();
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Creates a new synchronous transaction.
|
|
1851
|
+
* @returns A new BPTreeSyncTransaction.
|
|
1852
|
+
*/
|
|
1853
|
+
createTransaction() {
|
|
1854
|
+
const tx = new BPTreeSyncTransaction(this);
|
|
1855
|
+
tx.initTransaction();
|
|
1856
|
+
return tx;
|
|
1857
|
+
}
|
|
1858
|
+
insert(key, value) {
|
|
1859
|
+
const tx = this.createTransaction();
|
|
1860
|
+
tx.insert(key, value);
|
|
1861
|
+
const { success } = tx.commit();
|
|
1862
|
+
this.init();
|
|
1863
|
+
if (!success) {
|
|
1864
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
delete(key, value) {
|
|
1868
|
+
const tx = this.createTransaction();
|
|
1869
|
+
tx.delete(key, value);
|
|
1870
|
+
const { success } = tx.commit();
|
|
1871
|
+
this.init();
|
|
1872
|
+
if (!success) {
|
|
1873
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
// src/base/BPTreeAsyncBase.ts
|
|
1879
|
+
var BPTreeAsyncBase = class extends BPTree {
|
|
1880
|
+
constructor(strategy, comparator, option) {
|
|
1881
|
+
super(strategy, comparator, option);
|
|
1882
|
+
this.nodes = this._createCachedNode();
|
|
1883
|
+
}
|
|
1884
|
+
_createCachedNode() {
|
|
1885
|
+
return new CacheEntanglementAsync(async (key) => {
|
|
1886
|
+
return await this.strategy.read(key);
|
|
1887
|
+
}, {
|
|
1888
|
+
capacity: this.option.capacity ?? 1e3
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
1892
|
+
let node = startNode;
|
|
1893
|
+
let done = false;
|
|
1741
1894
|
let hasMatched = false;
|
|
1742
1895
|
while (!done) {
|
|
1743
1896
|
if (endNode && node.id === endNode.id) {
|
|
@@ -1799,7 +1952,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1799
1952
|
}
|
|
1800
1953
|
return id;
|
|
1801
1954
|
}
|
|
1802
|
-
async _createNode(isLeaf, keys, values, leaf =
|
|
1955
|
+
async _createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
1803
1956
|
const id = await this._createNodeId(isLeaf);
|
|
1804
1957
|
const node = {
|
|
1805
1958
|
id,
|
|
@@ -1810,40 +1963,37 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1810
1963
|
next,
|
|
1811
1964
|
prev
|
|
1812
1965
|
};
|
|
1813
|
-
this.
|
|
1966
|
+
await this.bufferForNodeCreate(node);
|
|
1814
1967
|
return node;
|
|
1815
1968
|
}
|
|
1816
1969
|
async _deleteEntry(node, key, value) {
|
|
1817
1970
|
if (!node.leaf) {
|
|
1971
|
+
let keyIndex = -1;
|
|
1818
1972
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
node.keys.splice(i, 1);
|
|
1822
|
-
this.bufferForNodeUpdate(node);
|
|
1973
|
+
if (node.keys[i] === key) {
|
|
1974
|
+
keyIndex = i;
|
|
1823
1975
|
break;
|
|
1824
1976
|
}
|
|
1825
1977
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
break;
|
|
1832
|
-
}
|
|
1978
|
+
if (keyIndex !== -1) {
|
|
1979
|
+
node.keys.splice(keyIndex, 1);
|
|
1980
|
+
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
1981
|
+
node.values.splice(valueIndex, 1);
|
|
1982
|
+
await this.bufferForNodeUpdate(node);
|
|
1833
1983
|
}
|
|
1834
1984
|
}
|
|
1835
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
1985
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
1836
1986
|
const keys = node.keys;
|
|
1837
|
-
this.bufferForNodeDelete(node);
|
|
1987
|
+
await this.bufferForNodeDelete(node);
|
|
1838
1988
|
const newRoot = await this.getNode(keys[0]);
|
|
1839
1989
|
this.rootId = newRoot.id;
|
|
1840
1990
|
newRoot.parent = null;
|
|
1841
1991
|
this.strategy.head.root = this.rootId;
|
|
1842
|
-
this.bufferForNodeUpdate(newRoot);
|
|
1992
|
+
await this.bufferForNodeUpdate(newRoot);
|
|
1843
1993
|
return;
|
|
1844
1994
|
} else if (this.rootId === node.id) {
|
|
1845
1995
|
const root = await this.getNode(this.rootId);
|
|
1846
|
-
this.bufferForNodeUpdate(root);
|
|
1996
|
+
await this.bufferForNodeUpdate(root);
|
|
1847
1997
|
return;
|
|
1848
1998
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
1849
1999
|
if (node.parent === null) {
|
|
@@ -1901,33 +2051,24 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1901
2051
|
pointer.values.push(guess);
|
|
1902
2052
|
} else {
|
|
1903
2053
|
pointer.next = node.next;
|
|
1904
|
-
pointer.prev = node.id;
|
|
1905
2054
|
if (pointer.next) {
|
|
1906
|
-
const n = await this.getNode(
|
|
2055
|
+
const n = await this.getNode(pointer.next);
|
|
1907
2056
|
n.prev = pointer.id;
|
|
1908
|
-
this.bufferForNodeUpdate(n);
|
|
1909
|
-
}
|
|
1910
|
-
if (pointer.prev) {
|
|
1911
|
-
const n = await this.getNode(node.id);
|
|
1912
|
-
n.next = pointer.id;
|
|
1913
|
-
this.bufferForNodeUpdate(n);
|
|
1914
|
-
}
|
|
1915
|
-
if (isPredecessor) {
|
|
1916
|
-
pointer.prev = null;
|
|
2057
|
+
await this.bufferForNodeUpdate(n);
|
|
1917
2058
|
}
|
|
1918
2059
|
}
|
|
1919
2060
|
pointer.values.push(...node.values);
|
|
1920
2061
|
if (!pointer.leaf) {
|
|
1921
2062
|
const keys = pointer.keys;
|
|
1922
2063
|
for (const key2 of keys) {
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
this.bufferForNodeUpdate(
|
|
2064
|
+
const n = await this.getNode(key2);
|
|
2065
|
+
n.parent = pointer.id;
|
|
2066
|
+
await this.bufferForNodeUpdate(n);
|
|
1926
2067
|
}
|
|
1927
2068
|
}
|
|
1928
2069
|
await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
|
|
1929
|
-
this.bufferForNodeUpdate(pointer);
|
|
1930
|
-
this.bufferForNodeDelete(node);
|
|
2070
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2071
|
+
await this.bufferForNodeDelete(node);
|
|
1931
2072
|
} else {
|
|
1932
2073
|
if (isPredecessor) {
|
|
1933
2074
|
let pointerPm;
|
|
@@ -1938,13 +2079,10 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1938
2079
|
node.keys = [pointerPm, ...node.keys];
|
|
1939
2080
|
node.values = [guess, ...node.values];
|
|
1940
2081
|
parentNode = await this.getNode(node.parent);
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1946
|
-
break;
|
|
1947
|
-
}
|
|
2082
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2083
|
+
if (nodeIndex > 0) {
|
|
2084
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
2085
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1948
2086
|
}
|
|
1949
2087
|
} else {
|
|
1950
2088
|
pointerPm = pointer.keys.splice(-1)[0];
|
|
@@ -1952,17 +2090,14 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1952
2090
|
node.keys = [pointerPm, ...node.keys];
|
|
1953
2091
|
node.values = [pointerKm, ...node.values];
|
|
1954
2092
|
parentNode = await this.getNode(node.parent);
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1960
|
-
break;
|
|
1961
|
-
}
|
|
2093
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2094
|
+
if (nodeIndex > 0) {
|
|
2095
|
+
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
2096
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1962
2097
|
}
|
|
1963
2098
|
}
|
|
1964
|
-
this.bufferForNodeUpdate(node);
|
|
1965
|
-
this.bufferForNodeUpdate(pointer);
|
|
2099
|
+
await this.bufferForNodeUpdate(node);
|
|
2100
|
+
await this.bufferForNodeUpdate(pointer);
|
|
1966
2101
|
} else {
|
|
1967
2102
|
let pointerP0;
|
|
1968
2103
|
let pointerK0;
|
|
@@ -1972,13 +2107,10 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1972
2107
|
node.keys = [...node.keys, pointerP0];
|
|
1973
2108
|
node.values = [...node.values, guess];
|
|
1974
2109
|
parentNode = await this.getNode(node.parent);
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1980
|
-
break;
|
|
1981
|
-
}
|
|
2110
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2111
|
+
if (pointerIndex > 0) {
|
|
2112
|
+
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
2113
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1982
2114
|
}
|
|
1983
2115
|
} else {
|
|
1984
2116
|
pointerP0 = pointer.keys.splice(0, 1)[0];
|
|
@@ -1986,40 +2118,42 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1986
2118
|
node.keys = [...node.keys, pointerP0];
|
|
1987
2119
|
node.values = [...node.values, pointerK0];
|
|
1988
2120
|
parentNode = await this.getNode(node.parent);
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
this.bufferForNodeUpdate(parentNode);
|
|
1994
|
-
break;
|
|
1995
|
-
}
|
|
2121
|
+
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2122
|
+
if (pointerIndex > 0) {
|
|
2123
|
+
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
2124
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
1996
2125
|
}
|
|
1997
2126
|
}
|
|
1998
|
-
this.bufferForNodeUpdate(node);
|
|
1999
|
-
this.bufferForNodeUpdate(pointer);
|
|
2127
|
+
await this.bufferForNodeUpdate(node);
|
|
2128
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2000
2129
|
}
|
|
2001
2130
|
if (!pointer.leaf) {
|
|
2002
|
-
|
|
2131
|
+
const keys = pointer.keys;
|
|
2132
|
+
for (const key2 of keys) {
|
|
2003
2133
|
const n = await this.getNode(key2);
|
|
2004
2134
|
n.parent = pointer.id;
|
|
2005
|
-
this.bufferForNodeUpdate(n);
|
|
2135
|
+
await this.bufferForNodeUpdate(n);
|
|
2006
2136
|
}
|
|
2007
2137
|
}
|
|
2008
2138
|
if (!node.leaf) {
|
|
2009
|
-
|
|
2139
|
+
const keys = node.keys;
|
|
2140
|
+
for (const key2 of keys) {
|
|
2010
2141
|
const n = await this.getNode(key2);
|
|
2011
2142
|
n.parent = node.id;
|
|
2012
|
-
this.bufferForNodeUpdate(n);
|
|
2143
|
+
await this.bufferForNodeUpdate(n);
|
|
2013
2144
|
}
|
|
2014
2145
|
}
|
|
2015
2146
|
if (!parentNode.leaf) {
|
|
2016
|
-
|
|
2147
|
+
const keys = parentNode.keys;
|
|
2148
|
+
for (const key2 of keys) {
|
|
2017
2149
|
const n = await this.getNode(key2);
|
|
2018
2150
|
n.parent = parentNode.id;
|
|
2019
|
-
this.bufferForNodeUpdate(n);
|
|
2151
|
+
await this.bufferForNodeUpdate(n);
|
|
2020
2152
|
}
|
|
2021
2153
|
}
|
|
2022
2154
|
}
|
|
2155
|
+
} else {
|
|
2156
|
+
await this.bufferForNodeUpdate(node);
|
|
2023
2157
|
}
|
|
2024
2158
|
}
|
|
2025
2159
|
async _insertInParent(node, value, pointer) {
|
|
@@ -2030,43 +2164,39 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2030
2164
|
node.parent = root.id;
|
|
2031
2165
|
pointer.parent = root.id;
|
|
2032
2166
|
if (pointer.leaf) {
|
|
2033
|
-
|
|
2034
|
-
|
|
2167
|
+
const nNode = node;
|
|
2168
|
+
nNode.next = pointer.id;
|
|
2169
|
+
const nPointer = pointer;
|
|
2170
|
+
nPointer.prev = node.id;
|
|
2035
2171
|
}
|
|
2036
|
-
this.bufferForNodeUpdate(node);
|
|
2037
|
-
this.bufferForNodeUpdate(pointer);
|
|
2172
|
+
await this.bufferForNodeUpdate(node);
|
|
2173
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2038
2174
|
return;
|
|
2039
2175
|
}
|
|
2040
2176
|
const parentNode = await this.getNode(node.parent);
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
insertIndex = i + 1;
|
|
2045
|
-
} else {
|
|
2046
|
-
break;
|
|
2047
|
-
}
|
|
2177
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2178
|
+
if (nodeIndex === -1) {
|
|
2179
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2048
2180
|
}
|
|
2181
|
+
const insertIndex = nodeIndex;
|
|
2049
2182
|
parentNode.values.splice(insertIndex, 0, value);
|
|
2050
2183
|
parentNode.keys.splice(insertIndex + 1, 0, pointer.id);
|
|
2051
2184
|
pointer.parent = parentNode.id;
|
|
2052
2185
|
if (pointer.leaf) {
|
|
2053
|
-
const
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
this.
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
}
|
|
2068
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2069
|
-
this.bufferForNodeUpdate(pointer);
|
|
2186
|
+
const leftSibling = node;
|
|
2187
|
+
const oldNextId = leftSibling.next;
|
|
2188
|
+
pointer.prev = leftSibling.id;
|
|
2189
|
+
pointer.next = oldNextId;
|
|
2190
|
+
leftSibling.next = pointer.id;
|
|
2191
|
+
await this.bufferForNodeUpdate(leftSibling);
|
|
2192
|
+
if (oldNextId) {
|
|
2193
|
+
const oldNext = await this.getNode(oldNextId);
|
|
2194
|
+
oldNext.prev = pointer.id;
|
|
2195
|
+
await this.bufferForNodeUpdate(oldNext);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2199
|
+
await this.bufferForNodeUpdate(pointer);
|
|
2070
2200
|
if (parentNode.keys.length > this.order) {
|
|
2071
2201
|
const parentPointer = await this._createNode(false, [], []);
|
|
2072
2202
|
parentPointer.parent = parentNode.parent;
|
|
@@ -2077,20 +2207,21 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2077
2207
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2078
2208
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2079
2209
|
for (const k of parentNode.keys) {
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
this.bufferForNodeUpdate(
|
|
2210
|
+
const n = await this.getNode(k);
|
|
2211
|
+
n.parent = parentNode.id;
|
|
2212
|
+
await this.bufferForNodeUpdate(n);
|
|
2083
2213
|
}
|
|
2084
2214
|
for (const k of parentPointer.keys) {
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2087
|
-
this.bufferForNodeUpdate(
|
|
2215
|
+
const n = await this.getNode(k);
|
|
2216
|
+
n.parent = parentPointer.id;
|
|
2217
|
+
await this.bufferForNodeUpdate(n);
|
|
2088
2218
|
}
|
|
2089
2219
|
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2090
|
-
this.bufferForNodeUpdate(parentNode);
|
|
2220
|
+
await this.bufferForNodeUpdate(parentNode);
|
|
2091
2221
|
}
|
|
2092
2222
|
}
|
|
2093
2223
|
async init() {
|
|
2224
|
+
this.clear();
|
|
2094
2225
|
const head = await this.strategy.readHead();
|
|
2095
2226
|
if (head === null) {
|
|
2096
2227
|
this.order = this.strategy.order;
|
|
@@ -2121,42 +2252,72 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2121
2252
|
}
|
|
2122
2253
|
async insertableNode(value) {
|
|
2123
2254
|
let node = await this.getNode(this.rootId);
|
|
2255
|
+
if (node.parent !== null) {
|
|
2256
|
+
node.parent = null;
|
|
2257
|
+
await this.bufferForNodeUpdate(node);
|
|
2258
|
+
}
|
|
2124
2259
|
while (!node.leaf) {
|
|
2260
|
+
const parentId = node.id;
|
|
2125
2261
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2126
2262
|
const nValue = node.values[i];
|
|
2127
2263
|
const k = node.keys;
|
|
2128
2264
|
if (this.comparator.isSame(value, nValue)) {
|
|
2129
|
-
node = await this.getNode(
|
|
2265
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2266
|
+
if (node.parent !== parentId) {
|
|
2267
|
+
node.parent = parentId;
|
|
2268
|
+
await this.bufferForNodeUpdate(node);
|
|
2269
|
+
}
|
|
2130
2270
|
break;
|
|
2131
2271
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
2132
|
-
node = await this.getNode(
|
|
2272
|
+
node = await this.getNode(node.keys[i]);
|
|
2273
|
+
if (node.parent !== parentId) {
|
|
2274
|
+
node.parent = parentId;
|
|
2275
|
+
await this.bufferForNodeUpdate(node);
|
|
2276
|
+
}
|
|
2133
2277
|
break;
|
|
2134
2278
|
} else if (i + 1 === node.values.length) {
|
|
2135
|
-
node = await this.getNode(
|
|
2279
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2280
|
+
if (node.parent !== parentId) {
|
|
2281
|
+
node.parent = parentId;
|
|
2282
|
+
await this.bufferForNodeUpdate(node);
|
|
2283
|
+
}
|
|
2136
2284
|
break;
|
|
2137
2285
|
}
|
|
2138
2286
|
}
|
|
2139
2287
|
}
|
|
2140
2288
|
return node;
|
|
2141
2289
|
}
|
|
2142
|
-
/**
|
|
2143
|
-
* Find the insertable node using primaryAsc comparison.
|
|
2144
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
2145
|
-
*/
|
|
2146
2290
|
async insertableNodeByPrimary(value) {
|
|
2147
2291
|
let node = await this.getNode(this.rootId);
|
|
2292
|
+
if (node.parent !== null) {
|
|
2293
|
+
node.parent = null;
|
|
2294
|
+
await this.bufferForNodeUpdate(node);
|
|
2295
|
+
}
|
|
2148
2296
|
while (!node.leaf) {
|
|
2297
|
+
const parentId = node.id;
|
|
2149
2298
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2150
2299
|
const nValue = node.values[i];
|
|
2151
2300
|
const k = node.keys;
|
|
2152
2301
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
2153
|
-
node = await this.getNode(
|
|
2302
|
+
node = await this.getNode(node.keys[i]);
|
|
2303
|
+
if (node.parent !== parentId) {
|
|
2304
|
+
node.parent = parentId;
|
|
2305
|
+
await this.bufferForNodeUpdate(node);
|
|
2306
|
+
}
|
|
2154
2307
|
break;
|
|
2155
2308
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2156
|
-
node = await this.getNode(
|
|
2309
|
+
node = await this.getNode(node.keys[i]);
|
|
2310
|
+
if (node.parent !== parentId) {
|
|
2311
|
+
node.parent = parentId;
|
|
2312
|
+
await this.bufferForNodeUpdate(node);
|
|
2313
|
+
}
|
|
2157
2314
|
break;
|
|
2158
2315
|
} else if (i + 1 === node.values.length) {
|
|
2159
|
-
node = await this.getNode(
|
|
2316
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2317
|
+
if (node.parent !== parentId) {
|
|
2318
|
+
node.parent = parentId;
|
|
2319
|
+
await this.bufferForNodeUpdate(node);
|
|
2320
|
+
}
|
|
2160
2321
|
break;
|
|
2161
2322
|
}
|
|
2162
2323
|
}
|
|
@@ -2165,16 +2326,29 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2165
2326
|
}
|
|
2166
2327
|
async insertableRightestNodeByPrimary(value) {
|
|
2167
2328
|
let node = await this.getNode(this.rootId);
|
|
2329
|
+
if (node.parent !== null) {
|
|
2330
|
+
node.parent = null;
|
|
2331
|
+
await this.bufferForNodeUpdate(node);
|
|
2332
|
+
}
|
|
2168
2333
|
while (!node.leaf) {
|
|
2334
|
+
const parentId = node.id;
|
|
2169
2335
|
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2170
2336
|
const nValue = node.values[i];
|
|
2171
2337
|
const k = node.keys;
|
|
2172
2338
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2173
|
-
node = await this.getNode(
|
|
2339
|
+
node = await this.getNode(node.keys[i]);
|
|
2340
|
+
if (node.parent !== parentId) {
|
|
2341
|
+
node.parent = parentId;
|
|
2342
|
+
await this.bufferForNodeUpdate(node);
|
|
2343
|
+
}
|
|
2174
2344
|
break;
|
|
2175
2345
|
}
|
|
2176
2346
|
if (i + 1 === node.values.length) {
|
|
2177
|
-
node = await this.getNode(
|
|
2347
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2348
|
+
if (node.parent !== parentId) {
|
|
2349
|
+
node.parent = parentId;
|
|
2350
|
+
await this.bufferForNodeUpdate(node);
|
|
2351
|
+
}
|
|
2178
2352
|
break;
|
|
2179
2353
|
}
|
|
2180
2354
|
}
|
|
@@ -2209,20 +2383,65 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2209
2383
|
}
|
|
2210
2384
|
async leftestNode() {
|
|
2211
2385
|
let node = await this.getNode(this.rootId);
|
|
2386
|
+
if (node.parent !== null) {
|
|
2387
|
+
node.parent = null;
|
|
2388
|
+
await this.bufferForNodeUpdate(node);
|
|
2389
|
+
}
|
|
2212
2390
|
while (!node.leaf) {
|
|
2391
|
+
const parentId = node.id;
|
|
2213
2392
|
const keys = node.keys;
|
|
2214
|
-
node = await this.getNode(keys[0]);
|
|
2393
|
+
node = await this.getNode(node.keys[0]);
|
|
2394
|
+
if (node.parent !== parentId) {
|
|
2395
|
+
node.parent = parentId;
|
|
2396
|
+
await this.bufferForNodeUpdate(node);
|
|
2397
|
+
}
|
|
2215
2398
|
}
|
|
2216
2399
|
return node;
|
|
2217
2400
|
}
|
|
2218
2401
|
async rightestNode() {
|
|
2219
2402
|
let node = await this.getNode(this.rootId);
|
|
2403
|
+
if (node.parent !== null) {
|
|
2404
|
+
node.parent = null;
|
|
2405
|
+
await this.bufferForNodeUpdate(node);
|
|
2406
|
+
}
|
|
2220
2407
|
while (!node.leaf) {
|
|
2408
|
+
const parentId = node.id;
|
|
2221
2409
|
const keys = node.keys;
|
|
2222
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2410
|
+
node = await this.getNode(node.keys[node.keys.length - 1]);
|
|
2411
|
+
if (node.parent !== parentId) {
|
|
2412
|
+
node.parent = parentId;
|
|
2413
|
+
await this.bufferForNodeUpdate(node);
|
|
2414
|
+
}
|
|
2223
2415
|
}
|
|
2224
2416
|
return node;
|
|
2225
2417
|
}
|
|
2418
|
+
async exists(key, value) {
|
|
2419
|
+
const node = await this.insertableNode(value);
|
|
2420
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2421
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
2422
|
+
const keys = node.keys[i];
|
|
2423
|
+
if (keys.includes(key)) {
|
|
2424
|
+
return true;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
return false;
|
|
2429
|
+
}
|
|
2430
|
+
async forceUpdate(id) {
|
|
2431
|
+
if (id) {
|
|
2432
|
+
this.nodes.delete(id);
|
|
2433
|
+
await this.getNode(id);
|
|
2434
|
+
return 1;
|
|
2435
|
+
}
|
|
2436
|
+
const keys = Array.from(this.nodes.keys());
|
|
2437
|
+
for (const key of keys) {
|
|
2438
|
+
this.nodes.delete(key);
|
|
2439
|
+
}
|
|
2440
|
+
for (const key of keys) {
|
|
2441
|
+
await this.getNode(key);
|
|
2442
|
+
}
|
|
2443
|
+
return keys.length;
|
|
2444
|
+
}
|
|
2226
2445
|
async commitHeadBuffer() {
|
|
2227
2446
|
if (!this._strategyDirty) {
|
|
2228
2447
|
return;
|
|
@@ -2253,25 +2472,15 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2253
2472
|
}
|
|
2254
2473
|
this._nodeDeleteBuffer.clear();
|
|
2255
2474
|
}
|
|
2256
|
-
/**
|
|
2257
|
-
* Retrieves the value associated with the given key (PK).
|
|
2258
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
2259
|
-
*
|
|
2260
|
-
* @param key The key to search for.
|
|
2261
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
2262
|
-
*/
|
|
2263
2475
|
async get(key) {
|
|
2264
|
-
return this.readLock(async () => {
|
|
2476
|
+
return await this.readLock(async () => {
|
|
2265
2477
|
let node = await this.leftestNode();
|
|
2266
2478
|
while (true) {
|
|
2267
|
-
|
|
2268
|
-
const
|
|
2269
|
-
for (let
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
if (keys[j] === key) {
|
|
2273
|
-
return node.values[i];
|
|
2274
|
-
}
|
|
2479
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2480
|
+
const keys = node.keys[i];
|
|
2481
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
2482
|
+
if (keys[j] === key) {
|
|
2483
|
+
return node.values[i];
|
|
2275
2484
|
}
|
|
2276
2485
|
}
|
|
2277
2486
|
}
|
|
@@ -2346,27 +2555,23 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2346
2555
|
}
|
|
2347
2556
|
}
|
|
2348
2557
|
async keys(condition, filterValues) {
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
return set;
|
|
2355
|
-
});
|
|
2558
|
+
const set = /* @__PURE__ */ new Set();
|
|
2559
|
+
for await (const key of this.keysStream(condition, filterValues)) {
|
|
2560
|
+
set.add(key);
|
|
2561
|
+
}
|
|
2562
|
+
return set;
|
|
2356
2563
|
}
|
|
2357
2564
|
async where(condition) {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
return map;
|
|
2364
|
-
});
|
|
2565
|
+
const map = /* @__PURE__ */ new Map();
|
|
2566
|
+
for await (const [key, value] of this.whereStream(condition)) {
|
|
2567
|
+
map.set(key, value);
|
|
2568
|
+
}
|
|
2569
|
+
return map;
|
|
2365
2570
|
}
|
|
2366
2571
|
async insert(key, value) {
|
|
2367
|
-
|
|
2572
|
+
await this.writeLock(async () => {
|
|
2368
2573
|
const before = await this.insertableNode(value);
|
|
2369
|
-
this._insertAtLeaf(before, key, value);
|
|
2574
|
+
await this._insertAtLeaf(before, key, value);
|
|
2370
2575
|
if (before.values.length === this.order) {
|
|
2371
2576
|
const after = await this._createNode(
|
|
2372
2577
|
true,
|
|
@@ -2383,7 +2588,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2383
2588
|
before.values = before.values.slice(0, mid + 1);
|
|
2384
2589
|
before.keys = before.keys.slice(0, mid + 1);
|
|
2385
2590
|
await this._insertInParent(before, after.values[0], after);
|
|
2386
|
-
this.bufferForNodeUpdate(before);
|
|
2591
|
+
await this.bufferForNodeUpdate(before);
|
|
2387
2592
|
}
|
|
2388
2593
|
await this.commitHeadBuffer();
|
|
2389
2594
|
await this.commitNodeCreateBuffer();
|
|
@@ -2391,28 +2596,23 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2391
2596
|
});
|
|
2392
2597
|
}
|
|
2393
2598
|
async delete(key, value) {
|
|
2394
|
-
|
|
2599
|
+
await this.writeLock(async () => {
|
|
2395
2600
|
const node = await this.insertableNode(value);
|
|
2396
2601
|
let i = node.values.length;
|
|
2397
2602
|
while (i--) {
|
|
2398
2603
|
const nValue = node.values[i];
|
|
2399
2604
|
if (this.comparator.isSame(value, nValue)) {
|
|
2400
2605
|
const keys = node.keys[i];
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
} else if (node.id === this.rootId) {
|
|
2406
|
-
node.values.splice(i, 1);
|
|
2606
|
+
const keyIndex = keys.indexOf(key);
|
|
2607
|
+
if (keyIndex !== -1) {
|
|
2608
|
+
keys.splice(keyIndex, 1);
|
|
2609
|
+
if (keys.length === 0) {
|
|
2407
2610
|
node.keys.splice(i, 1);
|
|
2408
|
-
|
|
2409
|
-
} else {
|
|
2410
|
-
keys.splice(keys.indexOf(key), 1);
|
|
2411
|
-
node.keys.splice(i, 1);
|
|
2412
|
-
node.values.splice(node.values.indexOf(value), 1);
|
|
2413
|
-
await this._deleteEntry(node, key, value);
|
|
2414
|
-
this.bufferForNodeUpdate(node);
|
|
2611
|
+
node.values.splice(i, 1);
|
|
2415
2612
|
}
|
|
2613
|
+
await this._deleteEntry(node, key, value);
|
|
2614
|
+
await this.bufferForNodeUpdate(node);
|
|
2615
|
+
break;
|
|
2416
2616
|
}
|
|
2417
2617
|
}
|
|
2418
2618
|
}
|
|
@@ -2422,157 +2622,408 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2422
2622
|
await this.commitNodeDeleteBuffer();
|
|
2423
2623
|
});
|
|
2424
2624
|
}
|
|
2425
|
-
|
|
2426
|
-
return this.
|
|
2427
|
-
const node = await this.insertableNode(value);
|
|
2428
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2429
|
-
const nValue = node.values[i];
|
|
2430
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2431
|
-
const keys = node.keys[i];
|
|
2432
|
-
return keys.includes(key);
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
return false;
|
|
2436
|
-
});
|
|
2625
|
+
getHeadData() {
|
|
2626
|
+
return this.strategy.head.data;
|
|
2437
2627
|
}
|
|
2438
2628
|
async setHeadData(data) {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
this._strategyDirty = true;
|
|
2442
|
-
await this.commitHeadBuffer();
|
|
2443
|
-
});
|
|
2444
|
-
}
|
|
2445
|
-
async forceUpdate() {
|
|
2446
|
-
return this.readLock(async () => {
|
|
2447
|
-
const keys = [...this.nodes.keys()];
|
|
2448
|
-
this.nodes.clear();
|
|
2449
|
-
await this.init();
|
|
2450
|
-
for (const key of keys) {
|
|
2451
|
-
await this.getNode(key);
|
|
2452
|
-
}
|
|
2453
|
-
return keys.length;
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
};
|
|
2457
|
-
|
|
2458
|
-
// src/base/SerializeStrategy.ts
|
|
2459
|
-
var SerializeStrategy = class {
|
|
2460
|
-
order;
|
|
2461
|
-
head;
|
|
2462
|
-
constructor(order) {
|
|
2463
|
-
this.order = order;
|
|
2464
|
-
this.head = {
|
|
2465
|
-
order,
|
|
2466
|
-
root: null,
|
|
2467
|
-
data: {}
|
|
2468
|
-
};
|
|
2629
|
+
this.strategy.head.data = data;
|
|
2630
|
+
await this.strategy.writeHead(this.strategy.head);
|
|
2469
2631
|
}
|
|
2470
2632
|
};
|
|
2471
2633
|
|
|
2472
|
-
// src/
|
|
2473
|
-
var
|
|
2474
|
-
getHeadData(key, defaultValue) {
|
|
2634
|
+
// src/SerializeStrategyAsync.ts
|
|
2635
|
+
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
2636
|
+
async getHeadData(key, defaultValue) {
|
|
2475
2637
|
if (!Object.hasOwn(this.head.data, key)) {
|
|
2476
|
-
this.setHeadData(key, defaultValue);
|
|
2638
|
+
await this.setHeadData(key, defaultValue);
|
|
2477
2639
|
}
|
|
2478
2640
|
return this.head.data[key];
|
|
2479
2641
|
}
|
|
2480
|
-
setHeadData(key, data) {
|
|
2642
|
+
async setHeadData(key, data) {
|
|
2481
2643
|
this.head.data[key] = data;
|
|
2482
|
-
this.writeHead(this.head);
|
|
2644
|
+
await this.writeHead(this.head);
|
|
2483
2645
|
}
|
|
2484
|
-
autoIncrement(key, defaultValue) {
|
|
2485
|
-
const current = this.getHeadData(key, defaultValue);
|
|
2646
|
+
async autoIncrement(key, defaultValue) {
|
|
2647
|
+
const current = await this.getHeadData(key, defaultValue);
|
|
2486
2648
|
const next = current + 1;
|
|
2487
|
-
this.setHeadData(key, next);
|
|
2649
|
+
await this.setHeadData(key, next);
|
|
2488
2650
|
return current;
|
|
2489
2651
|
}
|
|
2652
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2653
|
+
if (this.head.root !== oldRoot) {
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2656
|
+
this.head.root = newRoot;
|
|
2657
|
+
await this.writeHead(this.head);
|
|
2658
|
+
return true;
|
|
2659
|
+
}
|
|
2490
2660
|
};
|
|
2491
|
-
var
|
|
2661
|
+
var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
|
|
2492
2662
|
node;
|
|
2493
2663
|
constructor(order) {
|
|
2494
2664
|
super(order);
|
|
2495
2665
|
this.node = {};
|
|
2496
2666
|
}
|
|
2497
|
-
id(isLeaf) {
|
|
2498
|
-
return this.autoIncrement("index", 1).toString();
|
|
2667
|
+
async id(isLeaf) {
|
|
2668
|
+
return (await this.autoIncrement("index", 1)).toString();
|
|
2499
2669
|
}
|
|
2500
|
-
read(id) {
|
|
2670
|
+
async read(id) {
|
|
2501
2671
|
if (!Object.hasOwn(this.node, id)) {
|
|
2502
2672
|
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
2503
2673
|
}
|
|
2504
|
-
|
|
2674
|
+
const node = this.node[id];
|
|
2675
|
+
return JSON.parse(JSON.stringify(node));
|
|
2505
2676
|
}
|
|
2506
|
-
write(id, node) {
|
|
2677
|
+
async write(id, node) {
|
|
2507
2678
|
this.node[id] = node;
|
|
2508
2679
|
}
|
|
2509
|
-
delete(id) {
|
|
2680
|
+
async delete(id) {
|
|
2510
2681
|
delete this.node[id];
|
|
2511
2682
|
}
|
|
2512
|
-
readHead() {
|
|
2683
|
+
async readHead() {
|
|
2513
2684
|
if (this.head.root === null) {
|
|
2514
2685
|
return null;
|
|
2515
2686
|
}
|
|
2516
2687
|
return this.head;
|
|
2517
2688
|
}
|
|
2518
|
-
writeHead(head) {
|
|
2689
|
+
async writeHead(head) {
|
|
2519
2690
|
this.head = head;
|
|
2520
2691
|
}
|
|
2521
2692
|
};
|
|
2522
2693
|
|
|
2523
|
-
// src/
|
|
2524
|
-
var
|
|
2694
|
+
// src/transaction/BPTreeAsyncSnapshotStrategy.ts
|
|
2695
|
+
var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
|
|
2696
|
+
baseStrategy;
|
|
2697
|
+
snapshotHead;
|
|
2698
|
+
constructor(baseStrategy, root) {
|
|
2699
|
+
super(baseStrategy.order);
|
|
2700
|
+
this.baseStrategy = baseStrategy;
|
|
2701
|
+
this.snapshotHead = {
|
|
2702
|
+
...baseStrategy.head,
|
|
2703
|
+
root,
|
|
2704
|
+
data: { ...baseStrategy.head.data }
|
|
2705
|
+
};
|
|
2706
|
+
this.head = this.snapshotHead;
|
|
2707
|
+
}
|
|
2708
|
+
async id(isLeaf) {
|
|
2709
|
+
return await this.baseStrategy.id(isLeaf);
|
|
2710
|
+
}
|
|
2711
|
+
async read(id) {
|
|
2712
|
+
return await this.baseStrategy.read(id);
|
|
2713
|
+
}
|
|
2714
|
+
async write(id, node) {
|
|
2715
|
+
await this.baseStrategy.write(id, node);
|
|
2716
|
+
}
|
|
2717
|
+
async delete(id) {
|
|
2718
|
+
await this.baseStrategy.delete(id);
|
|
2719
|
+
}
|
|
2720
|
+
async readHead() {
|
|
2721
|
+
return this.snapshotHead;
|
|
2722
|
+
}
|
|
2723
|
+
async writeHead(head) {
|
|
2724
|
+
this.snapshotHead.root = head.root;
|
|
2725
|
+
this.snapshotHead.data = { ...head.data };
|
|
2726
|
+
}
|
|
2727
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2728
|
+
return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
2729
|
+
}
|
|
2525
2730
|
async getHeadData(key, defaultValue) {
|
|
2526
|
-
|
|
2527
|
-
await this.setHeadData(key, defaultValue);
|
|
2528
|
-
}
|
|
2529
|
-
return this.head.data[key];
|
|
2731
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2530
2732
|
}
|
|
2531
2733
|
async setHeadData(key, data) {
|
|
2532
|
-
this.
|
|
2533
|
-
await this.writeHead(this.head);
|
|
2734
|
+
this.snapshotHead.data[key] = data;
|
|
2534
2735
|
}
|
|
2535
2736
|
async autoIncrement(key, defaultValue) {
|
|
2536
|
-
|
|
2537
|
-
const next = current + 1;
|
|
2538
|
-
await this.setHeadData(key, next);
|
|
2539
|
-
return current;
|
|
2737
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2540
2738
|
}
|
|
2541
2739
|
};
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2740
|
+
|
|
2741
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2742
|
+
var BPTreeAsyncTransaction = class extends BPTreeAsyncBase {
|
|
2743
|
+
realBaseTree;
|
|
2744
|
+
realBaseStrategy;
|
|
2745
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
2746
|
+
dirtyIds;
|
|
2747
|
+
createdInTx;
|
|
2748
|
+
deletedIds;
|
|
2749
|
+
initialRootId;
|
|
2750
|
+
transactionRootId;
|
|
2751
|
+
constructor(baseTree) {
|
|
2752
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
2753
|
+
this.realBaseTree = baseTree;
|
|
2754
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
2755
|
+
this.order = baseTree.getOrder();
|
|
2756
|
+
this.initialRootId = "";
|
|
2757
|
+
this.transactionRootId = "";
|
|
2758
|
+
this.dirtyIds = /* @__PURE__ */ new Set();
|
|
2759
|
+
this.createdInTx = /* @__PURE__ */ new Set();
|
|
2760
|
+
this.deletedIds = /* @__PURE__ */ new Set();
|
|
2547
2761
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2762
|
+
/**
|
|
2763
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
2764
|
+
*/
|
|
2765
|
+
async initTransaction() {
|
|
2766
|
+
const head = await this.realBaseStrategy.readHead();
|
|
2767
|
+
if (head) {
|
|
2768
|
+
this.order = head.order;
|
|
2769
|
+
this.initialRootId = head.root;
|
|
2770
|
+
} else {
|
|
2771
|
+
this.initialRootId = this.realBaseTree.getRootId();
|
|
2772
|
+
}
|
|
2773
|
+
if (!this.initialRootId) {
|
|
2774
|
+
const root = await this._createNode(true, [], [], true);
|
|
2775
|
+
this.initialRootId = root.id;
|
|
2776
|
+
}
|
|
2777
|
+
this.transactionRootId = this.initialRootId;
|
|
2778
|
+
this.rootId = this.transactionRootId;
|
|
2779
|
+
const snapshotStrategy = new BPTreeAsyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
2780
|
+
this.strategy = snapshotStrategy;
|
|
2781
|
+
this.txNodes.clear();
|
|
2782
|
+
this.dirtyIds.clear();
|
|
2783
|
+
this.createdInTx.clear();
|
|
2784
|
+
this.deletedIds.clear();
|
|
2550
2785
|
}
|
|
2551
|
-
async
|
|
2552
|
-
if (
|
|
2553
|
-
|
|
2786
|
+
async getNode(id) {
|
|
2787
|
+
if (this.txNodes.has(id)) {
|
|
2788
|
+
return this.txNodes.get(id);
|
|
2789
|
+
}
|
|
2790
|
+
if (this.deletedIds.has(id)) {
|
|
2791
|
+
throw new Error(`The tree attempted to reference deleted node '${id}'`);
|
|
2554
2792
|
}
|
|
2555
|
-
|
|
2793
|
+
const baseNode = await this.realBaseStrategy.read(id);
|
|
2794
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
2795
|
+
this.txNodes.set(id, clone);
|
|
2796
|
+
return clone;
|
|
2556
2797
|
}
|
|
2557
|
-
async
|
|
2558
|
-
this.node
|
|
2798
|
+
async bufferForNodeUpdate(node) {
|
|
2799
|
+
if (this.dirtyIds.has(node.id) && this.txNodes.has(node.id) && node._p) {
|
|
2800
|
+
this.txNodes.set(node.id, node);
|
|
2801
|
+
return;
|
|
2802
|
+
}
|
|
2803
|
+
node._p = true;
|
|
2804
|
+
this.txNodes.set(node.id, node);
|
|
2805
|
+
this.dirtyIds.add(node.id);
|
|
2806
|
+
if (node.leaf) {
|
|
2807
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
2808
|
+
try {
|
|
2809
|
+
await this.bufferForNodeUpdate(await this.getNode(node.next));
|
|
2810
|
+
} catch (e) {
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
2814
|
+
try {
|
|
2815
|
+
await this.bufferForNodeUpdate(await this.getNode(node.prev));
|
|
2816
|
+
} catch (e) {
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
await this.markPathDirty(node);
|
|
2821
|
+
delete node._p;
|
|
2559
2822
|
}
|
|
2560
|
-
async
|
|
2561
|
-
|
|
2823
|
+
async bufferForNodeCreate(node) {
|
|
2824
|
+
this.txNodes.set(node.id, node);
|
|
2825
|
+
this.dirtyIds.add(node.id);
|
|
2826
|
+
this.createdInTx.add(node.id);
|
|
2827
|
+
if (node.leaf) {
|
|
2828
|
+
if (node.next && !this.dirtyIds.has(node.next) && !this.deletedIds.has(node.next)) {
|
|
2829
|
+
try {
|
|
2830
|
+
await this.bufferForNodeUpdate(await this.getNode(node.next));
|
|
2831
|
+
} catch (e) {
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (node.prev && !this.dirtyIds.has(node.prev) && !this.deletedIds.has(node.prev)) {
|
|
2835
|
+
try {
|
|
2836
|
+
await this.bufferForNodeUpdate(await this.getNode(node.prev));
|
|
2837
|
+
} catch (e) {
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
await this.markPathDirty(node);
|
|
2562
2842
|
}
|
|
2563
|
-
async
|
|
2564
|
-
|
|
2565
|
-
|
|
2843
|
+
async bufferForNodeDelete(node) {
|
|
2844
|
+
this.txNodes.delete(node.id);
|
|
2845
|
+
this.dirtyIds.add(node.id);
|
|
2846
|
+
this.deletedIds.add(node.id);
|
|
2847
|
+
}
|
|
2848
|
+
async markPathDirty(node) {
|
|
2849
|
+
let curr = node;
|
|
2850
|
+
while (curr.parent) {
|
|
2851
|
+
if (this.deletedIds.has(curr.parent)) {
|
|
2852
|
+
break;
|
|
2853
|
+
}
|
|
2854
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
2855
|
+
break;
|
|
2856
|
+
}
|
|
2857
|
+
const parent = await this.getNode(curr.parent);
|
|
2858
|
+
this.dirtyIds.add(parent.id);
|
|
2859
|
+
curr = parent;
|
|
2860
|
+
}
|
|
2861
|
+
if (!curr.parent) {
|
|
2862
|
+
this.transactionRootId = curr.id;
|
|
2566
2863
|
}
|
|
2567
|
-
return this.head;
|
|
2568
2864
|
}
|
|
2569
|
-
async
|
|
2570
|
-
|
|
2865
|
+
async _createNode(isLeaf, keys, values, leaf = isLeaf, parent = null, next = null, prev = null) {
|
|
2866
|
+
const id = await this.strategy.id(isLeaf);
|
|
2867
|
+
const node = {
|
|
2868
|
+
id,
|
|
2869
|
+
keys,
|
|
2870
|
+
values,
|
|
2871
|
+
leaf,
|
|
2872
|
+
parent,
|
|
2873
|
+
next,
|
|
2874
|
+
prev
|
|
2875
|
+
};
|
|
2876
|
+
await this.bufferForNodeCreate(node);
|
|
2877
|
+
return node;
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Attempts to commit the transaction.
|
|
2881
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
2882
|
+
*
|
|
2883
|
+
* @returns A promise that resolves to the transaction result.
|
|
2884
|
+
*/
|
|
2885
|
+
async commit() {
|
|
2886
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
2887
|
+
const finalNodes = [];
|
|
2888
|
+
for (const oldId of this.dirtyIds) {
|
|
2889
|
+
if (this.createdInTx.has(oldId)) {
|
|
2890
|
+
idMapping.set(oldId, oldId);
|
|
2891
|
+
} else {
|
|
2892
|
+
const node = this.txNodes.get(oldId);
|
|
2893
|
+
if (node) {
|
|
2894
|
+
const newId = await this.realBaseStrategy.id(node.leaf);
|
|
2895
|
+
idMapping.set(oldId, newId);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
const newCreatedIds = [];
|
|
2900
|
+
for (const oldId of this.dirtyIds) {
|
|
2901
|
+
const node = this.txNodes.get(oldId);
|
|
2902
|
+
if (!node) continue;
|
|
2903
|
+
const newId = idMapping.get(oldId);
|
|
2904
|
+
node.id = newId;
|
|
2905
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
2906
|
+
node.parent = idMapping.get(node.parent);
|
|
2907
|
+
}
|
|
2908
|
+
if (!node.leaf) {
|
|
2909
|
+
const internal = node;
|
|
2910
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
2911
|
+
const childId = internal.keys[i];
|
|
2912
|
+
if (idMapping.has(childId)) {
|
|
2913
|
+
internal.keys[i] = idMapping.get(childId);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
if (node.leaf) {
|
|
2918
|
+
const leaf = node;
|
|
2919
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
2920
|
+
leaf.next = idMapping.get(leaf.next);
|
|
2921
|
+
}
|
|
2922
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
2923
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
finalNodes.push(node);
|
|
2927
|
+
newCreatedIds.push(newId);
|
|
2928
|
+
}
|
|
2929
|
+
let newRootId = this.rootId;
|
|
2930
|
+
if (idMapping.has(this.rootId)) {
|
|
2931
|
+
newRootId = idMapping.get(this.rootId);
|
|
2932
|
+
}
|
|
2933
|
+
for (const node of finalNodes) {
|
|
2934
|
+
await this.realBaseStrategy.write(node.id, node);
|
|
2935
|
+
}
|
|
2936
|
+
const success = await this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
2937
|
+
if (success) {
|
|
2938
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
2939
|
+
for (const oldId of this.dirtyIds) {
|
|
2940
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
2941
|
+
distinctObsolete.add(oldId);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
return {
|
|
2945
|
+
success: true,
|
|
2946
|
+
createdIds: newCreatedIds,
|
|
2947
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
2948
|
+
};
|
|
2949
|
+
} else {
|
|
2950
|
+
await this.rollback();
|
|
2951
|
+
return {
|
|
2952
|
+
success: false,
|
|
2953
|
+
createdIds: newCreatedIds,
|
|
2954
|
+
obsoleteIds: []
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
2960
|
+
* Internal use only.
|
|
2961
|
+
*/
|
|
2962
|
+
async rollback() {
|
|
2963
|
+
this.txNodes.clear();
|
|
2964
|
+
this.dirtyIds.clear();
|
|
2965
|
+
this.createdInTx.clear();
|
|
2966
|
+
}
|
|
2967
|
+
async readLock(fn) {
|
|
2968
|
+
return await fn();
|
|
2969
|
+
}
|
|
2970
|
+
async writeLock(fn) {
|
|
2971
|
+
return await fn();
|
|
2972
|
+
}
|
|
2973
|
+
async commitHeadBuffer() {
|
|
2974
|
+
}
|
|
2975
|
+
async commitNodeCreateBuffer() {
|
|
2976
|
+
}
|
|
2977
|
+
async commitNodeUpdateBuffer() {
|
|
2978
|
+
}
|
|
2979
|
+
async commitNodeDeleteBuffer() {
|
|
2980
|
+
}
|
|
2981
|
+
};
|
|
2982
|
+
|
|
2983
|
+
// src/BPTreeAsync.ts
|
|
2984
|
+
var BPTreeAsync = class extends BPTreeAsyncBase {
|
|
2985
|
+
constructor(strategy, comparator, option) {
|
|
2986
|
+
super(strategy, comparator, option);
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Creates a new asynchronous transaction.
|
|
2990
|
+
* @returns A promise that resolves to a new BPTreeAsyncTransaction.
|
|
2991
|
+
*/
|
|
2992
|
+
async createTransaction() {
|
|
2993
|
+
const tx = new BPTreeAsyncTransaction(this);
|
|
2994
|
+
await tx.initTransaction();
|
|
2995
|
+
return tx;
|
|
2996
|
+
}
|
|
2997
|
+
async insert(key, value) {
|
|
2998
|
+
const tx = await this.createTransaction();
|
|
2999
|
+
await tx.insert(key, value);
|
|
3000
|
+
const { success } = await tx.commit();
|
|
3001
|
+
await this.init();
|
|
3002
|
+
if (!success) {
|
|
3003
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
async delete(key, value) {
|
|
3007
|
+
const tx = await this.createTransaction();
|
|
3008
|
+
await tx.delete(key, value);
|
|
3009
|
+
const { success } = await tx.commit();
|
|
3010
|
+
await this.init();
|
|
3011
|
+
if (!success) {
|
|
3012
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
async readLock(fn) {
|
|
3016
|
+
return await fn();
|
|
3017
|
+
}
|
|
3018
|
+
async writeLock(fn) {
|
|
3019
|
+
return await fn();
|
|
2571
3020
|
}
|
|
2572
3021
|
};
|
|
2573
3022
|
export {
|
|
2574
3023
|
BPTreeAsync,
|
|
3024
|
+
BPTreeAsyncTransaction,
|
|
2575
3025
|
BPTreeSync,
|
|
3026
|
+
BPTreeSyncTransaction,
|
|
2576
3027
|
InMemoryStoreStrategyAsync,
|
|
2577
3028
|
InMemoryStoreStrategySync,
|
|
2578
3029
|
NumericComparator,
|