btree-core 3.2.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/LICENSE +23 -0
- package/b+tree.d.ts +429 -0
- package/b+tree.js +1545 -0
- package/b+tree.min.js +1 -0
- package/extended/bulkLoad.d.ts +14 -0
- package/extended/bulkLoad.js +113 -0
- package/extended/bulkLoad.min.js +1 -0
- package/extended/decompose.d.ts +1 -0
- package/extended/decompose.js +680 -0
- package/extended/decompose.min.js +1 -0
- package/extended/diffAgainst.d.ts +23 -0
- package/extended/diffAgainst.js +254 -0
- package/extended/diffAgainst.min.js +1 -0
- package/extended/forEachKeyInBoth.d.ts +19 -0
- package/extended/forEachKeyInBoth.js +73 -0
- package/extended/forEachKeyInBoth.min.js +1 -0
- package/extended/forEachKeyNotIn.d.ts +18 -0
- package/extended/forEachKeyNotIn.js +87 -0
- package/extended/forEachKeyNotIn.min.js +1 -0
- package/extended/index.d.ts +133 -0
- package/extended/index.js +200 -0
- package/extended/index.min.js +1 -0
- package/extended/intersect.d.ts +16 -0
- package/extended/intersect.js +44 -0
- package/extended/intersect.min.js +1 -0
- package/extended/parallelWalk.d.ts +1 -0
- package/extended/parallelWalk.js +188 -0
- package/extended/parallelWalk.min.js +1 -0
- package/extended/shared.d.ts +1 -0
- package/extended/shared.js +64 -0
- package/extended/shared.min.js +1 -0
- package/extended/subtract.d.ts +16 -0
- package/extended/subtract.js +35 -0
- package/extended/subtract.min.js +1 -0
- package/extended/union.d.ts +16 -0
- package/extended/union.js +36 -0
- package/extended/union.min.js +1 -0
- package/interfaces.d.ts +307 -0
- package/package.json +122 -0
- package/readme.md +420 -0
- package/sorted-array.d.ts +22 -0
- package/sorted-array.js +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.buildFromDecomposition=exports.decompose=void 0;var b_tree_1=require("../b+tree"),shared_1=require("./shared"),parallelWalk_1=require("./parallelWalk"),decomposeLoadFactor=.7;function decompose(e,a,n,r){void 0===r&&(r=!1);var i=e._maxNodeSize,s=e._compare;(0,b_tree_1.check)(0<e._root.size()&&0<a._root.size(),"decompose requires non-empty inputs");function t(e){var a=e.keys.length<K?-1:0;_.push(a),k.push(e)}function u(e,a){(0,shared_1.makeLeavesFrom)(x,I,i,decomposeLoadFactor,t),x.length=0,I.length=0,e.isLeaf&&e.keys.length<K?(_.push(-1),k.push(e.clone())):(e.isShared=!0,_.push(a),k.push(e)),L<a&&(b=_.length-1,L=a)}function f(){void 0!==S&&(u(S.node,S.height),S=void 0)}function h(e,a){for(var n=e.spine,r=a;0<=r;--r){var i=n[r].payload;if(i.disqualified)break;i.disqualified=!0}}function l(){return{disqualified:!1}}function o(e,a,n){for(var r=e.keys,i=e.values,t=a;t<n;++t)x.push(r[t]),I.push(i[t])}function d(e,a,n,r,i){(0,b_tree_1.check)(!0===a.disqualified,"onMoveInLeaf: leaf must be disqualified"),(n=i?n+1:n)<r&&o(e,n,r)}function p(e,a,n,r,i){S=void 0,a.disqualified?(r=r?n+1:n)<(n=e.keys.length)&&o(e,r,n):(S={node:e,height:0},0===i.spine.length&&f())}function c(e,a,n,r,i,t,l){var s=e.children,o=a-1;if(t!=t||t===Number.POSITIVE_INFINITY)if(n.disqualified){f();for(var d=s.length,p=r+1;p<d;++p)u(s[p],o)}else if(t===Number.POSITIVE_INFINITY){if(e.keys.length<K)for(var p=r;p<s.length;++p)u(s[p],o);else u(e,a);S=void 0}else S={node:e,height:a};else{0<t&&h(l,i),f();for(p=r+1;p<t;++p)u(s[p],o)}}function v(e,a,n,r,i){if(0<r){h(i,n);for(var t=e.children,l=a-1,s=0;s<r;++s)u(t[s],l)}}function g(e,a,n,r){(0<a||(0,b_tree_1.areOverlapping)(e.minKey(),e.maxKey(),(0,parallelWalk_1.getKey)(r),r.leaf.maxKey(),s))&&(n.leafPayload.disqualified=!0,r.leafPayload.disqualified=!0,h(n,n.spine.length-1),h(r,r.spine.length-1),o(e,0,a))}var y,m,_=[],k=[],x=[],I=[],b=-1,L=-1,S=void 0,K=Math.floor(i/2),W=e._root.maxKey(),F=a._root.maxKey(),N=0<=s(W,F)?W:F,O=(0,parallelWalk_1.createCursor)(e,l,g,d,p,c,v),e=function(e,a){var n=(0,parallelWalk_1.getKey)(e),r=(0,parallelWalk_1.getKey)(a),i=a.leaf.maxKey();(0,b_tree_1.areOverlapping)(n,e.leaf.maxKey(),r,i,s)&&(e.leafPayload.disqualified=!0);for(var t=0;t<e.spine.length;++t){var l=e.spine[t];(0,b_tree_1.areOverlapping)(n,l.node.maxKey(),r,i,s)&&(l.payload.disqualified=!0)}};e(O,m=r?(y={disqualified:!0},(0,parallelWalk_1.createCursor)(a,function(){return y},function(e,a,n,r){(0<a||(0,b_tree_1.areOverlapping)(e.minKey(),e.maxKey(),(0,parallelWalk_1.getKey)(r),r.leaf.maxKey(),s))&&(r.leafPayload.disqualified=!0,h(r,r.spine.length-1))},parallelWalk_1.noop,parallelWalk_1.noop,function(e,a,n,r,i,t,l){0<t&&h(l,i)},function(e,a,n,r,i){0<r&&h(i,n)})):(0,parallelWalk_1.createCursor)(a,l,g,d,p,c,v)),e(m,O);for(var q=O,M=m,R=s((0,parallelWalk_1.getKey)(q),(0,parallelWalk_1.getKey)(M));;){var z=0===R;if(z){var w=(0,parallelWalk_1.getKey)(q),C=n(w,O.leaf.values[O.leafIndex],m.leaf.values[m.leafIndex]);void 0!==C&&(x.push(w),I.push(C));w=(0,parallelWalk_1.moveForwardOne)(M,q),C=(0,parallelWalk_1.moveForwardOne)(q,M);if(w||C){w&&C||(w?(0,parallelWalk_1.moveTo)(q,M,N,!1,!1):(0,parallelWalk_1.moveTo)(M,q,N,!1,!1));break}R=s((0,parallelWalk_1.getKey)(q),(0,parallelWalk_1.getKey)(M))}else{R<0&&(E=M,M=q,q=E);var w=(0,parallelWalk_1.moveTo)(M,q,(0,parallelWalk_1.getKey)(q),!0,z),E=w[0],w=w[1];if(E){(0,parallelWalk_1.moveTo)(q,M,N,!1,z);break}R=w?0:-1}}return(0,shared_1.makeLeavesFrom)(x,I,i,decomposeLoadFactor,t),L<0&&0<_.length&&(b=0),{heights:_,nodes:k,tallestIndex:b}}function buildFromDecomposition(e,a,n,r,i){var t=n.heights,l=n.nodes,s=n.tallestIndex;(0,b_tree_1.check)(t.length===l.length,"Decompose result has mismatched heights and nodes.");var o=t.length,d=[l[s]],n={branchingFactor:a,spine:d,sideIndex:getRightmostIndex,sideInsertionIndex:getRightInsertionIndex,splitOffSide:splitOffRightSide,balanceLeaves:balanceLeavesRight,updateMax:updateRightMax,mergeLeaves:mergeRightEntries};s+1<=o-1&&(updateFrontier(n,0),processSide(t,l,s+1,o,1,n));a={branchingFactor:a,spine:d,sideIndex:getLeftmostIndex,sideInsertionIndex:getLeftmostIndex,splitOffSide:splitOffLeftSide,balanceLeaves:balanceLeavesLeft,updateMax:parallelWalk_1.noop,mergeLeaves:mergeLeftEntries};0<=s-1&&(updateFrontier(a,0),processSide(t,l,s-1,-1,-1,a));i=new e(void 0,r,i);return i._root=d[0],i}function processSide(e,a,n,r,i,t){for(var l=t.spine,s=t.sideIndex,o=0,d=l[0];!d.isShared&&o<l.length-1;)o++,d=d.children[s(d)];for(var p=new Array(l.length).fill(0),u=n;u!=r;u+=i){var f=l.length-1,h=a[u],c=e[u],v=-1===c;(0,b_tree_1.check)(c<=f,"Subtree taller than spine during reconstruction.");var g=f-(c+1);ensureNotShared(t,o,g);var y=void 0,m=void 0;v?((0,b_tree_1.check)(!0!==h.isShared),y=m=h.keys.length):(y=1,m=h.size());f=findSplitCascadeEndDepth(t,g,y),c=Math.max(0,f);updateSizeAndMax(t,p,o,c);y=void 0,f=void 0,f=v?(y=splitUpwardsAndInsertEntries(t,g,h),g-1):(y=splitUpwardsAndInsert(t,g,h)[0],g);y&&(l[0]=y,p.push(0),f++),o=f+1,p[f]+=m,updateFrontier(t,c),(0,b_tree_1.check)(o===l.length-1||!0===l[o].isShared,"Non-leaf subtrees must be shared."),(0,b_tree_1.check)(p.length===l.length,"Unflushed sizes length mismatch after root split.")}updateSizeAndMax(t,p,o,0)}function splitUpwardsAndInsert(e,a,n){var r=e.spine,i=e.branchingFactor,t=e.sideIndex,l=e.sideInsertionIndex,s=e.splitOffSide,o=e.updateMax;if(0<=a){var d=void 0,e=r[a];e.keys.length===i&&(e=d=s(e));for(var p=a-1;d&&0<=p;){var u=r[p],f=t(u);o(u,u.children[f].maxKey()),d=u.keys.length<i?void insertNoCount(u,l(u),d):(insertNoCount(u=s(u),l(u),d),u),p--}var h=void 0;return void 0!==d&&(c=r[0],insertNoCount(h=new b_tree_1.BNodeInternal([c],c.size()+d.size()),l(h),d)),insertNoCount(e,l(e),n),[h,e]}var c=r[0];return insertNoCount(h=new b_tree_1.BNodeInternal([c],c.size()),l(h),n),[h,h]}function splitUpwardsAndInsertEntries(e,a,n){var r=e.branchingFactor,i=e.spine,t=e.balanceLeaves,l=e.mergeLeaves,s=n.keys.length,i=i[a];if(!(i.keys.length+s<=r)){e=splitUpwardsAndInsert(e,a-1,n),a=e[0];return t(e[1],n,Math.floor(r/2)-s),a}l(i,n)}function ensureNotShared(e,a,n){var r=e.spine,i=e.sideIndex;if(!(n<0)){0===a&&(e=r[0],r[0]=e.clone());for(var t=Math.max(a,1);t<=n;t++){var l=r[t-1],s=i(l),o=l.children[s].clone();l.children[s]=o,r[t]=o}}}function updateSizeAndMax(e,a,n,r){for(var i=e.spine,t=e.updateMax,l=i[n].maxKey(),s=n-1;r<=s;s--){var o=a[s];(a[s]=0)<s&&(a[s-1]+=o);var d=i[s];d._size+=o,t(d,l)}}function updateFrontier(e,a){var n=e.spine,r=e.sideIndex;(0,b_tree_1.check)(n.length>a,"updateFrontier: depthLastValid exceeds frontier height");e=n[a];if(!e.isLeaf){for(var i=e.children[r(e)],t=a+1;!i.isLeaf;){var l=i,i=(n[t]=l).children[r(l)];t++}n[t]=i}}function findSplitCascadeEndDepth(e,a,n){var r=e.spine,i=e.branchingFactor;if(0<=a){var t=a;if(r[t].keys.length+n<=i)return t;for(t--;0<=t;){if(r[t].keys.length<i)return t;t--}}return-1}function insertNoCount(e,a,n){e.children.splice(a,0,n),e.keys.splice(a,0,n.maxKey())}function getLeftmostIndex(){return 0}function getRightmostIndex(e){return e.children.length-1}function getRightInsertionIndex(e){return e.children.length}function splitOffRightSide(e){return e.splitOffRightSide()}function splitOffLeftSide(e){return e.splitOffLeftSide()}function balanceLeavesRight(e,a,n){var r=e.children.length-2,i=e.children[r],t=i.keys.length-n,n=i.keys.splice(t),t=i.values.splice(t);a.keys.unshift.apply(a.keys,n),a.values.unshift.apply(a.values,t),e.keys[r]=i.maxKey()}function balanceLeavesLeft(e,a,n){var r=e.children[1],i=r.keys.splice(0,n),n=r.values.splice(0,n);a.keys.push.apply(a.keys,i),a.values.push.apply(a.values,n),e.keys[0]=a.maxKey()}function updateRightMax(e,a){e.keys[e.keys.length-1]=a}function mergeRightEntries(e,a){e.keys.push.apply(e.keys,a.keys),e.values.push.apply(e.values,a.values)}function mergeLeftEntries(e,a){e.keys.unshift.apply(e.keys,a.keys),e.values.unshift.apply(e.values,a.values)}exports.decompose=decompose,exports.buildFromDecomposition=buildFromDecomposition;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import BTree from '../b+tree';
|
|
2
|
+
/**
|
|
3
|
+
* Computes the differences between `treeA` and `treeB`.
|
|
4
|
+
* For efficiency, the diff is returned via invocations of supplied handlers.
|
|
5
|
+
* The computation is optimized for the case in which the two trees have large amounts of shared data
|
|
6
|
+
* (obtained by calling the `clone` or `with` APIs) and will avoid any iteration of shared state.
|
|
7
|
+
* The handlers can cause computation to early exit by returning `{ break: R }`.
|
|
8
|
+
* Neither collection should be mutated during the comparison (inside your callbacks), as this method assumes they remain stable.
|
|
9
|
+
* @param treeA The tree whose differences will be reported via the callbacks.
|
|
10
|
+
* @param treeB The tree to compute a diff against.
|
|
11
|
+
* @param onlyA Callback invoked for all keys only present in `treeA`.
|
|
12
|
+
* @param onlyB Callback invoked for all keys only present in `treeB`.
|
|
13
|
+
* @param different Callback invoked for all keys with differing values.
|
|
14
|
+
* @returns The first `break` payload returned by a handler, or `undefined` if no handler breaks.
|
|
15
|
+
* @throws Error if the supplied trees were created with different comparators.
|
|
16
|
+
*/
|
|
17
|
+
export default function diffAgainst<K, V, R>(_treeA: BTree<K, V>, _treeB: BTree<K, V>, onlyA?: (k: K, v: V) => {
|
|
18
|
+
break?: R;
|
|
19
|
+
} | void, onlyB?: (k: K, v: V) => {
|
|
20
|
+
break?: R;
|
|
21
|
+
} | void, different?: (k: K, vThis: V, vOther: V) => {
|
|
22
|
+
break?: R;
|
|
23
|
+
} | void): R | undefined;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var b_tree_1 = require("../b+tree");
|
|
4
|
+
/**
|
|
5
|
+
* Computes the differences between `treeA` and `treeB`.
|
|
6
|
+
* For efficiency, the diff is returned via invocations of supplied handlers.
|
|
7
|
+
* The computation is optimized for the case in which the two trees have large amounts of shared data
|
|
8
|
+
* (obtained by calling the `clone` or `with` APIs) and will avoid any iteration of shared state.
|
|
9
|
+
* The handlers can cause computation to early exit by returning `{ break: R }`.
|
|
10
|
+
* Neither collection should be mutated during the comparison (inside your callbacks), as this method assumes they remain stable.
|
|
11
|
+
* @param treeA The tree whose differences will be reported via the callbacks.
|
|
12
|
+
* @param treeB The tree to compute a diff against.
|
|
13
|
+
* @param onlyA Callback invoked for all keys only present in `treeA`.
|
|
14
|
+
* @param onlyB Callback invoked for all keys only present in `treeB`.
|
|
15
|
+
* @param different Callback invoked for all keys with differing values.
|
|
16
|
+
* @returns The first `break` payload returned by a handler, or `undefined` if no handler breaks.
|
|
17
|
+
* @throws Error if the supplied trees were created with different comparators.
|
|
18
|
+
*/
|
|
19
|
+
function diffAgainst(_treeA, _treeB, onlyA, onlyB, different) {
|
|
20
|
+
var treeA = _treeA;
|
|
21
|
+
var treeB = _treeB;
|
|
22
|
+
if (treeB._compare !== treeA._compare) {
|
|
23
|
+
throw new Error('Tree comparators are not the same.');
|
|
24
|
+
}
|
|
25
|
+
if (treeA.isEmpty || treeB.isEmpty) {
|
|
26
|
+
if (_treeA.isEmpty && treeB.isEmpty)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (treeA.isEmpty) {
|
|
29
|
+
return onlyB === undefined
|
|
30
|
+
? undefined
|
|
31
|
+
: stepToEnd(makeDiffCursor(treeB), onlyB);
|
|
32
|
+
}
|
|
33
|
+
return onlyA === undefined
|
|
34
|
+
? undefined
|
|
35
|
+
: stepToEnd(makeDiffCursor(treeA), onlyA);
|
|
36
|
+
}
|
|
37
|
+
// Cursor-based diff algorithm is as follows:
|
|
38
|
+
// - Until neither cursor has navigated to the end of the tree, do the following:
|
|
39
|
+
// - If the `treeThis` cursor is "behind" the `treeOther` cursor (strictly <, via compare), advance it.
|
|
40
|
+
// - Otherwise, advance the `treeOther` cursor.
|
|
41
|
+
// - Any time a cursor is stepped, perform the following:
|
|
42
|
+
// - If either cursor points to a key/value pair:
|
|
43
|
+
// - If thisCursor === otherCursor and the values differ, it is a Different.
|
|
44
|
+
// - If thisCursor > otherCursor and otherCursor is at a key/value pair, it is an OnlyB.
|
|
45
|
+
// - If thisCursor < otherCursor and thisCursor is at a key/value pair, it is an OnlyA as long as the most recent
|
|
46
|
+
// cursor step was *not* otherCursor advancing from a tie. The extra condition avoids erroneous OnlyB calls
|
|
47
|
+
// that would occur due to otherCursor being the "leader".
|
|
48
|
+
// - Otherwise, if both cursors point to nodes, compare them. If they are equal by reference (shared), skip
|
|
49
|
+
// both cursors to the next node in the walk.
|
|
50
|
+
// - Once one cursor has finished stepping, any remaining steps (if any) are taken and key/value pairs are logged
|
|
51
|
+
// as OnlyB (if otherCursor is stepping) or OnlyA (if thisCursor is stepping).
|
|
52
|
+
// This algorithm gives the critical guarantee that all locations (both nodes and key/value pairs) in both trees that
|
|
53
|
+
// are identical by value (and possibly by reference) will be visited *at the same time* by the cursors.
|
|
54
|
+
// This removes the possibility of emitting incorrect diffs, as well as allowing for skipping shared nodes.
|
|
55
|
+
var compareKeys = treeA._compare;
|
|
56
|
+
var thisCursor = makeDiffCursor(treeA);
|
|
57
|
+
var otherCursor = makeDiffCursor(treeB);
|
|
58
|
+
var thisSuccess = true;
|
|
59
|
+
var otherSuccess = true;
|
|
60
|
+
// It doesn't matter how thisSteppedLast is initialized.
|
|
61
|
+
// Step order is only used when either cursor is at a leaf, and cursors always start at a node.
|
|
62
|
+
var prevCursorOrder = compareDiffCursors(thisCursor, otherCursor, compareKeys);
|
|
63
|
+
while (thisSuccess && otherSuccess) {
|
|
64
|
+
var cursorOrder = compareDiffCursors(thisCursor, otherCursor, compareKeys);
|
|
65
|
+
var thisLeaf = thisCursor.leaf, thisInternalSpine = thisCursor.internalSpine, thisLevelIndices = thisCursor.levelIndices;
|
|
66
|
+
var otherLeaf = otherCursor.leaf, otherInternalSpine = otherCursor.internalSpine, otherLevelIndices = otherCursor.levelIndices;
|
|
67
|
+
if (thisLeaf || otherLeaf) {
|
|
68
|
+
// If the cursors were at the same location last step, then there is no work to be done.
|
|
69
|
+
if (prevCursorOrder !== 0) {
|
|
70
|
+
if (cursorOrder === 0) {
|
|
71
|
+
if (thisLeaf && otherLeaf && different) {
|
|
72
|
+
// Equal keys, check for modifications
|
|
73
|
+
var valThis = thisLeaf.values[thisLevelIndices[thisLevelIndices.length - 1]];
|
|
74
|
+
var valOther = otherLeaf.values[otherLevelIndices[otherLevelIndices.length - 1]];
|
|
75
|
+
if (!Object.is(valThis, valOther)) {
|
|
76
|
+
var result = different(thisCursor.currentKey, valThis, valOther);
|
|
77
|
+
if (result && result.break)
|
|
78
|
+
return result.break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (cursorOrder > 0) {
|
|
83
|
+
// If this is the case, we know that either:
|
|
84
|
+
// 1. otherCursor stepped last from a starting position that trailed thisCursor, and is still behind, or
|
|
85
|
+
// 2. thisCursor stepped last and leapfrogged otherCursor
|
|
86
|
+
// Either of these cases is an "only other"
|
|
87
|
+
if (otherLeaf && onlyB) {
|
|
88
|
+
var otherVal = otherLeaf.values[otherLevelIndices[otherLevelIndices.length - 1]];
|
|
89
|
+
var result = onlyB(otherCursor.currentKey, otherVal);
|
|
90
|
+
if (result && result.break)
|
|
91
|
+
return result.break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (onlyA) {
|
|
95
|
+
if (thisLeaf && prevCursorOrder !== 0) {
|
|
96
|
+
var valThis = thisLeaf.values[thisLevelIndices[thisLevelIndices.length - 1]];
|
|
97
|
+
var result = onlyA(thisCursor.currentKey, valThis);
|
|
98
|
+
if (result && result.break)
|
|
99
|
+
return result.break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (!thisLeaf && !otherLeaf && cursorOrder === 0) {
|
|
105
|
+
var lastThis = thisInternalSpine.length - 1;
|
|
106
|
+
var lastOther = otherInternalSpine.length - 1;
|
|
107
|
+
var nodeThis = thisInternalSpine[lastThis][thisLevelIndices[lastThis]];
|
|
108
|
+
var nodeOther = otherInternalSpine[lastOther][otherLevelIndices[lastOther]];
|
|
109
|
+
if (nodeOther === nodeThis) {
|
|
110
|
+
prevCursorOrder = 0;
|
|
111
|
+
thisSuccess = stepDiffCursor(thisCursor, true);
|
|
112
|
+
otherSuccess = stepDiffCursor(otherCursor, true);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
prevCursorOrder = cursorOrder;
|
|
117
|
+
if (cursorOrder < 0) {
|
|
118
|
+
thisSuccess = stepDiffCursor(thisCursor);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
otherSuccess = stepDiffCursor(otherCursor);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (thisSuccess && onlyA)
|
|
125
|
+
return finishCursorWalk(thisCursor, otherCursor, compareKeys, onlyA);
|
|
126
|
+
if (otherSuccess && onlyB)
|
|
127
|
+
return finishCursorWalk(otherCursor, thisCursor, compareKeys, onlyB);
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
exports.default = diffAgainst;
|
|
131
|
+
/**
|
|
132
|
+
* Finishes walking `cursor` once the other cursor has already completed its walk.
|
|
133
|
+
*/
|
|
134
|
+
function finishCursorWalk(cursor, cursorFinished, compareKeys, callback) {
|
|
135
|
+
var compared = compareDiffCursors(cursor, cursorFinished, compareKeys);
|
|
136
|
+
if (compared === 0) {
|
|
137
|
+
if (!stepDiffCursor(cursor))
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
else if (compared < 0) {
|
|
141
|
+
(0, b_tree_1.check)(false, 'cursor walk terminated early');
|
|
142
|
+
}
|
|
143
|
+
return stepToEnd(cursor, callback);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Walks the cursor to the end of the tree, invoking the callback for each key/value pair.
|
|
147
|
+
*/
|
|
148
|
+
function stepToEnd(cursor, callback) {
|
|
149
|
+
var canStep = true;
|
|
150
|
+
while (canStep) {
|
|
151
|
+
var leaf = cursor.leaf, levelIndices = cursor.levelIndices, currentKey = cursor.currentKey;
|
|
152
|
+
if (leaf) {
|
|
153
|
+
var value = leaf.values[levelIndices[levelIndices.length - 1]];
|
|
154
|
+
var result = callback(currentKey, value);
|
|
155
|
+
if (result && result.break)
|
|
156
|
+
return result.break;
|
|
157
|
+
}
|
|
158
|
+
canStep = stepDiffCursor(cursor);
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
function makeDiffCursor(internal) {
|
|
163
|
+
var root = internal._root;
|
|
164
|
+
return {
|
|
165
|
+
height: internal.height,
|
|
166
|
+
internalSpine: [[root]],
|
|
167
|
+
levelIndices: [0],
|
|
168
|
+
leaf: undefined,
|
|
169
|
+
currentKey: root.maxKey()
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Advances the cursor to the next step in the walk of its tree.
|
|
174
|
+
* Cursors are walked backwards in sort order, as this allows them to leverage maxKey() in order to be compared in O(1).
|
|
175
|
+
*/
|
|
176
|
+
function stepDiffCursor(cursor, stepToNode) {
|
|
177
|
+
var internalSpine = cursor.internalSpine, levelIndices = cursor.levelIndices, leaf = cursor.leaf;
|
|
178
|
+
if (stepToNode === true || leaf) {
|
|
179
|
+
var levelsLength = levelIndices.length;
|
|
180
|
+
// Step to the next node only if:
|
|
181
|
+
// - We are explicitly directed to via stepToNode, or
|
|
182
|
+
// - There are no key/value pairs left to step to in this leaf
|
|
183
|
+
if (stepToNode === true || levelIndices[levelsLength - 1] === 0) {
|
|
184
|
+
var spineLength = internalSpine.length;
|
|
185
|
+
if (spineLength === 0)
|
|
186
|
+
return false;
|
|
187
|
+
// Walk back up the tree until we find a new subtree to descend into
|
|
188
|
+
var nodeLevelIndex = spineLength - 1;
|
|
189
|
+
var levelIndexWalkBack = nodeLevelIndex;
|
|
190
|
+
while (levelIndexWalkBack >= 0) {
|
|
191
|
+
if (levelIndices[levelIndexWalkBack] > 0) {
|
|
192
|
+
if (levelIndexWalkBack < levelsLength - 1) {
|
|
193
|
+
// Remove leaf state from cursor
|
|
194
|
+
cursor.leaf = undefined;
|
|
195
|
+
levelIndices.pop();
|
|
196
|
+
}
|
|
197
|
+
// If we walked upwards past any internal node, slice them out
|
|
198
|
+
if (levelIndexWalkBack < nodeLevelIndex)
|
|
199
|
+
cursor.internalSpine = internalSpine.slice(0, levelIndexWalkBack + 1);
|
|
200
|
+
cursor.currentKey = internalSpine[levelIndexWalkBack][--levelIndices[levelIndexWalkBack]].maxKey();
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
levelIndexWalkBack--;
|
|
204
|
+
}
|
|
205
|
+
// Cursor is in the far left leaf of the tree, no more nodes to enumerate
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Move to new leaf value
|
|
210
|
+
var valueIndex = --levelIndices[levelsLength - 1];
|
|
211
|
+
cursor.currentKey = leaf.keys[valueIndex];
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else { // Cursor does not point to a value in a leaf, so move downwards
|
|
216
|
+
var nextLevel = internalSpine.length;
|
|
217
|
+
var currentLevel = nextLevel - 1;
|
|
218
|
+
var node = internalSpine[currentLevel][levelIndices[currentLevel]];
|
|
219
|
+
if (node.isLeaf) {
|
|
220
|
+
cursor.leaf = node;
|
|
221
|
+
var valueIndex = (levelIndices[nextLevel] = node.values.length - 1);
|
|
222
|
+
cursor.currentKey = node.keys[valueIndex];
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
var children = node.children;
|
|
226
|
+
internalSpine[nextLevel] = children;
|
|
227
|
+
var childIndex = children.length - 1;
|
|
228
|
+
levelIndices[nextLevel] = childIndex;
|
|
229
|
+
cursor.currentKey = children[childIndex].maxKey();
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Compares two cursors and returns which cursor is ahead in the traversal.
|
|
236
|
+
* Note that cursors advance in reverse sort order.
|
|
237
|
+
*/
|
|
238
|
+
function compareDiffCursors(cursorA, cursorB, compareKeys) {
|
|
239
|
+
var heightA = cursorA.height, currentKeyA = cursorA.currentKey, levelIndicesA = cursorA.levelIndices;
|
|
240
|
+
var heightB = cursorB.height, currentKeyB = cursorB.currentKey, levelIndicesB = cursorB.levelIndices;
|
|
241
|
+
// Reverse the comparison order, as cursors are advanced in reverse sorting order
|
|
242
|
+
var keyComparison = compareKeys(currentKeyB, currentKeyA);
|
|
243
|
+
if (keyComparison !== 0)
|
|
244
|
+
return keyComparison;
|
|
245
|
+
// Normalize depth values relative to the shortest tree.
|
|
246
|
+
// This ensures that concurrent cursor walks of trees of differing heights can reliably land on shared nodes at the same time.
|
|
247
|
+
// To accomplish this, a cursor that is on an internal node at depth D1 with maxKey X is considered "behind" a cursor on an
|
|
248
|
+
// internal node at depth D2 with maxKey Y, when D1 < D2. Thus, always walking the cursor that is "behind" will allow the cursor
|
|
249
|
+
// at shallower depth (but equal maxKey) to "catch up" and land on shared nodes.
|
|
250
|
+
var heightMin = heightA < heightB ? heightA : heightB;
|
|
251
|
+
var depthANormalized = levelIndicesA.length - (heightA - heightMin);
|
|
252
|
+
var depthBNormalized = levelIndicesB.length - (heightB - heightMin);
|
|
253
|
+
return depthANormalized - depthBNormalized;
|
|
254
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var b_tree_1=require("../b+tree");function diffAgainst(e,r,i,t,n){var f=e,r=r;if(r._compare!==f._compare)throw new Error("Tree comparators are not the same.");if(f.isEmpty||r.isEmpty)return e.isEmpty&&r.isEmpty?void 0:f.isEmpty?void 0===t?void 0:stepToEnd(makeDiffCursor(r),t):void 0===i?void 0:stepToEnd(makeDiffCursor(f),i);for(var s=f._compare,a=makeDiffCursor(f),u=makeDiffCursor(r),o=!0,l=!0,c=compareDiffCursors(a,u,s);o&&l;){var v=compareDiffCursors(a,u,s),p=a.leaf,h=a.internalSpine,d=a.levelIndices,m=u.leaf,y=u.internalSpine,g=u.levelIndices;if(p||m){if(0!==c)if(0===v){if(p&&m&&n){var k=p.values[d[d.length-1]],C=m.values[g[g.length-1]];if(!Object.is(k,C))if((D=n(a.currentKey,k,C))&&D.break)return D.break}}else if(0<v){if(m&&t){C=m.values[g[g.length-1]];if((D=t(u.currentKey,C))&&D.break)return D.break}}else if(i&&p&&0!==c){var D,k=p.values[d[d.length-1]];if((D=i(a.currentKey,k))&&D.break)return D.break}}else if(!p&&!m&&0===v){p=h.length-1,m=y.length-1,p=h[p][d[p]];if(y[m][g[m]]===p){o=stepDiffCursor(a,!(c=0)),l=stepDiffCursor(u,!0);continue}}(c=v)<0?o=stepDiffCursor(a):l=stepDiffCursor(u)}return o&&i?finishCursorWalk(a,u,s,i):l&&t?finishCursorWalk(u,a,s,t):void 0}function finishCursorWalk(e,r,i,t){i=compareDiffCursors(e,r,i);if(0===i){if(!stepDiffCursor(e))return}else i<0&&(0,b_tree_1.check)(!1,"cursor walk terminated early");return stepToEnd(e,t)}function stepToEnd(e,r){for(var i=!0;i;){var t=e.leaf,n=e.levelIndices,f=e.currentKey;if(t){n=r(f,t.values[n[n.length-1]]);if(n&&n.break)return n.break}i=stepDiffCursor(e)}}function makeDiffCursor(e){var r=e._root;return{height:e.height,internalSpine:[[r]],levelIndices:[0],leaf:void 0,currentKey:r.maxKey()}}function stepDiffCursor(e,r){var i=e.internalSpine,t=e.levelIndices,n=e.leaf;if(!0===r||n){var f=t.length;if(!0===r||0===t[f-1]){var s=i.length;if(0===s)return!1;for(var a=s-1,u=a;0<=u;){if(0<t[u])return u<f-1&&(e.leaf=void 0,t.pop()),u<a&&(e.internalSpine=i.slice(0,u+1)),e.currentKey=i[u][--t[u]].maxKey(),!0;u--}return!1}var o=--t[f-1];return e.currentKey=n.keys[o],!0}s=i.length,n=s-1,n=i[n][t[n]];return n.isLeaf?(e.leaf=n,o=t[s]=n.values.length-1,e.currentKey=n.keys[o]):(o=n.children,n=(i[s]=o).length-1,t[s]=n,e.currentKey=o[n].maxKey()),!0}function compareDiffCursors(e,r,i){var t=e.height,n=e.currentKey,f=e.levelIndices,s=r.height,e=r.currentKey,r=r.levelIndices,n=i(e,n);if(0!==n)return n;n=t<s?t:s;return f.length-(t-n)-(r.length-(s-n))}exports.default=diffAgainst;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import BTree from '../b+tree';
|
|
2
|
+
/**
|
|
3
|
+
* Calls the supplied `callback` for each key/value pair shared by both trees, in sorted key order.
|
|
4
|
+
* Neither tree is modified.
|
|
5
|
+
*
|
|
6
|
+
* Complexity is O(N + M) when the trees overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
7
|
+
* where `D` is the number of disjoint key ranges between the trees, because whole non-intersecting subtrees
|
|
8
|
+
* are skipped.
|
|
9
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
10
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
11
|
+
* @param treeA First tree to compare.
|
|
12
|
+
* @param treeB Second tree to compare.
|
|
13
|
+
* @param callback Invoked for keys that appear in both trees. It can cause iteration to early exit by returning `{ break: R }`.
|
|
14
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if the walk finishes.
|
|
15
|
+
* @throws Error if the trees were built with different comparators.
|
|
16
|
+
*/
|
|
17
|
+
export default function forEachKeyInBoth<K, V, R = void>(treeA: BTree<K, V>, treeB: BTree<K, V>, callback: (key: K, leftValue: V, rightValue: V) => {
|
|
18
|
+
break?: R;
|
|
19
|
+
} | void): R | undefined;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var shared_1 = require("./shared");
|
|
4
|
+
var parallelWalk_1 = require("./parallelWalk");
|
|
5
|
+
/**
|
|
6
|
+
* Calls the supplied `callback` for each key/value pair shared by both trees, in sorted key order.
|
|
7
|
+
* Neither tree is modified.
|
|
8
|
+
*
|
|
9
|
+
* Complexity is O(N + M) when the trees overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
10
|
+
* where `D` is the number of disjoint key ranges between the trees, because whole non-intersecting subtrees
|
|
11
|
+
* are skipped.
|
|
12
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
13
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
14
|
+
* @param treeA First tree to compare.
|
|
15
|
+
* @param treeB Second tree to compare.
|
|
16
|
+
* @param callback Invoked for keys that appear in both trees. It can cause iteration to early exit by returning `{ break: R }`.
|
|
17
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if the walk finishes.
|
|
18
|
+
* @throws Error if the trees were built with different comparators.
|
|
19
|
+
*/
|
|
20
|
+
function forEachKeyInBoth(treeA, treeB, callback) {
|
|
21
|
+
var _treeA = treeA;
|
|
22
|
+
var _treeB = treeB;
|
|
23
|
+
(0, shared_1.checkCanDoSetOperation)(_treeA, _treeB, true);
|
|
24
|
+
if (treeB.size === 0 || treeA.size === 0)
|
|
25
|
+
return;
|
|
26
|
+
var cmp = treeA._compare;
|
|
27
|
+
var makePayload = function () { return undefined; };
|
|
28
|
+
var cursorA = (0, parallelWalk_1.createCursor)(_treeA, makePayload, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop);
|
|
29
|
+
var cursorB = (0, parallelWalk_1.createCursor)(_treeB, makePayload, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop);
|
|
30
|
+
var leading = cursorA;
|
|
31
|
+
var trailing = cursorB;
|
|
32
|
+
var order = cmp((0, parallelWalk_1.getKey)(leading), (0, parallelWalk_1.getKey)(trailing));
|
|
33
|
+
// This walk is somewhat similar to a merge walk in that it does an alternating hop walk with cursors.
|
|
34
|
+
// However, the only thing we care about is when the two cursors are equal (equality is intersection).
|
|
35
|
+
// When they are not equal we just advance the trailing cursor.
|
|
36
|
+
while (true) {
|
|
37
|
+
var areEqual = order === 0;
|
|
38
|
+
if (areEqual) {
|
|
39
|
+
var key = (0, parallelWalk_1.getKey)(leading);
|
|
40
|
+
var vA = cursorA.leaf.values[cursorA.leafIndex];
|
|
41
|
+
var vB = cursorB.leaf.values[cursorB.leafIndex];
|
|
42
|
+
var result = callback(key, vA, vB);
|
|
43
|
+
if (result && result.break) {
|
|
44
|
+
return result.break;
|
|
45
|
+
}
|
|
46
|
+
var outT = (0, parallelWalk_1.moveForwardOne)(trailing, leading);
|
|
47
|
+
var outL = (0, parallelWalk_1.moveForwardOne)(leading, trailing);
|
|
48
|
+
if (outT && outL)
|
|
49
|
+
break;
|
|
50
|
+
order = cmp((0, parallelWalk_1.getKey)(leading), (0, parallelWalk_1.getKey)(trailing));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
if (order < 0) {
|
|
54
|
+
var tmp = trailing;
|
|
55
|
+
trailing = leading;
|
|
56
|
+
leading = tmp;
|
|
57
|
+
}
|
|
58
|
+
// At this point, leading is guaranteed to be ahead of trailing.
|
|
59
|
+
var _a = (0, parallelWalk_1.moveTo)(trailing, leading, (0, parallelWalk_1.getKey)(leading), true, areEqual), out = _a[0], nowEqual = _a[1];
|
|
60
|
+
if (out) {
|
|
61
|
+
// We've reached the end of one tree, so intersections are guaranteed to be done.
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
else if (nowEqual) {
|
|
65
|
+
order = 0;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
order = -1; // trailing is ahead of leading
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.default = forEachKeyInBoth;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var shared_1=require("./shared"),parallelWalk_1=require("./parallelWalk");function forEachKeyInBoth(l,a,e){var r=l,o=a;if((0,shared_1.checkCanDoSetOperation)(r,o,!0),0!==a.size&&0!==l.size)for(var p=l._compare,l=function(){},k=(0,parallelWalk_1.createCursor)(r,l,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop),_=(0,parallelWalk_1.createCursor)(o,l,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop),n=k,W=_,t=p((0,parallelWalk_1.getKey)(n),(0,parallelWalk_1.getKey)(W));;){var f=0===t;if(f){var s=e((0,parallelWalk_1.getKey)(n),k.leaf.values[k.leafIndex],_.leaf.values[_.leafIndex]);if(s&&s.break)return s.break;var i=(0,parallelWalk_1.moveForwardOne)(W,n),s=(0,parallelWalk_1.moveForwardOne)(n,W);if(i&&s)break;t=p((0,parallelWalk_1.getKey)(n),(0,parallelWalk_1.getKey)(W))}else{t<0&&(u=W,W=n,n=u);var u=(0,parallelWalk_1.moveTo)(W,n,(0,parallelWalk_1.getKey)(n),!0,f),f=u[0],u=u[1];if(f)break;t=u?0:-1}}}exports.default=forEachKeyInBoth;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BTree from '../b+tree';
|
|
2
|
+
/**
|
|
3
|
+
* Calls the supplied `callback` for each key/value pair that is in `includeTree` but not in `excludeTree`
|
|
4
|
+
* (set subtraction). The callback runs in sorted key order and neither tree is modified.
|
|
5
|
+
*
|
|
6
|
+
* Complexity is O(N + M) when the key ranges overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
7
|
+
* where `D` is the number of disjoint ranges between the trees, because non-overlapping subtrees are skipped.
|
|
8
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
9
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
10
|
+
* @param includeTree The tree to iterate keys from.
|
|
11
|
+
* @param excludeTree Keys present in this tree are omitted from the callback.
|
|
12
|
+
* @param callback Invoked for keys that are in `includeTree` but not `excludeTree`. It can cause iteration to early exit by returning `{ break: R }`.
|
|
13
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if all qualifying keys are visited.
|
|
14
|
+
* @throws Error if the trees were built with different comparators.
|
|
15
|
+
*/
|
|
16
|
+
export default function forEachKeyNotIn<K, V, R = void>(includeTree: BTree<K, V>, excludeTree: BTree<K, V>, callback: (key: K, value: V) => {
|
|
17
|
+
break?: R;
|
|
18
|
+
} | void): R | undefined;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var shared_1 = require("./shared");
|
|
4
|
+
var parallelWalk_1 = require("./parallelWalk");
|
|
5
|
+
/**
|
|
6
|
+
* Calls the supplied `callback` for each key/value pair that is in `includeTree` but not in `excludeTree`
|
|
7
|
+
* (set subtraction). The callback runs in sorted key order and neither tree is modified.
|
|
8
|
+
*
|
|
9
|
+
* Complexity is O(N + M) when the key ranges overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
10
|
+
* where `D` is the number of disjoint ranges between the trees, because non-overlapping subtrees are skipped.
|
|
11
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
12
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
13
|
+
* @param includeTree The tree to iterate keys from.
|
|
14
|
+
* @param excludeTree Keys present in this tree are omitted from the callback.
|
|
15
|
+
* @param callback Invoked for keys that are in `includeTree` but not `excludeTree`. It can cause iteration to early exit by returning `{ break: R }`.
|
|
16
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if all qualifying keys are visited.
|
|
17
|
+
* @throws Error if the trees were built with different comparators.
|
|
18
|
+
*/
|
|
19
|
+
function forEachKeyNotIn(includeTree, excludeTree, callback) {
|
|
20
|
+
var _includeTree = includeTree;
|
|
21
|
+
var _excludeTree = excludeTree;
|
|
22
|
+
(0, shared_1.checkCanDoSetOperation)(_includeTree, _excludeTree, true);
|
|
23
|
+
if (includeTree.size === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
var finishWalk = function () {
|
|
27
|
+
var out = false;
|
|
28
|
+
do {
|
|
29
|
+
var key = (0, parallelWalk_1.getKey)(cursorInclude);
|
|
30
|
+
var value = cursorInclude.leaf.values[cursorInclude.leafIndex];
|
|
31
|
+
var result = callback(key, value);
|
|
32
|
+
if (result && result.break) {
|
|
33
|
+
return result.break;
|
|
34
|
+
}
|
|
35
|
+
out = (0, parallelWalk_1.moveForwardOne)(cursorInclude, cursorExclude);
|
|
36
|
+
} while (!out);
|
|
37
|
+
return undefined;
|
|
38
|
+
};
|
|
39
|
+
var cmp = includeTree._compare;
|
|
40
|
+
var makePayload = function () { return undefined; };
|
|
41
|
+
var cursorInclude = (0, parallelWalk_1.createCursor)(_includeTree, makePayload, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop);
|
|
42
|
+
if (excludeTree.size === 0) {
|
|
43
|
+
return finishWalk();
|
|
44
|
+
}
|
|
45
|
+
var cursorExclude = (0, parallelWalk_1.createCursor)(_excludeTree, makePayload, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop, parallelWalk_1.noop);
|
|
46
|
+
var order = cmp((0, parallelWalk_1.getKey)(cursorInclude), (0, parallelWalk_1.getKey)(cursorExclude));
|
|
47
|
+
while (true) {
|
|
48
|
+
var areEqual = order === 0;
|
|
49
|
+
if (areEqual) {
|
|
50
|
+
// Keys are equal, so this key is in both trees and should be skipped.
|
|
51
|
+
var outInclude = (0, parallelWalk_1.moveForwardOne)(cursorInclude, cursorExclude);
|
|
52
|
+
if (outInclude)
|
|
53
|
+
break;
|
|
54
|
+
order = 1; // include is now ahead of exclude
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (order < 0) {
|
|
58
|
+
var key = (0, parallelWalk_1.getKey)(cursorInclude);
|
|
59
|
+
var value = cursorInclude.leaf.values[cursorInclude.leafIndex];
|
|
60
|
+
var result = callback(key, value);
|
|
61
|
+
if (result && result.break) {
|
|
62
|
+
return result.break;
|
|
63
|
+
}
|
|
64
|
+
var outInclude = (0, parallelWalk_1.moveForwardOne)(cursorInclude, cursorExclude);
|
|
65
|
+
if (outInclude) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
order = cmp((0, parallelWalk_1.getKey)(cursorInclude), (0, parallelWalk_1.getKey)(cursorExclude));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// At this point, include is guaranteed to be ahead of exclude.
|
|
72
|
+
var _a = (0, parallelWalk_1.moveTo)(cursorExclude, cursorInclude, (0, parallelWalk_1.getKey)(cursorInclude), true, areEqual), out = _a[0], nowEqual = _a[1];
|
|
73
|
+
if (out) {
|
|
74
|
+
// We've reached the end of exclude, so call for all remaining keys in include
|
|
75
|
+
return finishWalk();
|
|
76
|
+
}
|
|
77
|
+
else if (nowEqual) {
|
|
78
|
+
order = 0;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
order = -1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.default = forEachKeyNotIn;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var shared_1=require("./shared"),parallelWalk_1=require("./parallelWalk");function forEachKeyNotIn(l,a,e){var r=l,o=a;if((0,shared_1.checkCanDoSetOperation)(r,o,!0),0!==l.size){var p=function(){do{var l=(0,parallelWalk_1.getKey)(n),a=n.leaf.values[n.leafIndex],a=e(l,a);if(a&&a.break)return a.break}while(!(0,parallelWalk_1.moveForwardOne)(n,_))},k=l._compare,l=function(){},n=(0,parallelWalk_1.createCursor)(r,l,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop);if(0===a.size)return p();for(var _=(0,parallelWalk_1.createCursor)(o,l,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop,parallelWalk_1.noop),t=k((0,parallelWalk_1.getKey)(n),(0,parallelWalk_1.getKey)(_));;){var W=0===t;if(W){if((0,parallelWalk_1.moveForwardOne)(n,_))break;t=1}else if(t<0){var f=(0,parallelWalk_1.getKey)(n),i=n.leaf.values[n.leafIndex],i=e(f,i);if(i&&i.break)return i.break;if((0,parallelWalk_1.moveForwardOne)(n,_))break;t=k((0,parallelWalk_1.getKey)(n),(0,parallelWalk_1.getKey)(_))}else{i=(0,parallelWalk_1.moveTo)(_,n,(0,parallelWalk_1.getKey)(n),!0,W),W=i[0],i=i[1];if(W)return p();t=i?0:-1}}}}exports.default=forEachKeyNotIn;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import BTree from '../b+tree';
|
|
2
|
+
/**
|
|
3
|
+
* An extended version of the `BTree` class that includes additional functionality
|
|
4
|
+
* such as bulk loading, set operations, and diffing.
|
|
5
|
+
* It is separated to keep the core BTree class small from a bundle size perspective.
|
|
6
|
+
* Note: each additional functionality piece is available as a standalone function from the extended folder.
|
|
7
|
+
* @extends BTree
|
|
8
|
+
*/
|
|
9
|
+
export declare class BTreeEx<K = any, V = any> extends BTree<K, V> {
|
|
10
|
+
/**
|
|
11
|
+
* Bulk loads a new `BTreeEx` from parallel arrays of sorted entries.
|
|
12
|
+
* This reuses the same algorithm as `extended/bulkLoad`, but produces a `BTreeEx`.
|
|
13
|
+
* Time and space complexity are O(n).
|
|
14
|
+
* @param keys Keys to load, sorted by key in strictly ascending order.
|
|
15
|
+
* @param values Values aligned with the supplied keys.
|
|
16
|
+
* @param maxNodeSize The branching factor (maximum number of children per node).
|
|
17
|
+
* @param compare Comparator to use. Defaults to the standard comparator if omitted.
|
|
18
|
+
* @returns A fully built tree containing the supplied entries.
|
|
19
|
+
* @throws Error if the entries are not strictly sorted or contain duplicate keys.
|
|
20
|
+
*/
|
|
21
|
+
static bulkLoad<K, V>(keys: K[], values: V[], maxNodeSize: number, compare?: (a: K, b: K) => number): BTreeEx<K, V>;
|
|
22
|
+
/** See {@link BTree.clone}. */
|
|
23
|
+
clone(): this;
|
|
24
|
+
/** See {@link BTree.greedyClone}. */
|
|
25
|
+
greedyClone(force?: boolean): this;
|
|
26
|
+
/**
|
|
27
|
+
* Computes the differences between `this` and `other`.
|
|
28
|
+
* For efficiency, the diff is returned via invocations of supplied handlers.
|
|
29
|
+
* The computation is optimized for the case in which the two trees have large amounts of shared data
|
|
30
|
+
* (obtained by calling the `clone` or `with` APIs) and will avoid any iteration of shared state.
|
|
31
|
+
* The handlers can cause computation to early exit by returning `{ break: R }`.
|
|
32
|
+
* Neither collection should be mutated during the comparison (inside your callbacks), as this method assumes they remain stable.
|
|
33
|
+
* @param other The tree to compute a diff against.
|
|
34
|
+
* @param onlyThis Callback invoked for all keys only present in `this`.
|
|
35
|
+
* @param onlyOther Callback invoked for all keys only present in `other`.
|
|
36
|
+
* @param different Callback invoked for all keys with differing values.
|
|
37
|
+
* @returns The first `break` payload returned by a handler, or `undefined` if no handler breaks.
|
|
38
|
+
* @throws Error if the supplied trees were created with different comparators.
|
|
39
|
+
*/
|
|
40
|
+
diffAgainst<R>(other: BTree<K, V>, onlyThis?: (k: K, v: V) => {
|
|
41
|
+
break?: R;
|
|
42
|
+
} | void, onlyOther?: (k: K, v: V) => {
|
|
43
|
+
break?: R;
|
|
44
|
+
} | void, different?: (k: K, vThis: V, vOther: V) => {
|
|
45
|
+
break?: R;
|
|
46
|
+
} | void): R | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Calls the supplied `callback` for each key/value pair shared by this tree and `other`, in sorted key order.
|
|
49
|
+
* Neither tree is modified.
|
|
50
|
+
*
|
|
51
|
+
* Complexity is O(N + M) when the trees overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
52
|
+
* where `D` is the number of disjoint key ranges between the trees, because disjoint subtrees are skipped.
|
|
53
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
54
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
55
|
+
* @param other The other tree to compare with this one.
|
|
56
|
+
* @param callback Called for keys that appear in both trees. It can cause iteration to early exit by returning `{ break: R }`.
|
|
57
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if the walk finishes.
|
|
58
|
+
* @throws Error if the two trees were created with different comparators.
|
|
59
|
+
*/
|
|
60
|
+
forEachKeyInBoth<R = void>(other: BTree<K, V>, callback: (key: K, leftValue: V, rightValue: V) => {
|
|
61
|
+
break?: R;
|
|
62
|
+
} | void): R | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Calls the supplied `callback` for each key/value pair that exists in this tree but not in `other`
|
|
65
|
+
* (set subtraction). The callback runs in sorted key order and neither tree is modified.
|
|
66
|
+
*
|
|
67
|
+
* Complexity is O(N + M) when the key ranges overlap heavily, and additionally bounded by O(log(N + M) * D)
|
|
68
|
+
* where `D` is the number of disjoint ranges between the trees, because non-overlapping subtrees are skipped.
|
|
69
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
70
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
71
|
+
* @param other Keys present in this tree will be omitted from the callback.
|
|
72
|
+
* @param callback Invoked for keys unique to `this`. It can cause iteration to early exit by returning `{ break: R }`.
|
|
73
|
+
* @returns The first `break` payload returned by the callback, or `undefined` if all qualifying keys are visited.
|
|
74
|
+
* @throws Error if the trees were created with different comparators.
|
|
75
|
+
*/
|
|
76
|
+
forEachKeyNotIn<R = void>(other: BTree<K, V>, callback: (key: K, value: V) => {
|
|
77
|
+
break?: R;
|
|
78
|
+
} | void): R | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Returns a new tree containing only keys present in both trees.
|
|
81
|
+
* Neither tree is modified.
|
|
82
|
+
*
|
|
83
|
+
* Complexity is O(N + M) in the fully overlapping case and additionally bounded by O(log(N + M) * D),
|
|
84
|
+
* where `D` is the number of disjoint key ranges, because disjoint subtrees are skipped entirely.
|
|
85
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
86
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
87
|
+
* @param other The other tree to intersect with this one.
|
|
88
|
+
* @param combineFn Called for keys that appear in both trees. Return the desired value.
|
|
89
|
+
* @returns A new `BTreeEx` populated with the intersection.
|
|
90
|
+
* @throws Error if the trees were created with different comparators.
|
|
91
|
+
*/
|
|
92
|
+
intersect(other: BTreeEx<K, V>, combineFn: (key: K, leftValue: V, rightValue: V) => V): BTreeEx<K, V>;
|
|
93
|
+
/**
|
|
94
|
+
* Efficiently unions this tree with `other`, reusing subtrees wherever possible without modifying either input.
|
|
95
|
+
*
|
|
96
|
+
* Complexity is O(N + M) in the fully overlapping case, and additionally bounded by O(log(N + M) * D)
|
|
97
|
+
* where `D` is the number of disjoint key ranges, because disjoint subtrees are skipped entirely.
|
|
98
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
99
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
100
|
+
* @param other The other tree to union with this one.
|
|
101
|
+
* @param combineFn Called for keys that appear in both trees. Return the desired value, or `undefined` to omit the key.
|
|
102
|
+
* @returns A new `BTreeEx` that contains the unioned key/value pairs.
|
|
103
|
+
* @throws Error if the trees were created with different comparators or max node sizes.
|
|
104
|
+
*/
|
|
105
|
+
union(other: BTreeEx<K, V>, combineFn: (key: K, leftValue: V, rightValue: V) => V | undefined): BTreeEx<K, V>;
|
|
106
|
+
/**
|
|
107
|
+
* Returns a new tree containing only the keys that are present in this tree but not `other` (set subtraction).
|
|
108
|
+
* Neither input tree is modified.
|
|
109
|
+
*
|
|
110
|
+
* Complexity is O(N + M) for time and O(N) for allocations in the worst case. Additionally, time is bounded by
|
|
111
|
+
* O(log(N + M) * D1) and space by O(log N * D2) where `D1` is the number of disjoint key ranges between the trees
|
|
112
|
+
* and `D2` is the number of disjoint ranges inside this tree.
|
|
113
|
+
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
|
|
114
|
+
* numbers of non-overlapping key ranges it is much faster.
|
|
115
|
+
* @param other The tree whose keys will be removed from the result.
|
|
116
|
+
* @returns A new `BTreeEx` representing `this \ other`.
|
|
117
|
+
* @throws Error if the trees were created with different comparators or max node sizes.
|
|
118
|
+
*/
|
|
119
|
+
subtract(other: BTreeEx<K, V>): BTreeEx<K, V>;
|
|
120
|
+
}
|
|
121
|
+
export interface BTreeEx<K = any, V = any> {
|
|
122
|
+
/** See {@link BTree.with}. */
|
|
123
|
+
with(key: K): BTreeEx<K, V | undefined>;
|
|
124
|
+
with<V2>(key: K, value: V2, overwrite?: boolean): BTreeEx<K, V | V2>;
|
|
125
|
+
with<V2>(key: K, value?: V2, overwrite?: boolean): BTreeEx<K, V | V2 | undefined>;
|
|
126
|
+
/** See {@link BTree.withPairs}. */
|
|
127
|
+
withPairs<V2>(pairs: [K, V | V2][], overwrite: boolean): BTreeEx<K, V | V2>;
|
|
128
|
+
/** See {@link BTree.withKeys}. */
|
|
129
|
+
withKeys(keys: K[], returnThisIfUnchanged?: boolean): BTreeEx<K, V | undefined>;
|
|
130
|
+
/** See {@link BTree.mapValues}. */
|
|
131
|
+
mapValues<R>(callback: (v: V, k: K, counter: number) => R): BTreeEx<K, R>;
|
|
132
|
+
}
|
|
133
|
+
export default BTreeEx;
|