atomic-fns 1.2.0 → 2.0.0

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.
Files changed (216) hide show
  1. package/README.md +397 -91
  2. package/dist/array/index.d.ts +9 -0
  3. package/dist/array/index.js +9 -0
  4. package/dist/array/list.d.ts +99 -0
  5. package/dist/array/list.js +133 -0
  6. package/dist/array/sets.d.ts +130 -0
  7. package/dist/array/sets.js +213 -0
  8. package/dist/async/index.d.ts +6 -0
  9. package/dist/async/index.js +6 -0
  10. package/dist/async/retry.d.ts +38 -0
  11. package/dist/async/retry.js +56 -0
  12. package/dist/async/sleep.d.ts +42 -0
  13. package/dist/async/sleep.js +76 -0
  14. package/dist/collections/bisect.d.ts +103 -0
  15. package/dist/collections/bisect.js +157 -0
  16. package/dist/collections/deque.d.ts +67 -97
  17. package/dist/collections/deque.js +197 -256
  18. package/dist/collections/entries.d.ts +89 -0
  19. package/dist/collections/entries.js +131 -0
  20. package/dist/collections/frozen-set.d.ts +42 -0
  21. package/dist/collections/frozen-set.js +64 -0
  22. package/dist/collections/group.d.ts +141 -0
  23. package/dist/collections/group.js +241 -0
  24. package/dist/collections/heap.d.ts +64 -0
  25. package/dist/collections/heap.js +205 -0
  26. package/dist/collections/index.d.ts +15 -623
  27. package/dist/collections/index.js +14 -844
  28. package/dist/collections/internal/arrayKeys.d.ts +19 -0
  29. package/dist/collections/internal/arrayKeys.js +83 -0
  30. package/dist/collections/internal/invariants.d.ts +4 -0
  31. package/dist/collections/internal/invariants.js +12 -0
  32. package/dist/collections/internal/iterable.d.ts +25 -0
  33. package/dist/collections/internal/iterable.js +62 -0
  34. package/dist/collections/iterate.d.ts +162 -0
  35. package/dist/collections/iterate.js +188 -0
  36. package/dist/collections/lru-cache.d.ts +58 -0
  37. package/dist/collections/lru-cache.js +164 -0
  38. package/dist/collections/order.d.ts +19 -0
  39. package/dist/collections/order.js +38 -0
  40. package/dist/collections/shape.d.ts +32 -0
  41. package/dist/collections/shape.js +86 -0
  42. package/dist/collections/sorted-map.d.ts +171 -0
  43. package/dist/collections/sorted-map.js +845 -0
  44. package/dist/collections/sorted-set.d.ts +103 -0
  45. package/dist/collections/sorted-set.js +147 -0
  46. package/dist/collections/splay-tree.d.ts +82 -0
  47. package/dist/collections/splay-tree.js +354 -0
  48. package/dist/collections/trie.d.ts +58 -0
  49. package/dist/collections/trie.js +196 -0
  50. package/dist/decimal/currency.d.ts +211 -0
  51. package/dist/decimal/currency.js +321 -0
  52. package/dist/decimal/decimal.d.ts +299 -0
  53. package/dist/decimal/decimal.js +660 -0
  54. package/dist/decimal/index.d.ts +11 -148
  55. package/dist/decimal/index.js +10 -410
  56. package/dist/decimal/internal/bits.d.ts +2 -0
  57. package/dist/decimal/internal/bits.js +7 -0
  58. package/dist/decimal/internal/echo.d.ts +4 -0
  59. package/dist/decimal/internal/echo.js +6 -0
  60. package/dist/decimal/internal/isqrt.d.ts +9 -0
  61. package/dist/decimal/internal/isqrt.js +34 -0
  62. package/dist/decimal/internal/locale.d.ts +7 -0
  63. package/dist/decimal/internal/locale.js +24 -0
  64. package/dist/decimal/internal/minor-units.d.ts +2 -0
  65. package/dist/decimal/internal/minor-units.js +33 -0
  66. package/dist/decimal/internal/pow10.d.ts +24 -0
  67. package/dist/decimal/internal/pow10.js +96 -0
  68. package/dist/encoding/base64.d.ts +39 -0
  69. package/dist/encoding/base64.js +53 -0
  70. package/dist/encoding/hex.d.ts +23 -0
  71. package/dist/encoding/hex.js +34 -0
  72. package/dist/encoding/index.d.ts +6 -0
  73. package/dist/encoding/index.js +6 -0
  74. package/dist/function/debounce.d.ts +68 -0
  75. package/dist/function/debounce.js +203 -0
  76. package/dist/function/index.d.ts +6 -0
  77. package/dist/function/index.js +6 -0
  78. package/dist/function/shared.d.ts +2 -0
  79. package/dist/function/shared.js +5 -0
  80. package/dist/function/wrap.d.ts +130 -0
  81. package/dist/function/wrap.js +113 -0
  82. package/dist/hash/digest.d.ts +18 -0
  83. package/dist/hash/digest.js +26 -0
  84. package/dist/hash/hash.d.ts +22 -0
  85. package/dist/hash/hash.js +252 -0
  86. package/dist/hash/hashString.d.ts +13 -0
  87. package/dist/hash/hashString.js +53 -0
  88. package/dist/hash/index.d.ts +8 -0
  89. package/dist/hash/index.js +8 -0
  90. package/dist/hash/stableStringify.d.ts +19 -0
  91. package/dist/hash/stableStringify.js +102 -0
  92. package/dist/id/index.d.ts +7 -0
  93. package/dist/id/index.js +7 -0
  94. package/dist/id/nanoid.d.ts +16 -0
  95. package/dist/id/nanoid.js +65 -0
  96. package/dist/id/uniqueId.d.ts +14 -0
  97. package/dist/id/uniqueId.js +20 -0
  98. package/dist/id/uuid.d.ts +25 -0
  99. package/dist/id/uuid.js +64 -0
  100. package/dist/index.d.ts +15 -8
  101. package/dist/index.js +15 -8
  102. package/dist/internal/bytes.d.ts +11 -0
  103. package/dist/internal/bytes.js +33 -0
  104. package/dist/internal/codec.d.ts +11 -0
  105. package/dist/internal/codec.js +84 -0
  106. package/dist/internal/compare.d.ts +10 -0
  107. package/dist/internal/compare.js +26 -0
  108. package/dist/internal/crypto.d.ts +5 -0
  109. package/dist/internal/crypto.js +11 -0
  110. package/dist/internal/keys.d.ts +12 -0
  111. package/dist/internal/keys.js +23 -0
  112. package/dist/internal/kind.d.ts +44 -0
  113. package/dist/internal/kind.js +139 -0
  114. package/dist/internal/sparse.d.ts +51 -0
  115. package/dist/internal/sparse.js +104 -0
  116. package/dist/internal/text.d.ts +7 -0
  117. package/dist/internal/text.js +15 -0
  118. package/dist/internal/timer.d.ts +10 -0
  119. package/dist/internal/timer.js +17 -0
  120. package/dist/internal/truncate.d.ts +10 -0
  121. package/dist/internal/truncate.js +22 -0
  122. package/dist/internal/walk.d.ts +47 -0
  123. package/dist/internal/walk.js +100 -0
  124. package/dist/is/index.d.ts +8 -0
  125. package/dist/is/index.js +8 -0
  126. package/dist/is/isArrayLike.d.ts +18 -0
  127. package/dist/is/isArrayLike.js +25 -0
  128. package/dist/is/isError.d.ts +13 -0
  129. package/dist/is/isError.js +18 -0
  130. package/dist/is/isNil.d.ts +12 -0
  131. package/dist/is/isNil.js +14 -0
  132. package/dist/is/isPlainObject.d.ts +15 -0
  133. package/dist/is/isPlainObject.js +34 -0
  134. package/dist/number/index.d.ts +7 -0
  135. package/dist/number/index.js +7 -0
  136. package/dist/number/round.d.ts +86 -0
  137. package/dist/number/round.js +130 -0
  138. package/dist/number/stats.d.ts +52 -0
  139. package/dist/number/stats.js +115 -0
  140. package/dist/object/cloneDeep.d.ts +28 -0
  141. package/dist/object/cloneDeep.js +200 -0
  142. package/dist/object/index.d.ts +14 -0
  143. package/dist/object/index.js +13 -0
  144. package/dist/object/isEqual.d.ts +64 -0
  145. package/dist/object/isEqual.js +289 -0
  146. package/dist/object/matches.d.ts +40 -0
  147. package/dist/object/matches.js +284 -0
  148. package/dist/object/merge.d.ts +66 -0
  149. package/dist/object/merge.js +272 -0
  150. package/dist/object/parsePath.d.ts +22 -0
  151. package/dist/object/parsePath.js +111 -0
  152. package/dist/object/path.d.ts +85 -0
  153. package/dist/object/path.js +208 -0
  154. package/dist/object/pick.d.ts +91 -0
  155. package/dist/object/pick.js +180 -0
  156. package/dist/object/reshape.d.ts +73 -0
  157. package/dist/object/reshape.js +144 -0
  158. package/dist/random/index.d.ts +96 -32
  159. package/dist/random/index.js +215 -53
  160. package/dist/string/case.d.ts +71 -0
  161. package/dist/string/case.js +152 -0
  162. package/dist/string/fold.d.ts +35 -0
  163. package/dist/string/fold.js +100 -0
  164. package/dist/string/index.d.ts +6 -187
  165. package/dist/string/index.js +6 -224
  166. package/dist/string/truncate.d.ts +25 -0
  167. package/dist/string/truncate.js +44 -0
  168. package/dist/string/words.d.ts +27 -0
  169. package/dist/string/words.js +241 -0
  170. package/package.json +99 -51
  171. package/dist/collections/BTree.d.ts +0 -230
  172. package/dist/collections/BTree.js +0 -1143
  173. package/dist/collections/Heap.d.ts +0 -69
  174. package/dist/collections/Heap.js +0 -181
  175. package/dist/collections/LRUCache.d.ts +0 -83
  176. package/dist/collections/LRUCache.js +0 -139
  177. package/dist/collections/SortedMap.d.ts +0 -92
  178. package/dist/collections/SortedMap.js +0 -131
  179. package/dist/collections/SortedSet.d.ts +0 -69
  180. package/dist/collections/SortedSet.js +0 -99
  181. package/dist/collections/SortedTree.d.ts +0 -103
  182. package/dist/collections/SortedTree.js +0 -142
  183. package/dist/collections/SplayTree.d.ts +0 -107
  184. package/dist/collections/SplayTree.js +0 -351
  185. package/dist/collections/Trie.d.ts +0 -42
  186. package/dist/collections/Trie.js +0 -104
  187. package/dist/collections/abc.d.ts +0 -152
  188. package/dist/collections/abc.js +0 -39
  189. package/dist/collections/frozenset.d.ts +0 -19
  190. package/dist/collections/frozenset.js +0 -33
  191. package/dist/functools/index.d.ts +0 -106
  192. package/dist/functools/index.js +0 -203
  193. package/dist/globals/index.d.ts +0 -268
  194. package/dist/globals/index.js +0 -447
  195. package/dist/i18n/currency.d.ts +0 -27
  196. package/dist/i18n/currency.js +0 -100
  197. package/dist/i18n/date/index.d.ts +0 -210
  198. package/dist/i18n/date/index.js +0 -552
  199. package/dist/i18n/date/utils.d.ts +0 -64
  200. package/dist/i18n/date/utils.js +0 -192
  201. package/dist/i18n/duration.d.ts +0 -113
  202. package/dist/i18n/duration.js +0 -398
  203. package/dist/i18n/format.d.ts +0 -34
  204. package/dist/i18n/format.js +0 -378
  205. package/dist/i18n/index.d.ts +0 -9
  206. package/dist/i18n/index.js +0 -8
  207. package/dist/itertools/index.d.ts +0 -251
  208. package/dist/itertools/index.js +0 -434
  209. package/dist/math/index.d.ts +0 -79
  210. package/dist/math/index.js +0 -81
  211. package/dist/mod.d.ts +0 -1
  212. package/dist/mod.js +0 -1
  213. package/dist/operators/index.d.ts +0 -148
  214. package/dist/operators/index.js +0 -325
  215. package/dist/types.d.ts +0 -19
  216. package/dist/types.js +0 -1
@@ -1,1143 +0,0 @@
1
- import { KeyError } from '../globals/index.js';
2
- import { compare } from '../operators/index.js';
3
- import { Mapping } from './abc.js';
4
- const Delete = { delete: true };
5
- const DeleteRange = () => Delete;
6
- const Break = { done: true };
7
- const EmptyArray = [];
8
- const ReusedArray = []; // assumed thread-local
9
- // Optimization: this array of `undefined`s is used instead of a normal
10
- // array of values in nodes where `undefined` is the only value.
11
- // Its length is extended to max node size on first use; since it can
12
- // be shared between trees with different maximums, its length can only
13
- // increase, never decrease. Its type should be undefined[] but strangely
14
- // TypeScript won't allow the comparison V[] === undefined[]. To prevent
15
- // users from making this array too large, BTree has a maximum node size.
16
- //
17
- // FAQ: undefVals[i] is already undefined, so why increase the array size?
18
- // Reading outside the bounds of an array is relatively slow because it
19
- // has the side effect of scanning the prototype chain.
20
- const undefVals = [];
21
- /**
22
- * Leaf nodes and base class for internals.
23
- * Based on the `sorted-btree` implementation by David Piepgrass (MIT License).
24
- * @private
25
- */
26
- class BNode {
27
- // If this is an internal node, _keys[i] is the highest key in children[i].
28
- keys;
29
- values;
30
- // True if this node might be within multiple `BTree`s (or have multiple parents).
31
- // If so, it must be cloned before being mutated to avoid changing an unrelated tree.
32
- // This is transitive: if it's true, children are also shared even if `isShared!=true`
33
- // in those children. (Certain operations will propagate isShared=true to children.)
34
- isShared;
35
- get isLeaf() {
36
- return this.children === undefined;
37
- }
38
- constructor(keys = [], values) {
39
- this.keys = keys;
40
- this.values = values || undefVals;
41
- this.isShared = undefined;
42
- }
43
- /// ////////////////////////////////////////////////////////////////////////
44
- // Shared methods /////////////////////////////////////////////////////////
45
- maxKey() {
46
- return this.keys.at(-1);
47
- }
48
- // If key not found, returns i^failXor where i is the insertion index.
49
- // Callers that don't care whether there was a match will set failXor=0.
50
- indexOf(key, failXor, cmp) {
51
- const keys = this.keys;
52
- let lo = 0;
53
- let hi = keys.length;
54
- let mid = hi >> 1;
55
- while (lo < hi) {
56
- const c = cmp(keys[mid], key);
57
- if (c < 0)
58
- lo = mid + 1;
59
- else if (c > 0)
60
- hi = mid; // key < keys[mid]
61
- else if (c === 0)
62
- return mid;
63
- else {
64
- // c is NaN or otherwise invalid
65
- if (key === key)
66
- // at least the search key is not NaN
67
- return keys.length ^ failXor;
68
- else
69
- throw new KeyError(`Btree found an invalid key: ${key}`);
70
- }
71
- mid = (lo + hi) >> 1;
72
- }
73
- return mid ^ failXor;
74
- }
75
- /// //////////////////////////////////////////////////////////////////////////
76
- // Leaf Node: misc //////////////////////////////////////////////////////////
77
- minKey() {
78
- return this.keys[0];
79
- }
80
- minPair(reusedArray) {
81
- if (this.keys.length === 0)
82
- return undefined;
83
- reusedArray[0] = this.keys[0];
84
- reusedArray[1] = this.values[0];
85
- return reusedArray;
86
- }
87
- maxPair(reusedArray) {
88
- if (this.keys.length === 0)
89
- return undefined;
90
- const lastIndex = this.keys.length - 1;
91
- reusedArray[0] = this.keys[lastIndex];
92
- reusedArray[1] = this.values[lastIndex];
93
- return reusedArray;
94
- }
95
- clone() {
96
- const v = this.values;
97
- return new BNode(this.keys.slice(0), v === undefVals ? v : v.slice(0));
98
- }
99
- greedyClone(force) {
100
- return this.isShared && !force ? this : this.clone();
101
- }
102
- get(key, defaultValue, tree) {
103
- const i = this.indexOf(key, -1, tree._compare);
104
- return i < 0 ? defaultValue : this.values[i];
105
- }
106
- getPairOrNextLower(key, compare, inclusive, reusedArray) {
107
- const i = this.indexOf(key, -1, compare);
108
- const indexOrLower = i < 0 ? ~i - 1 : inclusive ? i : i - 1;
109
- if (indexOrLower >= 0) {
110
- reusedArray[0] = this.keys[indexOrLower];
111
- reusedArray[1] = this.values[indexOrLower];
112
- return reusedArray;
113
- }
114
- return undefined;
115
- }
116
- getPairOrNextHigher(key, compare, inclusive, reusedArray) {
117
- const i = this.indexOf(key, -1, compare);
118
- const indexOrLower = i < 0 ? ~i : inclusive ? i : i + 1;
119
- const keys = this.keys;
120
- if (indexOrLower < keys.length) {
121
- reusedArray[0] = keys[indexOrLower];
122
- reusedArray[1] = this.values[indexOrLower];
123
- return reusedArray;
124
- }
125
- return undefined;
126
- }
127
- checkValid(depth, tree, baseIndex) {
128
- const kL = this.keys.length;
129
- const vL = this.values.length;
130
- assert(this.values === undefVals ? kL <= vL : kL === vL, 'keys/values length mismatch: depth', depth, 'with lengths', kL, vL, 'and baseIndex', baseIndex);
131
- // Note: we don't check for "node too small" because sometimes a node
132
- // can legitimately have size 1. This occurs if there is a batch
133
- // deletion, leaving a node of size 1, and the siblings are full so
134
- // it can't be merged with adjacent nodes. However, the parent will
135
- // verify that the average node size is at least half of the maximum.
136
- assert(depth === 0 || kL > 0, 'empty leaf at depth', depth, 'and baseIndex', baseIndex);
137
- return kL;
138
- }
139
- /// //////////////////////////////////////////////////////////////////////////
140
- // Leaf Node: set & node splitting //////////////////////////////////////////
141
- set(key, value, overwrite, tree) {
142
- let i = this.indexOf(key, -1, tree._compare);
143
- if (i < 0) {
144
- // key does not exist yet
145
- i = ~i;
146
- tree.count++;
147
- if (this.keys.length < tree._maxNodeSize) {
148
- return this.insertInLeaf(i, key, value, tree);
149
- }
150
- else {
151
- // This leaf node is full and must split
152
- const newRightSibling = this.splitOffRightSide();
153
- let target = this;
154
- if (i > this.keys.length) {
155
- i -= this.keys.length;
156
- target = newRightSibling;
157
- }
158
- target.insertInLeaf(i, key, value, tree);
159
- return newRightSibling;
160
- }
161
- }
162
- else {
163
- // Key already exists
164
- if (overwrite) {
165
- if (value !== undefined)
166
- this.reifyValues();
167
- // usually this is a no-op, but some users may wish to edit the key
168
- this.keys[i] = key;
169
- this.values[i] = value;
170
- }
171
- return false;
172
- }
173
- }
174
- reifyValues() {
175
- if (this.values === undefVals)
176
- return (this.values = this.values.slice(0, this.keys.length));
177
- return this.values;
178
- }
179
- insertInLeaf(i, key, value, tree) {
180
- this.keys.splice(i, 0, key);
181
- if (this.values === undefVals) {
182
- while (undefVals.length < tree._maxNodeSize)
183
- undefVals.push(undefined);
184
- if (value === undefined) {
185
- return true;
186
- }
187
- else {
188
- this.values = undefVals.slice(0, this.keys.length - 1);
189
- }
190
- }
191
- this.values.splice(i, 0, value);
192
- return true;
193
- }
194
- takeFromRight(rhs) {
195
- // Reminder: parent node must update its copy of key for this node
196
- // assert: neither node is shared
197
- // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
198
- let v = this.values;
199
- if (rhs.values === undefVals) {
200
- if (v !== undefVals)
201
- v.push(undefined);
202
- }
203
- else {
204
- v = this.reifyValues();
205
- v.push(rhs.values.shift());
206
- }
207
- this.keys.push(rhs.keys.shift());
208
- }
209
- takeFromLeft(lhs) {
210
- // Reminder: parent node must update its copy of key for this node
211
- // assert: neither node is shared
212
- // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
213
- let v = this.values;
214
- if (lhs.values === undefVals) {
215
- if (v !== undefVals)
216
- v.unshift();
217
- }
218
- else {
219
- v = this.reifyValues();
220
- v.unshift(lhs.values.pop());
221
- }
222
- this.keys.unshift(lhs.keys.pop());
223
- }
224
- splitOffRightSide() {
225
- // Reminder: parent node must update its copy of key for this node
226
- const half = this.keys.length >> 1;
227
- const keys = this.keys.splice(half);
228
- const values = this.values === undefVals ? undefVals : this.values.splice(half);
229
- return new BNode(keys, values);
230
- }
231
- /// //////////////////////////////////////////////////////////////////////////
232
- // Leaf Node: scanning & deletions //////////////////////////////////////////
233
- forRange(low, high, includeHigh, editMode, tree, count, onFound) {
234
- const cmp = tree._compare;
235
- let iLow, iHigh;
236
- if (high === low) {
237
- if (!includeHigh)
238
- return count;
239
- iHigh = (iLow = this.indexOf(low, -1, cmp)) + 1;
240
- if (iLow < 0)
241
- return count;
242
- }
243
- else {
244
- iLow = this.indexOf(low, 0, cmp);
245
- iHigh = this.indexOf(high, -1, cmp);
246
- if (iHigh < 0)
247
- iHigh = ~iHigh;
248
- else if (includeHigh)
249
- iHigh++;
250
- }
251
- const keys = this.keys;
252
- if (onFound !== undefined) {
253
- for (let i = iLow; i < iHigh; i++) {
254
- const key = keys[i];
255
- const result = onFound(key, this.values[i], count++);
256
- if (result != null) {
257
- if (editMode) {
258
- if (!Object.is(key, keys[i]) || this.isShared)
259
- throw new Error('BTree illegally changed or cloned in editRange');
260
- if (result.delete) {
261
- this.keys.splice(i, 1);
262
- if (this.values !== undefVals)
263
- this.values.splice(i, 1);
264
- tree.count--;
265
- i--;
266
- iHigh--;
267
- }
268
- else if ('value' in Object(result)) {
269
- this.reifyValues()[i] = result.value;
270
- }
271
- }
272
- if (result.done !== undefined)
273
- return result;
274
- }
275
- }
276
- }
277
- else
278
- count += Math.max(iHigh - iLow, 0);
279
- return count;
280
- }
281
- /** Adds entire contents of right-hand sibling (rhs is left unchanged) */
282
- mergeSibling(rhs, _) {
283
- this.keys.push.apply(this.keys, rhs.keys);
284
- if (this.values === undefVals) {
285
- if (rhs.values === undefVals)
286
- return;
287
- this.values = this.values.slice(0, this.keys.length - rhs.keys.length);
288
- }
289
- this.values.push.apply(this.values, rhs.reifyValues());
290
- }
291
- }
292
- const EmptyLeaf = new BNode();
293
- EmptyLeaf.isShared = true;
294
- function assert(exp, ...args) {
295
- if (!exp) {
296
- args.unshift('B+ tree'); // at beginning of message
297
- throw new Error(args.join(' '));
298
- }
299
- }
300
- export class BTree extends Mapping {
301
- root = EmptyLeaf;
302
- count = 0;
303
- _maxNodeSize;
304
- /**
305
- * provides a total order over keys (and a strict partial order over the type K)
306
- * @returns a negative value if a < b, 0 if a === b and a positive value if a > b
307
- */
308
- _compare;
309
- /**
310
- * Initializes an empty B+ tree.
311
- * @param {?Iterable<[K, V]>} [entries] The initial key value pairs.
312
- * @param compareFn Custom function to compare pairs of elements in the tree.
313
- * If not specified, {@link operators.compare} is used.
314
- * @param {number} [maxNodeSize=64] Branching factor (maximum items or children per node)
315
- * Must be in range 4..256. If undefined or <4 then default is used; if >256 then 256.
316
- */
317
- constructor(entries, compareFn, maxNodeSize = 64) {
318
- super();
319
- this._maxNodeSize = maxNodeSize >= 4 ? Math.min(maxNodeSize, 256) : 64;
320
- this._compare = compareFn || compare;
321
- if (entries)
322
- this.extend(entries);
323
- }
324
- /** Gets the number of key-value pairs in the tree. */
325
- get size() {
326
- return this.count;
327
- }
328
- /** Releases the tree so that its size is 0. */
329
- clear() {
330
- this.root = EmptyLeaf;
331
- this.count = 0;
332
- }
333
- // /** Runs a function for each key-value pair, in order from smallest to
334
- // * largest key. For compatibility with ES6 Map, the argument order to
335
- // * the callback is backwards: value first, then key. Call forEachPair
336
- // * instead to receive the key as the first argument.
337
- // * @returns the number of values that were sent to the callback,
338
- // * or the R value if the callback returned {done:R}. */
339
- // forEach<R = number>(iteratee: Iteratee<V, K>): R | number {
340
- // return this.forEachPair((k, v) => iteratee(v as V, k, this))
341
- // }
342
- // /** Runs a function for each key-value pair, in order from smallest to
343
- // * largest key. The callback can return {done:R} (where R is any value
344
- // * except undefined) to stop immediately and return R from forEachPair.
345
- // * @param onFound A function that is called for each key-value pair. This
346
- // * function can return {done:R} to stop early with result R.
347
- // * The reason that you must return {done:R} instead of simply R
348
- // * itself is for consistency with editRange(), which allows
349
- // * multiple actions, not just breaking.
350
- // * @param initialCounter This is the value of the third argument of
351
- // * `onFound` the first time it is called. The counter increases
352
- // * by one each time `onFound` is called. Default value: 0
353
- // * @returns the number of pairs sent to the callback (plus initialCounter,
354
- // * if you provided one). If the callback returned {done:R} then
355
- // * the R value is returned instead. */
356
- // private forEachPair<R = number>(callback: Iteratee<K, V>, initialCounter?: number): R | number {
357
- // const low = this.minKey()
358
- // const high = this.maxKey()
359
- //
360
- // return this.rangeForEach(low!, high, true, callback, initialCounter)
361
- // }
362
- /**
363
- * Finds a pair in the tree and returns the associated value.
364
- * @param defaultValue a value to return if the key was not found.
365
- * @returns the value, or defaultValue if the key was not found.
366
- * @description Computational complexity: O(log size)
367
- */
368
- get(key, defaultValue) {
369
- return this.root.get(key, defaultValue, this);
370
- }
371
- /**
372
- * Adds or overwrites a key-value pair in the B+ tree.
373
- * @param key the key is used to determine the sort order of
374
- * data in the tree.
375
- * @param value data to associate with the key (optional)
376
- * @returns true if a new key-value pair was added.
377
- * @description Computational complexity: O(log size)
378
- * Note: when overwriting a previous entry, the key is updated
379
- * as well as the value. This has no effect unless the new key
380
- * has data that does not affect its sort order.
381
- */
382
- set(key, value) {
383
- if (this.root.isShared)
384
- this.root = this.root.clone();
385
- const result = this.root.set(key, value, true, this);
386
- if (result === true || result === false)
387
- return result;
388
- // Root node has split, so create a new root node.
389
- this.root = new BNodeInternal([this.root, result]);
390
- return true;
391
- }
392
- add(key) {
393
- return this.set(key, this.get(key, null));
394
- }
395
- /**
396
- * Returns true if the key exists in the B+ tree, false if not.
397
- * @param key Key to detect
398
- * @description Computational complexity: O(log size)
399
- */
400
- contains(key) {
401
- return this.rangeForEach(key, key, true) !== 0;
402
- }
403
- /**
404
- * Removes a single key-value pair from the B+ tree.
405
- * @param key Key to find
406
- * @returns true if a pair was found and removed, false otherwise.
407
- * @description Computational complexity: O(log size)
408
- */
409
- remove(key) {
410
- return this.rangeUpdate(key, key, true, DeleteRange) !== 0;
411
- }
412
- /** Returns an iterator that provides items in order (ascending order if
413
- * the collection's comparator uses ascending order, as is the default.)
414
- * @param lowestKey First key to be iterated, or undefined to start at
415
- * minKey(). If the specified key doesn't exist then iteration
416
- * starts at the next higher key (according to the comparator).
417
- * @param reusedArray Optional array used repeatedly to store key-value
418
- * pairs, to avoid creating a new array on every iteration.
419
- * Note: changing the tree during iteration may skip or repeat keys.
420
- */
421
- entries(lowestKey, reusedArray) {
422
- const info = this.findPath(lowestKey);
423
- if (info === undefined)
424
- return iterator();
425
- let { nodequeue, nodeindex, leaf } = info;
426
- let state = reusedArray !== undefined ? 1 : 0;
427
- let i = lowestKey === undefined ? -1 : leaf.indexOf(lowestKey, 0, this._compare) - 1;
428
- return iterator(() => {
429
- jump: while (true) {
430
- switch (state) {
431
- case 0:
432
- if (++i < leaf.keys.length)
433
- return { done: false, value: [leaf.keys[i], leaf.values[i]] };
434
- state = 2;
435
- continue;
436
- case 1:
437
- if (++i < leaf.keys.length) {
438
- reusedArray[0] = leaf.keys[i];
439
- reusedArray[1] = leaf.values[i];
440
- return { done: false, value: reusedArray };
441
- }
442
- state = 2;
443
- case 2:
444
- // Advance to the next leaf node
445
- let level = -1;
446
- while (true) {
447
- if (++level >= nodequeue.length) {
448
- state = 3;
449
- continue jump;
450
- }
451
- if (++nodeindex[level] < nodequeue[level].length)
452
- break;
453
- }
454
- // Keys removed during iteration can leave this path empty.
455
- for (; level > 0; level--) {
456
- const parent = nodequeue[level][nodeindex[level]];
457
- nodequeue[level - 1] = parent?.children ?? EmptyArray;
458
- nodeindex[level - 1] = 0;
459
- }
460
- leaf = nodequeue[0][nodeindex[0]] ?? EmptyLeaf;
461
- i = -1;
462
- state = reusedArray !== undefined ? 1 : 0;
463
- continue;
464
- case 3:
465
- return { done: true, value: undefined };
466
- }
467
- }
468
- });
469
- }
470
- /** Returns an iterator that provides items in reversed order.
471
- * @param highestKey Key at which to start iterating, or undefined to
472
- * start at maxKey(). If the specified key doesn't exist then iteration
473
- * starts at the next lower key (according to the comparator).
474
- * @param reusedArray Optional array used repeatedly to store key-value
475
- * pairs, to avoid creating a new array on every iteration.
476
- * @param skipHighest Iff this flag is true and the highestKey exists in the
477
- * collection, the pair matching highestKey is skipped, not iterated.
478
- * Note: changing the tree during iteration may skip or repeat keys.
479
- */
480
- reversed(highestKey, reusedArray, skipHighest) {
481
- let { nodequeue, nodeindex, leaf } = this.findPath(highestKey, true) || this.findPath(undefined, true);
482
- assert(!nodequeue[0] || leaf === nodequeue[0][nodeindex[0]], 'wat!');
483
- let i = leaf.keys.length;
484
- if (highestKey !== undefined) {
485
- i = leaf.indexOf(highestKey, 0, this._compare);
486
- if (!skipHighest && i < leaf.keys.length && this._compare(leaf.keys[i], highestKey) <= 0)
487
- i++;
488
- }
489
- let state = reusedArray !== undefined ? 1 : 0;
490
- return iterator(() => {
491
- jump: while (true) {
492
- switch (state) {
493
- case 0:
494
- i = Math.min(i, leaf.keys.length) - 1;
495
- if (i >= 0)
496
- return { done: false, value: [leaf.keys[i], leaf.values[i]] };
497
- state = 2;
498
- continue;
499
- case 1:
500
- i = Math.min(i, leaf.keys.length) - 1;
501
- if (i >= 0) {
502
- reusedArray[0] = leaf.keys[i];
503
- reusedArray[1] = leaf.values[i];
504
- return { done: false, value: reusedArray };
505
- }
506
- state = 2;
507
- case 2:
508
- // Advance to the next leaf node
509
- let level = -1;
510
- while (true) {
511
- if (++level >= nodequeue.length) {
512
- state = 3;
513
- continue jump;
514
- }
515
- if (--nodeindex[level] >= 0)
516
- break;
517
- }
518
- // Keys removed during iteration can leave this path empty.
519
- for (; level > 0; level--) {
520
- const parent = nodequeue[level][nodeindex[level]];
521
- nodequeue[level - 1] = parent?.children ?? EmptyArray;
522
- nodeindex[level - 1] = nodequeue[level - 1].length - 1;
523
- }
524
- leaf = nodequeue[0][nodeindex[0]] ?? EmptyLeaf;
525
- i = leaf.keys.length;
526
- state = reusedArray !== undefined ? 1 : 0;
527
- continue;
528
- case 3:
529
- return { done: true, value: undefined };
530
- }
531
- }
532
- });
533
- }
534
- /* Used by entries() and reversed() to prepare to start iterating.
535
- * It develops a "node queue" for each non-leaf level of the tree.
536
- * Levels are numbered "bottom-up" so that level 0 is a list of leaf
537
- * nodes from a low-level non-leaf node. The queue at a given level L
538
- * consists of nodequeue[L] which is the children of a BNodeInternal,
539
- * and nodeindex[L], the current index within that child list, such
540
- * such that nodequeue[L-1] === nodequeue[L][nodeindex[L]].children.
541
- * (However inside this function the order is reversed.)
542
- * @private
543
- */
544
- findPath(key, fromEnd = false) {
545
- let nextnode = this.root;
546
- let nodequeue, nodeindex;
547
- if (nextnode.isLeaf) {
548
- // use preallocated empty
549
- nodequeue = EmptyArray;
550
- nodeindex = EmptyArray;
551
- }
552
- else {
553
- nodequeue = [];
554
- nodeindex = [];
555
- for (let d = 0; !nextnode.isLeaf; d++) {
556
- nodequeue[d] = nextnode.children;
557
- if (key !== undefined)
558
- nodeindex[d] = nextnode.indexOf(key, 0, this._compare);
559
- else
560
- nodeindex[d] = fromEnd ? nodequeue[d].length - 1 : 0;
561
- if (nodeindex[d] >= nodequeue[d].length)
562
- return; // first key > maxKey()
563
- nextnode = nodequeue[d][nodeindex[d]];
564
- }
565
- nodequeue.reverse();
566
- nodeindex.reverse();
567
- }
568
- return { nodequeue, nodeindex, leaf: nextnode };
569
- }
570
- /** Returns a new iterator for iterating the keys of each pair in ascending order.
571
- * @param firstKey: Minimum key to include in the output. */
572
- keys(firstKey) {
573
- const it = this.entries(firstKey, ReusedArray);
574
- return iterator(() => {
575
- const n = it.next();
576
- if (n.value)
577
- n.value = n.value[0];
578
- return n;
579
- });
580
- }
581
- /** Gets the lowest key in the tree. Complexity: O(log size) */
582
- minKey() {
583
- return this.root.minKey();
584
- }
585
- /** Gets the highest key in the tree. Complexity: O(1) */
586
- maxKey() {
587
- return this.root.maxKey();
588
- }
589
- /** Returns a new iterator for iterating the values of each pair in order by key.
590
- * @param firstKey: Minimum key whose associated value is included in the output. */
591
- values(firstKey) {
592
- const it = this.entries(firstKey, ReusedArray);
593
- return iterator(() => {
594
- const n = it.next();
595
- if (n.value)
596
- n.value = n.value[1];
597
- return n;
598
- });
599
- }
600
- /// //////////////////////////////////////////////////////////////////////////
601
- // Additional methods ///////////////////////////////////////////////////////
602
- /** Returns the maximum number of children/values before nodes will split. */
603
- get maxNodeSize() {
604
- return this._maxNodeSize;
605
- }
606
- // /** Quickly clones the tree by marking the root node as shared.
607
- // * Both copies remain editable. When you modify either copy, any
608
- // * nodes that are shared (or potentially shared) between the two
609
- // * copies are cloned so that the changes do not affect other copies.
610
- // * This is known as copy-on-write behavior, or "lazy copying". */
611
- // clone(): BTree<K, V> {
612
- // this.root.isShared = true
613
- // let result = new BTree<K, V>(undefined, this._compare, this._maxNodeSize)
614
- // result.root = this.root
615
- // result.count = this.count
616
- // return result
617
- // }
618
- /** Performs a deep cloning of the tree, immediately duplicating any nodes that are
619
- * not currently marked as shared, in order to avoid marking any
620
- * additional nodes as shared.
621
- * @param force Clone all nodes, even shared ones.
622
- */
623
- clone(force = false) {
624
- const result = new BTree(undefined, this._compare, this._maxNodeSize);
625
- result.root = this.root.greedyClone(force);
626
- result.count = this.count;
627
- return result;
628
- }
629
- /** Gets an array filled with the contents of the tree, sorted by key */
630
- toArray() {
631
- return [...this.entries()];
632
- }
633
- /** Gets a string representing the tree's data based on toArray(). */
634
- toString() {
635
- return this.toArray().toString();
636
- }
637
- /** Returns the next pair whose key is larger than the specified key (or undefined if there is none).
638
- * If key === undefined, this function returns the lowest pair.
639
- * @param key The key to search for.
640
- * @param reusedArray Optional array used repeatedly to store key-value pairs, to
641
- * avoid creating a new array on every iteration.
642
- */
643
- lowerBound(key, reusedArray) {
644
- reusedArray ||= [];
645
- if (key === undefined) {
646
- return this.root.minPair(reusedArray);
647
- }
648
- return this.root.getPairOrNextHigher(key, this._compare, false, reusedArray);
649
- }
650
- /** Returns the next key larger than the specified key, or undefined if there is none.
651
- * Also, nextHigherKey(undefined) returns the lowest key.
652
- */
653
- // nextHigherKey(key: K | undefined): K | undefined {
654
- // let p = this.after(key, ReusedArray as [K, V])
655
- // return p && p[0]
656
- // }
657
- /** Returns the next pair whose key is smaller than the specified key (or undefined if there is none).
658
- * If key === undefined, this function returns the highest pair.
659
- * @param key The key to search for.
660
- * @param reusedArray Optional array used repeatedly to store key-value pairs, to
661
- * avoid creating a new array each time you call this method.
662
- */
663
- upperBound(key, reusedArray) {
664
- reusedArray ||= [];
665
- if (key === undefined) {
666
- return this.root.maxPair(reusedArray);
667
- }
668
- return this.root.getPairOrNextLower(key, this._compare, false, reusedArray);
669
- }
670
- /** Returns the next key smaller than the specified key, or undefined if there is none.
671
- * Also, nextLowerKey(undefined) returns the highest key.
672
- */
673
- // nextLowerKey(key: K | undefined): K | undefined {
674
- // let p = this.before(key, ReusedArray as [K, V])
675
- // return p && p[0]
676
- // }
677
- /** Returns the key-value pair associated with the supplied key if it exists
678
- * or the pair associated with the next lower pair otherwise. If there is no
679
- * next lower pair, undefined is returned.
680
- * @param key The key to search for.
681
- * @param reusedArray Optional array used repeatedly to store key-value pairs, to
682
- * avoid creating a new array each time you call this method.
683
- * */
684
- floor(key, reusedArray) {
685
- return this.root.getPairOrNextLower(key, this._compare, true, reusedArray || []);
686
- }
687
- /** Returns the key-value pair associated with the supplied key if it exists
688
- * or the next higher pair otherwise. If there is no next higher pair,
689
- * undefined is returned.
690
- * @param key The key to search for.
691
- * @param reusedArray Optional array used repeatedly to store key-value pairs, to
692
- * avoid creating a new array each time you call this method.
693
- * */
694
- ceiling(key, reusedArray) {
695
- return this.root.getPairOrNextHigher(key, this._compare, true, reusedArray || []);
696
- }
697
- /**
698
- * Builds an array of pairs from the specified range of keys, sorted by key.
699
- * Each returned pair is also an array: pair[0] is the key, pair[1] is the value.
700
- * @param low The first key in the array will be greater than or equal to `low`.
701
- * @param high This method returns when a key larger than this is reached.
702
- * @param includeHigh If the `high` key is present, its pair will be included
703
- * in the output if and only if this parameter is true. Note: if the
704
- * `low` key is present, it is included in the output unless `low`
705
- * equals `high` and this parameter is false.
706
- * @param maxLength Length limit. keysRange will stop scanning the tree when
707
- * the array reaches this size.
708
- * @description Computational complexity: O(result.length + log size)
709
- */
710
- keysRange(low, high, includeHigh, maxLength = 0x3ffffff) {
711
- const results = [];
712
- this.root.forRange(low, high, includeHigh, false, this, 0, (k, v) => {
713
- if (results.length >= maxLength)
714
- return Break;
715
- results.push([k, v]);
716
- });
717
- return results;
718
- }
719
- /** Adds all pairs from a list of key-value pairs.
720
- * @param entries Pairs to add to this tree. If there are duplicate keys,
721
- * later pairs currently overwrite earlier ones (e.g. [[0,1],[0,7]]
722
- * associates 0 with 7.)
723
- * @returns The number of pairs added to the collection.
724
- * @description Computational complexity: O(pairs.length * log(size + pairs.length))
725
- */
726
- extend(entries) {
727
- let added = 0;
728
- for (const pair of entries) {
729
- if (this.set(pair[0], pair[1] ?? null))
730
- added++;
731
- }
732
- return added;
733
- }
734
- /**
735
- * Scans the specified range of keys, in ascending order by key.
736
- * Note: the callback `onFound` must not insert or remove items in the
737
- * collection. Doing so may cause incorrect data to be sent to the
738
- * callback afterward.
739
- * @param low The first key scanned will be greater than or equal to `low`.
740
- * @param high Scanning stops when a key larger than this is reached.
741
- * @param includeHigh If the `high` key is present, `onFound` is called for
742
- * that final pair if and only if this parameter is true.
743
- * @param onFound A function that is called for each key-value pair. This
744
- * function can return {done:R} to stop early with result R.
745
- * @param initialCounter Initial third argument of onFound. This value
746
- * increases by one each time `onFound` is called. Default: 0
747
- * @returns The number of values found, or R if the callback returned
748
- * `{done:R}` to stop early.
749
- * @description Computational complexity: O(number of items scanned + log size)
750
- */
751
- rangeForEach(low, high, includeHigh, onFound, initialCounter) {
752
- const r = this.root.forRange(low, high, includeHigh, false, this, initialCounter || 0, onFound);
753
- return typeof r === 'number' ? r : r.done;
754
- }
755
- /**
756
- * Scans and potentially modifies values for a subsequence of keys.
757
- * Note: the callback `onFound` should ideally be a pure function.
758
- * Specifically, it must not insert items, call clone(), or change
759
- * the collection except via return value; out-of-band editing may
760
- * cause an exception or may cause incorrect data to be sent to
761
- * the callback (duplicate or missed items). It must not cause a
762
- * clone() of the collection, otherwise the clone could be modified
763
- * by changes requested by the callback.
764
- * @param low The first key scanned will be greater than or equal to `low`.
765
- * @param high Scanning stops when a key larger than this is reached.
766
- * @param includeHigh If the `high` key is present, `onFound` is called for
767
- * that final pair if and only if this parameter is true.
768
- * @param onFound A function that is called for each key-value pair. This
769
- * function can return `{value:v}` to change the value associated
770
- * with the current key, `{delete:true}` to delete the current pair,
771
- * `{done:R}` to stop early with result R, or it can return nothing
772
- * (undefined, null or {}) to cause no effect and continue iterating.
773
- * `{done:R}` can be combined with one of the other two commands.
774
- * The third argument `counter` is the number of items iterated
775
- * previously; it equals 0 when `onFound` is called the first time.
776
- * @returns The number of values scanned, or R if the callback returned
777
- * `{done:R}` to stop early.
778
- * @description
779
- * Computational complexity: O(number of items scanned + log size)
780
- * Note: if the tree has been cloned with clone(), any shared
781
- * nodes are copied before `onFound` is called. This takes O(n) time
782
- * where n is proportional to the amount of shared data scanned.
783
- */
784
- rangeUpdate(low, high, includeHigh, onFound, initialCounter) {
785
- let root = this.root;
786
- if (root.isShared)
787
- this.root = root = root.clone();
788
- try {
789
- const r = root.forRange(low, high, includeHigh, true, this, initialCounter || 0, onFound);
790
- return typeof r === 'number' ? r : r.done;
791
- }
792
- finally {
793
- let isShared;
794
- while (root.keys.length <= 1 && !root.isLeaf) {
795
- isShared ||= root.isShared;
796
- this.root = root =
797
- root.keys.length === 0 ? EmptyLeaf : root.children[0];
798
- }
799
- // If any ancestor of the new root was shared, the new root must also be shared
800
- if (isShared) {
801
- root.isShared = true;
802
- }
803
- }
804
- }
805
- /**
806
- * Removes a range of key-value pairs from the B+ tree.
807
- * @param low The first key scanned will be greater than or equal to `low`.
808
- * @param high Scanning stops when a key larger than this is reached.
809
- * @param includeHigh Specifies whether the `high` key, if present, is deleted.
810
- * @returns The number of key-value pairs that were deleted.
811
- * @description Computational complexity: O(log size + number of items deleted)
812
- */
813
- removeRange(low, high, includeHigh) {
814
- return this.rangeUpdate(low, high, includeHigh, DeleteRange);
815
- }
816
- /** Removes a series of keys from the collection. */
817
- removeKeys(keys) {
818
- let r = 0;
819
- for (let i = 0; i < keys.length; i++)
820
- if (this.remove(keys[i]))
821
- r++;
822
- return r;
823
- }
824
- /** Gets the height of the tree: the number of internal nodes between the
825
- * BTree object and its leaf nodes (zero if there are no internal nodes). */
826
- get height() {
827
- let node = this.root;
828
- let height = -1;
829
- while (node) {
830
- height++;
831
- node = node.isLeaf ? undefined : node?.children?.[0];
832
- }
833
- return height;
834
- }
835
- /** Makes the object read-only to ensure it is not accidentally modified.
836
- * This is accomplished by replacing mutator functions with a function
837
- * that throws an Error. Compared to using a property (e.g. this.isFrozen)
838
- * this implementation gives better performance in non-frozen BTrees.
839
- */
840
- freeze() {
841
- const t = this;
842
- // Note: all other mutators ultimately call set() or rangeUpdate()
843
- // so we don't need to override those others.
844
- t.clear =
845
- t.set =
846
- t.rangeUpdate =
847
- () => {
848
- throw TypeError('Attempted to modify a frozen BTree');
849
- };
850
- }
851
- [Symbol.iterator]() {
852
- return this.entries();
853
- }
854
- }
855
- function iterator(next = () => ({ done: true, value: undefined })) {
856
- return {
857
- next,
858
- [Symbol.iterator]() {
859
- return this;
860
- }
861
- };
862
- }
863
- /** Internal node (non-leaf node) ********************************************/
864
- class BNodeInternal extends BNode {
865
- // Note: conventionally B+ trees have one fewer key than the number of
866
- // children, but I find it easier to keep the array lengths equal: each
867
- // keys[i] caches the value of children[i].maxKey().
868
- children;
869
- /**
870
- * This does not mark `children` as shared, so it is the responsibility of the caller
871
- * to ensure children are either marked shared, or aren't included in another tree.
872
- */
873
- constructor(children, keys) {
874
- if (!keys) {
875
- keys = [];
876
- for (let i = 0; i < children.length; i++)
877
- keys[i] = children[i].maxKey();
878
- }
879
- super(keys);
880
- this.children = children;
881
- }
882
- clone() {
883
- const children = this.children.slice(0);
884
- for (let i = 0; i < children.length; i++)
885
- children[i].isShared = true;
886
- return new BNodeInternal(children, this.keys.slice(0));
887
- }
888
- greedyClone(force) {
889
- if (this.isShared && !force)
890
- return this;
891
- const nu = new BNodeInternal(this.children.slice(0), this.keys.slice(0));
892
- for (let i = 0; i < nu.children.length; i++)
893
- nu.children[i] = nu.children[i].greedyClone(force);
894
- return nu;
895
- }
896
- minKey() {
897
- return this.children[0].minKey();
898
- }
899
- minPair(reusedArray) {
900
- return this.children[0].minPair(reusedArray);
901
- }
902
- maxPair(reusedArray) {
903
- return this.children.at(-1).maxPair(reusedArray);
904
- }
905
- get(key, defaultValue, tree) {
906
- const i = this.indexOf(key, 0, tree._compare);
907
- const children = this.children;
908
- return i < children.length ? children[i].get(key, defaultValue, tree) : defaultValue;
909
- }
910
- getPairOrNextLower(key, compare, inclusive, reusedArray) {
911
- const i = this.indexOf(key, 0, compare);
912
- const children = this.children;
913
- if (i >= children.length)
914
- return this.maxPair(reusedArray);
915
- const result = children[i].getPairOrNextLower(key, compare, inclusive, reusedArray);
916
- if (result === undefined && i > 0) {
917
- return children[i - 1].maxPair(reusedArray);
918
- }
919
- return result;
920
- }
921
- getPairOrNextHigher(key, compare, inclusive, reusedArray) {
922
- const i = this.indexOf(key, 0, compare);
923
- const children = this.children;
924
- const length = children.length;
925
- if (i >= length)
926
- return undefined;
927
- const result = children[i].getPairOrNextHigher(key, compare, inclusive, reusedArray);
928
- if (result === undefined && i < length - 1) {
929
- return children[i + 1].minPair(reusedArray);
930
- }
931
- return result;
932
- }
933
- checkValid(depth, tree, baseIndex) {
934
- const kL = this.keys.length;
935
- const cL = this.children.length;
936
- assert(kL === cL, 'keys/children length mismatch: depth', depth, 'lengths', kL, cL, 'baseIndex', baseIndex);
937
- assert(kL > 1 || depth > 0, 'internal node has length', kL, 'at depth', depth, 'baseIndex', baseIndex);
938
- let size = 0;
939
- const c = this.children;
940
- const k = this.keys;
941
- let childSize = 0;
942
- for (let i = 0; i < cL; i++) {
943
- size += c[i].checkValid(depth + 1, tree, baseIndex + size);
944
- childSize += c[i].keys.length;
945
- assert(size >= childSize, 'wtf', baseIndex); // no way this will ever fail
946
- assert(i === 0 || c[i - 1].constructor === c[i].constructor, 'type mismatch, baseIndex:', baseIndex);
947
- if (c[i].maxKey() !== k[i])
948
- assert(false, 'keys[', i, '] =', k[i], 'is wrong, should be ', c[i].maxKey(), 'at depth', depth, 'baseIndex', baseIndex);
949
- if (!(i === 0 || tree._compare(k[i - 1], k[i]) < 0))
950
- assert(false, 'sort violation at depth', depth, 'index', i, 'keys', k[i - 1], k[i]);
951
- }
952
- // 2020/08: BTree doesn't always avoid grossly undersized nodes,
953
- // but AFAIK such nodes are pretty harmless, so accept them.
954
- const toofew = childSize === 0; // childSize < (tree.maxNodeSize >> 1)*cL;
955
- if (toofew || childSize > tree.maxNodeSize * cL)
956
- assert(false, toofew ? 'too few' : 'too many', 'children (', childSize, size, ') at depth', depth, 'maxNodeSize:', tree.maxNodeSize, 'children.length:', cL, 'baseIndex:', baseIndex);
957
- return size;
958
- }
959
- /// //////////////////////////////////////////////////////////////////////////
960
- // Internal Node: set & node splitting //////////////////////////////////////
961
- set(key, value, overwrite, tree) {
962
- const c = this.children;
963
- const max = tree._maxNodeSize;
964
- const cmp = tree._compare;
965
- let i = Math.min(this.indexOf(key, 0, cmp), c.length - 1);
966
- let child = c[i];
967
- if (child.isShared)
968
- c[i] = child = child.clone();
969
- if (child.keys.length >= max) {
970
- // child is full; inserting anything else will cause a split.
971
- // Shifting an item to the left or right sibling may avoid a split.
972
- // We can do a shift if the adjacent node is not full and if the
973
- // current key can still be placed in the same node after the shift.
974
- let other;
975
- if (i > 0 && (other = c[i - 1]).keys.length < max && cmp(child.keys[0], key) < 0) {
976
- if (other.isShared)
977
- c[i - 1] = other = other.clone();
978
- other.takeFromRight(child);
979
- this.keys[i - 1] = other.maxKey();
980
- }
981
- else if ((other = c[i + 1]) !== undefined &&
982
- other.keys.length < max &&
983
- cmp(child.maxKey(), key) < 0) {
984
- if (other.isShared)
985
- c[i + 1] = other = other.clone();
986
- other.takeFromLeft(child);
987
- this.keys[i] = c[i].maxKey();
988
- }
989
- }
990
- const result = child.set(key, value, overwrite, tree);
991
- this.keys[i] = child.maxKey();
992
- if (result === false)
993
- return false;
994
- if (result === true)
995
- return true;
996
- // The child has split and `result` is a new right child... does it fit?
997
- if (this.keys.length < max) {
998
- // yes
999
- this.insert(i + 1, result);
1000
- return true;
1001
- }
1002
- else {
1003
- // no, we must split also
1004
- const newRightSibling = this.splitOffRightSide();
1005
- let target = this;
1006
- if (cmp(result.maxKey(), this.maxKey()) > 0) {
1007
- target = newRightSibling;
1008
- i -= this.keys.length;
1009
- }
1010
- target.insert(i + 1, result);
1011
- return newRightSibling;
1012
- }
1013
- }
1014
- /**
1015
- * Inserts `child` at index `i`.
1016
- * This does not mark `child` as shared, so it is the responsibility of the caller
1017
- * to ensure that either child is marked shared, or it is not included in another tree.
1018
- */
1019
- insert(i, child) {
1020
- this.children.splice(i, 0, child);
1021
- this.keys.splice(i, 0, child.maxKey());
1022
- }
1023
- /**
1024
- * Split this node.
1025
- * Modifies this to remove the second half of the items, returning a separate node containing them.
1026
- */
1027
- splitOffRightSide() {
1028
- // assert !this.isShared;
1029
- const half = this.children.length >> 1;
1030
- return new BNodeInternal(this.children.splice(half), this.keys.splice(half));
1031
- }
1032
- takeFromRight(rhs) {
1033
- // Reminder: parent node must update its copy of key for this node
1034
- // assert: neither node is shared
1035
- // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
1036
- this.keys.push(rhs.keys.shift());
1037
- this.children.push(rhs.children.shift());
1038
- }
1039
- takeFromLeft(lhs) {
1040
- // Reminder: parent node must update its copy of key for this node
1041
- // assert: neither node is shared
1042
- // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
1043
- this.keys.unshift(lhs.keys.pop());
1044
- this.children.unshift(lhs.children.pop());
1045
- }
1046
- /// //////////////////////////////////////////////////////////////////////////
1047
- // Internal Node: scanning & deletions //////////////////////////////////////
1048
- // Note: `count` is the next value of the third argument to `onFound`.
1049
- // A leaf node's `forRange` function returns a new value for this counter,
1050
- // unless the operation is to stop early.
1051
- forRange(low, high, includeHigh, editMode, tree, count, onFound) {
1052
- const cmp = tree._compare;
1053
- const keys = this.keys;
1054
- const children = this.children;
1055
- let iLow = this.indexOf(low, 0, cmp);
1056
- let i = iLow;
1057
- const iHigh = Math.min(high === low ? iLow : this.indexOf(high, 0, cmp), keys.length - 1);
1058
- if (!editMode) {
1059
- // Simple case
1060
- for (; i <= iHigh; i++) {
1061
- const result = children[i].forRange(low, high, includeHigh, editMode, tree, count, onFound);
1062
- if (typeof result !== 'number')
1063
- return result;
1064
- count = result;
1065
- }
1066
- }
1067
- else if (i <= iHigh) {
1068
- try {
1069
- for (; i <= iHigh; i++) {
1070
- if (children[i].isShared)
1071
- children[i] = children[i].clone();
1072
- const result = children[i].forRange(low, high, includeHigh, editMode, tree, count, onFound);
1073
- // Note: if children[i] is empty then keys[i]=undefined.
1074
- // This is an invalid state, but it is fixed below.
1075
- keys[i] = children[i].maxKey();
1076
- if (typeof result !== 'number')
1077
- return result;
1078
- count = result;
1079
- }
1080
- }
1081
- finally {
1082
- // Deletions may have occurred, so look for opportunities to merge nodes.
1083
- const half = tree._maxNodeSize >> 1;
1084
- if (iLow > 0)
1085
- iLow--;
1086
- for (i = iHigh; i >= iLow; i--) {
1087
- if (children[i].keys.length <= half) {
1088
- if (children[i].keys.length !== 0) {
1089
- this.tryMerge(i, tree._maxNodeSize);
1090
- }
1091
- else {
1092
- // child is empty! delete it!
1093
- keys.splice(i, 1);
1094
- children.splice(i, 1);
1095
- }
1096
- }
1097
- }
1098
- if (children.length !== 0 && children[0].keys.length === 0)
1099
- assert(false, 'emptiness bug');
1100
- }
1101
- }
1102
- return count;
1103
- }
1104
- /** Merges child i with child i+1 if their combined size is not too large */
1105
- tryMerge(i, maxSize) {
1106
- const children = this.children;
1107
- if (i >= 0 && i + 1 < children.length) {
1108
- if (children[i].keys.length + children[i + 1].keys.length <= maxSize) {
1109
- if (children[i].isShared)
1110
- // cloned already UNLESS i is outside scan range
1111
- children[i] = children[i].clone();
1112
- children[i].mergeSibling(children[i + 1], maxSize);
1113
- children.splice(i + 1, 1);
1114
- this.keys.splice(i + 1, 1);
1115
- this.keys[i] = children[i].maxKey();
1116
- return true;
1117
- }
1118
- }
1119
- return false;
1120
- }
1121
- /**
1122
- * Move children from `rhs` into this.
1123
- * `rhs` must be part of this tree, and be removed from it after this call
1124
- * (otherwise isShared for its children could be incorrect).
1125
- */
1126
- mergeSibling(rhs, maxNodeSize) {
1127
- // assert !this.isShared;
1128
- const oldLength = this.keys.length;
1129
- this.keys.push.apply(this.keys, rhs.keys);
1130
- const rhsChildren = rhs.children;
1131
- this.children.push.apply(this.children, rhsChildren);
1132
- if (rhs.isShared && !this.isShared) {
1133
- // All children of a shared node are implicitly shared, and since their new
1134
- // parent is not shared, they must now be explicitly marked as shared.
1135
- for (let i = 0; i < rhsChildren.length; i++)
1136
- rhsChildren[i].isShared = true;
1137
- }
1138
- // If our children are themselves almost empty due to a mass-delete,
1139
- // they may need to be merged too (but only the oldLength-1 and its
1140
- // right sibling should need this).
1141
- this.tryMerge(oldLength - 1, maxNodeSize);
1142
- }
1143
- }