@x-oasis/integer-buffer-set 0.1.19 → 0.1.23

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/src/index.ts CHANGED
@@ -1,27 +1,25 @@
1
- // import invariant from 'invariant';
2
1
  import Heap from '@x-oasis/heap';
2
+ import isClamped from '@x-oasis/is-clamped';
3
+ import invariant from '@x-oasis/invariant';
4
+ import returnHook, { ReturnHook } from '@x-oasis/return-hook';
5
+ import {
6
+ HeapItem,
7
+ SafeRange,
8
+ MetaExtractor,
9
+ IndexExtractor,
10
+ IntegerBufferSetProps,
11
+ MetaToIndexMap,
12
+ MetaToPositionMap,
13
+ IndexToMetaMap,
14
+ } from './types';
3
15
 
4
- type HeapItem = {
5
- position: number;
6
- value: number;
7
- };
8
-
9
- const defaultUseMinValueFn = (options: {
10
- safeRange: {
11
- lowValue: number;
12
- highValue: number;
13
- };
14
- bufferSetRange: {
15
- maxValue: number;
16
- minValue: number;
17
- };
18
- currentIndex: number;
19
- }) => {
20
- const { safeRange, bufferSetRange } = options;
21
- const { lowValue, highValue } = safeRange;
22
- const { maxValue, minValue } = bufferSetRange;
23
- return lowValue - minValue > maxValue - highValue;
24
- };
16
+ const defaultMetaExtractor = (value) => value;
17
+ export const defaultBufferSize = 10;
18
+
19
+ // !!!!! should do meta validation...meta should has an index...
20
+ // value: original data `index` value
21
+ // value(index) => meta => position
22
+ // `index to getIndices, meta to find index`
25
23
 
26
24
  // Data structure that allows to store values and assign positions to them
27
25
  // in a way to minimize changing positions of stored values when new ones are
@@ -34,64 +32,147 @@ const defaultUseMinValueFn = (options: {
34
32
  // and get it's position back
35
33
  // All operations take amortized log(n) time where n is number of elements in
36
34
  // the set.
37
- class IntegerBufferSet {
38
- private _size: number;
39
- private _valueToPositionMap: {
40
- [key: string]: number;
41
- };
35
+ // feature: add / delete / update item will also in consider..
36
+ class IntegerBufferSet<Meta = any> {
37
+ private _name: string;
38
+ private _bufferSize: number;
39
+
40
+ private _indexToMetaMap: IndexToMetaMap<Meta>;
41
+ private _metaToPositionMap: MetaToPositionMap<Meta>;
42
+ private _positionToMetaList: Array<Meta>;
43
+ private _metaToIndexMap: MetaToIndexMap<Meta>;
44
+
42
45
  private _smallValues: Heap<HeapItem>;
43
46
  private _largeValues: Heap<HeapItem>;
44
- private _vacantPositions: Array<number>;
47
+ private _metaExtractor: MetaExtractor<Meta>;
48
+ private _indexExtractor: IndexExtractor<Meta>;
49
+
50
+ private _onTheFlyIndices: Array<Meta>;
51
+
52
+ private _isOnTheFlyFull: boolean;
53
+ private _isOnTheFlyFullReturnHook: ReturnHook;
54
+
55
+ private _loopMS: number;
56
+ private _lastUpdatedMS: number;
57
+
58
+ constructor(props: IntegerBufferSetProps<Meta> = {}) {
59
+ const {
60
+ name = 'default_buffer',
61
+ indexExtractor,
62
+ bufferSize = defaultBufferSize,
63
+ metaExtractor = defaultMetaExtractor,
64
+ } = props;
65
+ this._metaExtractor = metaExtractor;
66
+ this._indexExtractor = indexExtractor;
67
+
68
+ this._name = name;
69
+
70
+ /**
71
+ * this._indexToMetaMap is used to find the prev meta when finding a position for index.
72
+ */
73
+ this._indexToMetaMap = new Map();
74
+ this._metaToPositionMap = new Map();
75
+ this._positionToMetaList = [];
76
+ this._metaToIndexMap = new Map();
77
+ this._onTheFlyIndices = [];
78
+
79
+ this._bufferSize = bufferSize;
45
80
 
46
- constructor() {
47
- this._valueToPositionMap = {};
48
- this._size = 0;
49
81
  this._smallValues = new Heap([], this._smallerComparator);
50
82
  this._largeValues = new Heap([], this._greaterComparator);
51
83
 
52
- this.getNewPositionForValue = this.getNewPositionForValue.bind(this);
53
- this.getValuePosition = this.getValuePosition.bind(this);
54
- this.getSize = this.getSize.bind(this);
55
- this.replaceFurthestValuePosition =
56
- this.replaceFurthestValuePosition.bind(this);
84
+ this.getNewPositionForIndex = this.getNewPositionForIndex.bind(this);
85
+ this.getIndexPosition = this.getIndexPosition.bind(this);
86
+ this.replacePositionInFliedIndices =
87
+ this.replacePositionInFliedIndices.bind(this);
88
+ this.replaceFurthestIndexPosition =
89
+ this.replaceFurthestIndexPosition.bind(this);
90
+ this._isOnTheFlyFullReturnHook = returnHook(
91
+ this.setIsOnTheFlyFull.bind(this)
92
+ );
57
93
 
58
- this._vacantPositions = [];
94
+ this._loopMS = Date.now();
95
+ this._lastUpdatedMS = this._loopMS;
59
96
  }
60
97
 
61
- getSize() {
62
- return this._size;
98
+ get bufferSize() {
99
+ return this._bufferSize;
63
100
  }
64
101
 
65
- get indices() {
66
- const indices = [];
67
- for (const key in this._valueToPositionMap) {
68
- const value = this._valueToPositionMap[key];
69
- indices[value] = key;
102
+ setIsOnTheFlyFull(val: any) {
103
+ if (val != null) {
104
+ const data = this._onTheFlyIndices.filter((v) => v);
105
+ this._isOnTheFlyFull = data.length === this._bufferSize;
70
106
  }
71
- return indices;
72
107
  }
73
108
 
74
- getValuePosition(value: number): null | number {
75
- if (this._valueToPositionMap[value] === undefined) {
76
- return null;
109
+ get isBufferFull() {
110
+ return this._positionToMetaList.length >= this._bufferSize;
111
+ }
112
+
113
+ getOnTheFlyUncriticalPosition(safeRange: SafeRange) {
114
+ const { startIndex, endIndex } = safeRange;
115
+ for (let idx = 0; idx < this._onTheFlyIndices.length; idx++) {
116
+ const meta = this._onTheFlyIndices[idx];
117
+ const metaIndex = this._metaToIndexMap.get(meta);
118
+ if (!isClamped(startIndex, metaIndex, endIndex)) {
119
+ return idx;
120
+ }
77
121
  }
78
- return this._valueToPositionMap[value];
122
+ return null;
79
123
  }
80
124
 
81
- getNewPositionForValue(value: number) {
82
- if (this._valueToPositionMap[value] !== undefined) {
83
- console.warn(
84
- "Shouldn't try to find new position for value already stored in BufferSet"
85
- );
125
+ initialize() {
126
+ return {
127
+ smallValues: new Heap([], this._smallerComparator),
128
+ largeValues: new Heap([], this._greaterComparator),
129
+ valueToPositionObject: {},
130
+ };
131
+ }
132
+
133
+ getIndexMeta(index: number) {
134
+ return this._metaExtractor(index);
135
+ }
136
+
137
+ getMetaIndex(meta: Meta) {
138
+ if (this._indexExtractor) return this._indexExtractor(meta);
139
+ return this._metaToIndexMap.get(meta);
140
+ }
141
+
142
+ setMetaIndex(meta: Meta, index: number) {
143
+ if (!this._indexExtractor) {
144
+ return this._metaToIndexMap.set(meta, index);
86
145
  }
87
- // invariant(
88
- // this._valueToPositionMap[value] === undefined,
89
- // "Shouldn't try to find new position for value already stored in BufferSet"
90
- // );
91
- const newPosition = this._size;
92
- this._size++;
93
- this._pushToHeaps(newPosition, value);
94
- this._valueToPositionMap[value] = newPosition;
146
+ return false;
147
+ }
148
+
149
+ deleteMetaIndex(meta: Meta) {
150
+ return this._metaToIndexMap.delete(meta);
151
+ }
152
+
153
+ replaceMetaToIndexMap(newMetaToIndexMap: MetaToIndexMap<Meta>) {
154
+ if (!this._indexExtractor) {
155
+ return (this._metaToIndexMap = newMetaToIndexMap);
156
+ }
157
+ return false;
158
+ }
159
+
160
+ getIndexPosition(index: number): undefined | number {
161
+ return this.getMetaIndex(this.getIndexMeta(index));
162
+ }
163
+
164
+ getNewPositionForIndex(index: number) {
165
+ const meta = this.getIndexMeta(index);
166
+ invariant(
167
+ this._metaToPositionMap.get(meta) === undefined,
168
+ "Shouldn't try to find new position for value already stored in BufferSet"
169
+ );
170
+ const newPosition = this._positionToMetaList.length;
171
+
172
+ this._pushToHeaps(newPosition, index);
173
+ this._setMetaIndex(meta, index);
174
+ this._setMetaPosition(meta, newPosition);
175
+
95
176
  return newPosition;
96
177
  }
97
178
 
@@ -103,110 +184,358 @@ class IntegerBufferSet {
103
184
  return this._largeValues.peek()?.value;
104
185
  }
105
186
 
106
- setPositionValue(position: number, value: number) {
107
- const originalPosition = this._valueToPositionMap[value];
108
- if (originalPosition !== undefined) {
109
- const index = this._vacantPositions.findIndex(
110
- (v) => v === originalPosition
111
- );
112
- if (index === -1) this._vacantPositions.push(originalPosition);
113
- delete this._valueToPositionMap[value];
114
- this._valueToPositionMap[value] = position;
187
+ replacePositionInFliedIndices(newIndex: number, safeRange: SafeRange) {
188
+ const { startIndex, endIndex } = safeRange;
189
+
190
+ if (this._isOnTheFlyFull) {
191
+ // newIndex is not critical index, do nothing
192
+ if (!isClamped(startIndex, newIndex, endIndex)) {
193
+ return null;
194
+ }
195
+ // if `newIndex` is critical index, replace an un-committed
196
+ // index value from _onTheFlyIndices.
197
+ const pos = this.getOnTheFlyUncriticalPosition(safeRange);
198
+ if (pos != null) return pos;
115
199
  }
200
+ return null;
116
201
  }
117
202
 
118
- replaceFurthestValuePosition(
119
- lowValue: number,
120
- highValue: number,
121
- newValue: number,
122
- useMinValueFn: (options: {
123
- safeRange: {
124
- lowValue: number;
125
- highValue: number;
126
- };
127
- bufferSetRange: {
128
- maxValue: number;
129
- minValue: number;
130
- };
131
- currentIndex: number;
132
- }) => boolean = defaultUseMinValueFn
133
- ): null | number {
134
- if (this._valueToPositionMap[newValue] !== undefined) {
135
- console.warn(
136
- "Shouldn't try to replace values with value already stored value in " +
137
- 'BufferSet'
203
+ getFliedPosition(newIndex: number, safeRange: SafeRange) {
204
+ const pos = this.replacePositionInFliedIndices(newIndex, safeRange);
205
+ if (pos != null) {
206
+ const meta = this.getIndexMeta(newIndex);
207
+ this._onTheFlyIndices[pos] = meta;
208
+ this._setMetaIndex(meta, newIndex);
209
+ return this._isOnTheFlyFullReturnHook(pos);
210
+ }
211
+ return null;
212
+ }
213
+
214
+ /**
215
+ *
216
+ * @param newIndex
217
+ * @param safeRange
218
+ * @returns
219
+ *
220
+ *
221
+ * _positionToMetaList maybe undefined on next loop
222
+ */
223
+ getPosition(newIndex: number, safeRange?: SafeRange) {
224
+ this.prepare();
225
+ const meta = this.getIndexMeta(newIndex);
226
+ const prevMetaPosition = this._metaToPositionMap.get(meta);
227
+
228
+ if (prevMetaPosition !== undefined) {
229
+ const onTheFlyPositionMeta = this._onTheFlyIndices[prevMetaPosition];
230
+ // the occupied meta should change position
231
+ if (onTheFlyPositionMeta) {
232
+ // such as place item 11 twice...
233
+ if (onTheFlyPositionMeta === meta) {
234
+ return prevMetaPosition;
235
+ }
236
+ let positionToReplace = this._replaceFurthestIndexPosition(
237
+ newIndex,
238
+ safeRange
239
+ );
240
+ if (this._isOnTheFlyFull)
241
+ return this.getFliedPosition(newIndex, safeRange);
242
+
243
+ while (this._onTheFlyIndices[positionToReplace]) {
244
+ positionToReplace = this._replaceFurthestIndexPosition(
245
+ newIndex,
246
+ safeRange
247
+ );
248
+ }
249
+
250
+ if (positionToReplace != null) {
251
+ this._setMetaIndex(meta, newIndex);
252
+ this._onTheFlyIndices[positionToReplace] = onTheFlyPositionMeta;
253
+ return this._isOnTheFlyFullReturnHook(positionToReplace);
254
+ }
255
+ }
256
+ this._onTheFlyIndices[prevMetaPosition] = meta;
257
+ return this._isOnTheFlyFullReturnHook(prevMetaPosition);
258
+ }
259
+
260
+ // placed on new buffered position
261
+ if (!this.isBufferFull)
262
+ return this._isOnTheFlyFullReturnHook(
263
+ this.getNewPositionForIndex(newIndex)
264
+ );
265
+
266
+ if (this._isOnTheFlyFull) return this.getFliedPosition(newIndex, safeRange);
267
+
268
+ let positionToReplace;
269
+ const prevIndexMeta = this._indexToMetaMap.get(newIndex);
270
+
271
+ // Index has already been stored, but we cant use its old position directly...
272
+ // 1:index -> meta, meta may be reused later
273
+
274
+ // 2: temp use index -> meta -> position, this issue should exist for follows...
275
+ if (!prevIndexMeta) {
276
+ this._cleanHeaps();
277
+ positionToReplace = this._replaceFurthestIndexPosition(
278
+ newIndex,
279
+ safeRange
138
280
  );
281
+ } else {
282
+ positionToReplace = this._metaToPositionMap.get(prevIndexMeta);
139
283
  }
140
- // invariant(
141
- // this._valueToPositionMap[newValue] === undefined,
142
- // "Shouldn't try to replace values with value already stored value in " +
143
- // 'BufferSet'
144
- // );
145
-
146
- this._cleanHeaps();
147
- if (this._smallValues.empty() || this._largeValues.empty()) {
148
- // Threre are currently no values stored. We will have to create new
149
- // position for this value.
150
- return null;
284
+
285
+ this._onTheFlyIndices[positionToReplace] = meta;
286
+ this._setMetaIndex(meta, newIndex);
287
+ this._setMetaPosition(meta, positionToReplace);
288
+ // should not push to heap, pop only
289
+ // this._pushToHeaps(positionToReplace, newIndex)
290
+
291
+ return this._isOnTheFlyFullReturnHook(positionToReplace);
292
+ }
293
+
294
+ replaceFurthestIndexPosition(
295
+ newIndex: number,
296
+ safeRange?: {
297
+ startIndex: number;
298
+ endIndex: number;
299
+ }
300
+ ) {
301
+ if (!this.isBufferFull) {
302
+ return this._isOnTheFlyFullReturnHook(
303
+ this.getNewPositionForIndex(newIndex)
304
+ );
151
305
  }
152
306
 
153
- if (this._vacantPositions.length) {
154
- const position = this._vacantPositions.pop();
155
- this._valueToPositionMap[newValue] = position;
156
- this._pushToHeaps(position, newValue);
157
- return position;
307
+ return this._replaceFurthestIndexPosition(newIndex, safeRange);
308
+ }
309
+
310
+ _replaceFurthestIndexPosition(
311
+ newIndex: number,
312
+ safeRange?: {
313
+ startIndex: number;
314
+ endIndex: number;
315
+ }
316
+ ) {
317
+ if (this._largeValues.empty() || this._smallValues.empty()) {
318
+ return this._isOnTheFlyFullReturnHook(
319
+ this.getNewPositionForIndex(newIndex)
320
+ );
158
321
  }
159
322
 
160
323
  const minValue = this._smallValues.peek()!.value;
161
324
  const maxValue = this._largeValues.peek()!.value;
162
- if (minValue >= lowValue && maxValue <= highValue) {
163
- // All values currently stored are necessary, we can't reuse any of them.
164
- return null;
325
+
326
+ let indexToReplace;
327
+
328
+ if (!safeRange) {
329
+ // far from min
330
+ if (Math.abs(newIndex - minValue) > Math.abs(newIndex - maxValue)) {
331
+ indexToReplace = minValue;
332
+ this._smallValues.pop();
333
+ } else {
334
+ indexToReplace = maxValue;
335
+ this._largeValues.pop();
336
+ }
337
+ const replacedMeta = this._indexToMetaMap.get(indexToReplace);
338
+ const position = this._metaToPositionMap.get(replacedMeta);
339
+
340
+ return position;
165
341
  }
166
342
 
167
- let valueToReplace;
343
+ const { startIndex: lowValue, endIndex: highValue } = safeRange;
344
+
345
+ // All values currently stored are necessary, we can't reuse any of them.
168
346
  if (
169
- useMinValueFn({
170
- safeRange: {
171
- lowValue,
172
- highValue,
173
- },
174
- bufferSetRange: {
175
- minValue,
176
- maxValue,
177
- },
178
- currentIndex: newValue,
179
- })
347
+ isClamped(lowValue, minValue, highValue) &&
348
+ isClamped(lowValue, maxValue, highValue)
180
349
  ) {
181
- // if (lowValue - minValue > maxValue - highValue) {
350
+ return null;
351
+ } else if (
352
+ isClamped(lowValue, minValue, highValue) &&
353
+ !isClamped(lowValue, maxValue, highValue)
354
+ ) {
355
+ indexToReplace = maxValue;
356
+ this._largeValues.pop();
357
+ } else if (
358
+ !isClamped(lowValue, minValue, highValue) &&
359
+ isClamped(lowValue, maxValue, highValue)
360
+ ) {
361
+ indexToReplace = minValue;
362
+ this._smallValues.pop();
363
+ } else if (lowValue - minValue > maxValue - highValue) {
182
364
  // minValue is further from provided range. We will reuse it's position.
183
- valueToReplace = minValue;
365
+ indexToReplace = minValue;
184
366
  this._smallValues.pop();
185
367
  } else {
186
- valueToReplace = maxValue;
368
+ indexToReplace = maxValue;
187
369
  this._largeValues.pop();
188
370
  }
189
- const position = this._valueToPositionMap[valueToReplace];
190
- delete this._valueToPositionMap[valueToReplace];
191
- this._valueToPositionMap[newValue] = position;
192
- this._pushToHeaps(position, newValue);
193
371
 
194
- const _i = this._vacantPositions.findIndex((v) => v === position);
195
- if (_i !== -1) this._vacantPositions.splice(_i, 1);
372
+ const replacedMeta = this._indexToMetaMap.get(indexToReplace);
373
+ const position = this._metaToPositionMap.get(replacedMeta);
196
374
 
197
375
  return position;
198
376
  }
199
377
 
378
+ shuffle() {
379
+ const indices = new Array(this.bufferSize);
380
+ for (let idx = 0; idx < indices.length; idx++) {
381
+ const meta = this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
382
+ const targetIndex = this.getMetaIndex(meta);
383
+ indices[idx] = targetIndex;
384
+ }
385
+
386
+ const _arr = new Array(indices.length);
387
+ const _available = [];
388
+ const indexToMetaMap = new Map();
389
+ const metaToIndexMap = new Map();
390
+ const metaToPositionMap = new Map();
391
+ for (let idx = 0; idx < indices.length; idx++) {
392
+ const currentIndex = indices[idx];
393
+ const currentMeta = this._metaExtractor(currentIndex);
394
+ if (currentMeta == null) continue;
395
+
396
+ indexToMetaMap.set(currentIndex, currentMeta);
397
+ metaToIndexMap.set(currentMeta, currentIndex);
398
+
399
+ if (currentMeta === this._positionToMetaList[idx]) {
400
+ _arr[idx] = currentMeta;
401
+ continue;
402
+ }
403
+ const _i = this._positionToMetaList.findIndex((v) => v === currentMeta);
404
+ if (_i !== -1) {
405
+ _arr[_i] = currentMeta;
406
+ continue;
407
+ }
408
+
409
+ _available.push(currentMeta);
410
+ }
411
+
412
+ const { smallValues, largeValues } = this.initialize();
413
+ const positionToMetaList = [];
414
+
415
+ for (let position = 0; position < indices.length; position++) {
416
+ const value = indices[position];
417
+ if (_arr[position] != null) {
418
+ positionToMetaList[position] = _arr[position];
419
+ metaToPositionMap.set(_arr[position], position);
420
+ const element = { position, value };
421
+ smallValues.push(element);
422
+ largeValues.push(element);
423
+ continue;
424
+ }
425
+ const meta = _available.shift();
426
+ if (meta != null) {
427
+ positionToMetaList[position] = meta;
428
+ metaToPositionMap.set(meta, position);
429
+
430
+ const element = { position, value };
431
+ smallValues.push(element);
432
+ largeValues.push(element);
433
+ }
434
+ }
435
+
436
+ this._positionToMetaList = positionToMetaList;
437
+ this._smallValues = smallValues;
438
+ this._largeValues = largeValues;
439
+ this._indexToMetaMap = indexToMetaMap;
440
+ this.replaceMetaToIndexMap(metaToIndexMap);
441
+ this._metaToPositionMap = metaToPositionMap;
442
+ this._onTheFlyIndices = [];
443
+
444
+ try {
445
+ const indices = new Array(this.bufferSize);
446
+ for (let idx = 0; idx < indices.length; idx++) {
447
+ const meta =
448
+ this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
449
+ const targetIndex = this.getMetaIndex(meta);
450
+ if (meta != null) {
451
+ indices[idx] = {
452
+ meta,
453
+ targetIndex,
454
+ recyclerKey: `${this._name}_${idx}`,
455
+ };
456
+ }
457
+ }
458
+ return indices;
459
+ } catch (err) {
460
+ this.readyToStartNextLoop();
461
+ return this._positionToMetaList;
462
+ }
463
+ }
464
+
465
+ // key point: `meta` should be preserved..
466
+ getIndices() {
467
+ try {
468
+ const indices = new Array(this.bufferSize);
469
+ for (let idx = 0; idx < indices.length; idx++) {
470
+ const meta =
471
+ this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
472
+ const targetIndex = this.getMetaIndex(meta);
473
+ // which means source data has changed. such as one element has been deleted
474
+ if (meta !== this.getIndexMeta(targetIndex)) {
475
+ return this.shuffle();
476
+ }
477
+ if (meta != null) {
478
+ indices[idx] = {
479
+ meta,
480
+ targetIndex,
481
+ recyclerKey: `${this._name}_${idx}`,
482
+ };
483
+ }
484
+ }
485
+ // clear on the fly indices after return indices.
486
+ this._onTheFlyIndices = [];
487
+
488
+ return indices;
489
+ } catch (err) {
490
+ this.readyToStartNextLoop();
491
+ return this._positionToMetaList;
492
+ }
493
+ }
494
+
200
495
  _pushToHeaps(position: number, value: number) {
201
- const element = {
202
- position,
203
- value,
204
- };
496
+ const element = { position, value };
205
497
  // We can reuse the same object in both heaps, because we don't mutate them
206
498
  this._smallValues.push(element);
207
499
  this._largeValues.push(element);
208
500
  }
209
501
 
502
+ _setMetaPosition(meta: Meta, position: number) {
503
+ const prevMetaOnPosition = this._positionToMetaList[position];
504
+ if (prevMetaOnPosition) this._metaToPositionMap.delete(prevMetaOnPosition);
505
+ this._positionToMetaList[position] = meta;
506
+ this._metaToPositionMap.set(meta, position);
507
+ }
508
+
509
+ /**
510
+ *
511
+ * @param meta
512
+ * @param index
513
+ * @returns true means index not changed
514
+ */
515
+ _setMetaIndex(meta: Meta, index: number) {
516
+ const prevMetaIndex = this.getMetaIndex(meta);
517
+ if (prevMetaIndex !== undefined) {
518
+ // no need to set
519
+ // if (prevMetaIndex === index) return true;
520
+ this._indexToMetaMap.delete(prevMetaIndex);
521
+ }
522
+ this.setMetaIndex(meta, index);
523
+ this._indexToMetaMap.set(index, meta);
524
+ return false;
525
+ }
526
+
527
+ readyToStartNextLoop() {
528
+ this._lastUpdatedMS = Date.now();
529
+ }
530
+
531
+ prepare() {
532
+ if (this._loopMS === this._lastUpdatedMS) return;
533
+ this._loopMS = this._lastUpdatedMS;
534
+
535
+ this._onTheFlyIndices = [];
536
+ this._isOnTheFlyFull = false;
537
+ }
538
+
210
539
  _cleanHeaps() {
211
540
  // We not usually only remove object from one heap while moving value.
212
541
  // Here we make sure that there is no stale data on top of heaps.
@@ -226,7 +555,6 @@ class IntegerBufferSet {
226
555
  this._recreateHeaps();
227
556
  }
228
557
  }
229
-
230
558
  _recreateHeaps() {
231
559
  const sourceHeap =
232
560
  this._smallValues.size() < this._largeValues.size()
@@ -242,8 +570,11 @@ class IntegerBufferSet {
242
570
  );
243
571
  while (!sourceHeap.empty()) {
244
572
  const element = sourceHeap.pop()!;
245
- // Push all stil valid elements to new heaps
246
- if (this._valueToPositionMap[element.value] !== undefined) {
573
+ // Push all still valid elements to new heaps
574
+ if (
575
+ this._metaToPositionMap.get(this._indexToMetaMap.get(element.value)) !=
576
+ null
577
+ ) {
247
578
  newSmallValues.push(element);
248
579
  newLargeValues.push(element);
249
580
  }
@@ -255,7 +586,9 @@ class IntegerBufferSet {
255
586
  _cleanHeap(heap: Heap<HeapItem>) {
256
587
  while (
257
588
  !heap.empty() &&
258
- this._valueToPositionMap[heap.peek()!.value] === undefined
589
+ this._metaToPositionMap.get(
590
+ this._indexToMetaMap.get(heap.peek()!.value)
591
+ ) == null
259
592
  ) {
260
593
  heap.pop();
261
594
  }