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

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
@@ -15,6 +15,7 @@ import {
15
15
 
16
16
  const defaultMetaExtractor = (value) => value;
17
17
  export const defaultBufferSize = 10;
18
+ const thresholdNumber = Number.MAX_SAFE_INTEGER - 100000;
18
19
 
19
20
  // !!!!! should do meta validation...meta should has an index...
20
21
  // value: original data `index` value
@@ -83,8 +84,6 @@ class IntegerBufferSet<Meta = any> {
83
84
 
84
85
  this.getNewPositionForIndex = this.getNewPositionForIndex.bind(this);
85
86
  this.getIndexPosition = this.getIndexPosition.bind(this);
86
- this.replacePositionInFliedIndices =
87
- this.replacePositionInFliedIndices.bind(this);
88
87
  this.replaceFurthestIndexPosition =
89
88
  this.replaceFurthestIndexPosition.bind(this);
90
89
  this._isOnTheFlyFullReturnHook = returnHook(
@@ -99,13 +98,24 @@ class IntegerBufferSet<Meta = any> {
99
98
  return this._bufferSize;
100
99
  }
101
100
 
101
+ isThresholdMeta(meta) {
102
+ if (typeof meta === 'number' && meta > thresholdNumber) return true;
103
+ return false;
104
+ }
105
+
102
106
  setIsOnTheFlyFull(val: any) {
103
107
  if (val != null) {
104
- const data = this._onTheFlyIndices.filter((v) => v);
108
+ const data = this._onTheFlyIndices.filter((v) => v != null);
105
109
  this._isOnTheFlyFull = data.length === this._bufferSize;
110
+ // console.log('fly ', this._isOnTheFlyFull, data.length, this._bufferSize);
106
111
  }
107
112
  }
108
113
 
114
+ resetOnTheFlies() {
115
+ this._isOnTheFlyFull = false;
116
+ this._onTheFlyIndices = [];
117
+ }
118
+
109
119
  get isBufferFull() {
110
120
  return this._positionToMetaList.length >= this._bufferSize;
111
121
  }
@@ -114,7 +124,7 @@ class IntegerBufferSet<Meta = any> {
114
124
  const { startIndex, endIndex } = safeRange;
115
125
  for (let idx = 0; idx < this._onTheFlyIndices.length; idx++) {
116
126
  const meta = this._onTheFlyIndices[idx];
117
- const metaIndex = this._metaToIndexMap.get(meta);
127
+ const metaIndex = this.getMetaIndex(meta);
118
128
  if (!isClamped(startIndex, metaIndex, endIndex)) {
119
129
  return idx;
120
130
  }
@@ -126,15 +136,17 @@ class IntegerBufferSet<Meta = any> {
126
136
  return {
127
137
  smallValues: new Heap([], this._smallerComparator),
128
138
  largeValues: new Heap([], this._greaterComparator),
129
- valueToPositionObject: {},
130
139
  };
131
140
  }
132
141
 
133
142
  getIndexMeta(index: number) {
143
+ if (index == null || index < 0) return null;
134
144
  return this._metaExtractor(index);
135
145
  }
136
146
 
137
147
  getMetaIndex(meta: Meta) {
148
+ if (meta == null) return -1;
149
+ if (this.isThresholdMeta(meta)) return -1;
138
150
  if (this._indexExtractor) return this._indexExtractor(meta);
139
151
  return this._metaToIndexMap.get(meta);
140
152
  }
@@ -184,29 +196,19 @@ class IntegerBufferSet<Meta = any> {
184
196
  return this._largeValues.peek()?.value;
185
197
  }
186
198
 
187
- replacePositionInFliedIndices(newIndex: number, safeRange: SafeRange) {
188
- const { startIndex, endIndex } = safeRange;
189
-
199
+ getFliedPosition(newIndex: number, safeRange: SafeRange) {
190
200
  if (this._isOnTheFlyFull) {
191
201
  // newIndex is not critical index, do nothing
192
- if (!isClamped(startIndex, newIndex, endIndex)) {
193
- return null;
202
+ if (
203
+ safeRange &&
204
+ isClamped(safeRange.startIndex, newIndex, safeRange.endIndex)
205
+ ) {
206
+ return this.getOnTheFlyUncriticalPosition(safeRange);
194
207
  }
195
208
  // if `newIndex` is critical index, replace an un-committed
196
209
  // index value from _onTheFlyIndices.
197
- const pos = this.getOnTheFlyUncriticalPosition(safeRange);
198
- if (pos != null) return pos;
199
- }
200
- return null;
201
- }
202
-
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
+ // const pos = this.getOnTheFlyUncriticalPosition(safeRange);
211
+ // if (pos != null) return pos;
210
212
  }
211
213
  return null;
212
214
  }
@@ -223,72 +225,73 @@ class IntegerBufferSet<Meta = any> {
223
225
  getPosition(newIndex: number, safeRange?: SafeRange) {
224
226
  this.prepare();
225
227
  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);
228
+ const metaPosition = this._metaToPositionMap.get(meta);
229
+ let position, indexMeta;
230
+
231
+ // if (this._name === 'normal_goods')
232
+ // console.log(
233
+ // 'getPosition ',
234
+ // newIndex,
235
+ // !this.isBufferFull,
236
+ // this._isOnTheFlyFull,
237
+ // this._onTheFlyIndices.slice(),
238
+ // this._indexToMetaMap.get(newIndex),
239
+ // this._metaToPositionMap.get(this._indexToMetaMap.get(newIndex))
240
+ // );
241
+
242
+ if (metaPosition !== undefined) {
243
+ position = this.commitPosition({
244
+ newIndex,
245
+ meta,
246
+ safeRange,
247
+ position: metaPosition,
248
+ });
249
+ } else if (!this.isBufferFull) {
250
+ /** placed on new buffered position */
251
+ position = this.getNewPositionForIndex(newIndex);
252
+ } else if (this._isOnTheFlyFull) {
253
+ position = this.getFliedPosition(newIndex, safeRange);
254
+ } else if (
255
+ (indexMeta = this._indexToMetaMap.get(newIndex)) &&
256
+ this._metaToPositionMap.get(indexMeta)
257
+ ) {
258
+ /**
259
+ Index has already been stored, but we cant use its old position directly...
260
+ 1:index -> meta, meta may be reused later
261
+ 2: temp use index -> meta -> position, this issue should exist for follows...
262
+ */
263
+ position = this.commitPosition({
264
+ newIndex,
265
+ meta,
266
+ safeRange,
267
+ position: this._metaToPositionMap.get(indexMeta),
268
+ });
269
+ } else {
270
+ this._cleanHeaps();
271
+ // console.log('commeit ---')
272
+ position = this.commitPosition({
273
+ newIndex,
274
+ meta,
275
+ safeRange,
276
+ position: this._replaceFurthestIndexPosition(newIndex, safeRange),
277
+ });
258
278
  }
259
279
 
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);
280
+ // console.log('position ', position)
267
281
 
268
- let positionToReplace;
269
- const prevIndexMeta = this._indexToMetaMap.get(newIndex);
282
+ if (position != null) {
283
+ this._onTheFlyIndices[position] = meta;
284
+ this._setMetaIndex(meta, newIndex);
285
+ this._metaToPositionMap.set(meta, position);
270
286
 
271
- // Index has already been stored, but we cant use its old position directly...
272
- // 1:index -> meta, meta may be reused later
287
+ // this._setMetaPosition(meta, position);
288
+ // should not push to heap, pop only
289
+ // this._pushToHeaps(position, newIndex)
273
290
 
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
280
- );
281
- } else {
282
- positionToReplace = this._metaToPositionMap.get(prevIndexMeta);
291
+ return this._isOnTheFlyFullReturnHook(position);
283
292
  }
284
293
 
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);
294
+ return null;
292
295
  }
293
296
 
294
297
  replaceFurthestIndexPosition(
@@ -320,10 +323,20 @@ class IntegerBufferSet<Meta = any> {
320
323
  );
321
324
  }
322
325
 
326
+ let indexToReplace;
327
+
323
328
  const minValue = this._smallValues.peek()!.value;
324
329
  const maxValue = this._largeValues.peek()!.value;
325
330
 
326
- let indexToReplace;
331
+ // console.log('mathc ', maxValue, maxValue > thresholdNumber)
332
+ if (maxValue > thresholdNumber) {
333
+ indexToReplace = maxValue;
334
+ this._largeValues.pop();
335
+ const replacedMeta = this._indexToMetaMap.get(indexToReplace);
336
+
337
+ const position = this._metaToPositionMap.get(replacedMeta);
338
+ return position;
339
+ }
327
340
 
328
341
  if (!safeRange) {
329
342
  // far from min
@@ -372,6 +385,8 @@ class IntegerBufferSet<Meta = any> {
372
385
  const replacedMeta = this._indexToMetaMap.get(indexToReplace);
373
386
  const position = this._metaToPositionMap.get(replacedMeta);
374
387
 
388
+ // console.log('index ', indexToReplace, replacedMeta, position)
389
+
375
390
  return position;
376
391
  }
377
392
 
@@ -379,23 +394,30 @@ class IntegerBufferSet<Meta = any> {
379
394
  const indices = new Array(this.bufferSize);
380
395
  for (let idx = 0; idx < indices.length; idx++) {
381
396
  const meta = this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
397
+ // console.log('ix ', idx,this.getMetaIndex(meta) )
382
398
  const targetIndex = this.getMetaIndex(meta);
383
399
  indices[idx] = targetIndex;
384
400
  }
385
401
 
402
+ // console.log(
403
+ // 'indices ',
404
+ // this._positionToMetaList,
405
+ // this._onTheFlyIndices.slice(),
406
+ // indices
407
+ // );
408
+
386
409
  const _arr = new Array(indices.length);
387
410
  const _available = [];
388
411
  const indexToMetaMap = new Map();
389
412
  const metaToIndexMap = new Map();
390
- const metaToPositionMap = new Map();
413
+
391
414
  for (let idx = 0; idx < indices.length; idx++) {
392
415
  const currentIndex = indices[idx];
393
416
  const currentMeta = this._metaExtractor(currentIndex);
417
+ // console.log("current ", currentIndex, currentMeta)
394
418
  if (currentMeta == null) continue;
395
-
396
419
  indexToMetaMap.set(currentIndex, currentMeta);
397
420
  metaToIndexMap.set(currentMeta, currentIndex);
398
-
399
421
  if (currentMeta === this._positionToMetaList[idx]) {
400
422
  _arr[idx] = currentMeta;
401
423
  continue;
@@ -409,72 +431,53 @@ class IntegerBufferSet<Meta = any> {
409
431
  _available.push(currentMeta);
410
432
  }
411
433
 
412
- const { smallValues, largeValues } = this.initialize();
413
434
  const positionToMetaList = [];
435
+ this._indexToMetaMap = indexToMetaMap;
436
+ this.replaceMetaToIndexMap(metaToIndexMap);
414
437
 
415
438
  for (let position = 0; position < indices.length; position++) {
416
- const value = indices[position];
417
439
  if (_arr[position] != null) {
418
440
  positionToMetaList[position] = _arr[position];
419
- metaToPositionMap.set(_arr[position], position);
420
- const element = { position, value };
421
- smallValues.push(element);
422
- largeValues.push(element);
423
441
  continue;
424
442
  }
425
443
  const meta = _available.shift();
426
444
  if (meta != null) {
427
445
  positionToMetaList[position] = meta;
428
- metaToPositionMap.set(meta, position);
429
-
430
- const element = { position, value };
431
- smallValues.push(element);
432
- largeValues.push(element);
433
446
  }
434
447
  }
435
448
 
436
449
  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
450
 
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
- }
451
+ return this.getIndices();
463
452
  }
464
453
 
465
454
  // key point: `meta` should be preserved..
466
455
  getIndices() {
456
+ const { smallValues, largeValues } = this.initialize();
457
+
467
458
  try {
468
- const indices = new Array(this.bufferSize);
459
+ const indices = new Array(this._positionToMetaList.length);
460
+ const metaToPositionMap = new Map();
461
+ const indexToMetaMap = new Map();
462
+ const metaToIndexMap = new Map();
469
463
  for (let idx = 0; idx < indices.length; idx++) {
470
464
  const meta =
471
465
  this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
472
466
  const targetIndex = this.getMetaIndex(meta);
473
467
  // which means source data has changed. such as one element has been deleted
474
- if (meta !== this.getIndexMeta(targetIndex)) {
468
+ if (
469
+ !this.isThresholdMeta(meta) &&
470
+ meta != this.getIndexMeta(targetIndex)
471
+ ) {
475
472
  return this.shuffle();
476
473
  }
477
- if (meta != null) {
474
+ if (meta != null && !this.isThresholdMeta(meta)) {
475
+ const element = { position: idx, value: targetIndex };
476
+ smallValues.push(element);
477
+ largeValues.push(element);
478
+ metaToPositionMap.set(meta, idx);
479
+ indexToMetaMap.set(targetIndex, meta);
480
+ metaToIndexMap.set(meta, targetIndex);
478
481
  indices[idx] = {
479
482
  meta,
480
483
  targetIndex,
@@ -482,13 +485,21 @@ class IntegerBufferSet<Meta = any> {
482
485
  };
483
486
  }
484
487
  }
485
- // clear on the fly indices after return indices.
486
- this._onTheFlyIndices = [];
488
+ this._smallValues = smallValues;
489
+ this._largeValues = largeValues;
490
+ this._metaToPositionMap = metaToPositionMap;
491
+ this._positionToMetaList = indices.map((v) => v?.meta);
492
+ this.resetOnTheFlies();
493
+ this._indexToMetaMap = indexToMetaMap;
494
+ this.replaceMetaToIndexMap(metaToIndexMap);
487
495
 
488
496
  return indices;
489
497
  } catch (err) {
490
- this.readyToStartNextLoop();
498
+ console.log('err ', err);
491
499
  return this._positionToMetaList;
500
+ } finally {
501
+ this.readyToStartNextLoop();
502
+ // clear on the fly indices after return indices.
492
503
  }
493
504
  }
494
505
 
@@ -500,12 +511,45 @@ class IntegerBufferSet<Meta = any> {
500
511
  }
501
512
 
502
513
  _setMetaPosition(meta: Meta, position: number) {
503
- const prevMetaOnPosition = this._positionToMetaList[position];
504
- if (prevMetaOnPosition) this._metaToPositionMap.delete(prevMetaOnPosition);
514
+ // do not delete meta2position; because getPosition will get by meta first...
515
+ // const prevMetaOnPosition = this._positionToMetaList[position];
516
+ // if (prevMetaOnPosition) this._metaToPositionMap.delete(prevMetaOnPosition);
505
517
  this._positionToMetaList[position] = meta;
506
518
  this._metaToPositionMap.set(meta, position);
507
519
  }
508
520
 
521
+ commitPosition(props: {
522
+ newIndex: number;
523
+ position: number;
524
+ meta: Meta;
525
+ safeRange: SafeRange;
526
+ }) {
527
+ const { newIndex, safeRange, position, meta } = props;
528
+ const onTheFlyPositionMeta = this._onTheFlyIndices[position];
529
+ let positionToReplace = position;
530
+
531
+ // console.log('position ', newIndex, position);
532
+
533
+ if (onTheFlyPositionMeta) {
534
+ // such as place item 11 twice...
535
+ if (onTheFlyPositionMeta === meta) return position;
536
+ if (this._isOnTheFlyFull)
537
+ return this.getFliedPosition(newIndex, safeRange);
538
+ positionToReplace = this._replaceFurthestIndexPosition(
539
+ newIndex,
540
+ safeRange
541
+ );
542
+
543
+ while (this._onTheFlyIndices[positionToReplace]) {
544
+ positionToReplace = this._replaceFurthestIndexPosition(
545
+ newIndex,
546
+ safeRange
547
+ );
548
+ }
549
+ }
550
+ return positionToReplace;
551
+ }
552
+
509
553
  /**
510
554
  *
511
555
  * @param meta
@@ -539,8 +583,16 @@ class IntegerBufferSet<Meta = any> {
539
583
  _cleanHeaps() {
540
584
  // We not usually only remove object from one heap while moving value.
541
585
  // Here we make sure that there is no stale data on top of heaps.
542
- this._cleanHeap(this._smallValues);
543
- this._cleanHeap(this._largeValues);
586
+ // this._cleanHeap(this._smallValues);
587
+ // this._cleanHeap(this._largeValues);
588
+
589
+ for (let idx = 0; idx < this._positionToMetaList.length; idx++) {
590
+ if (this._positionToMetaList[idx] == null) {
591
+ this._recreateHeaps();
592
+ return;
593
+ }
594
+ }
595
+
544
596
  const minHeapSize = Math.min(
545
597
  this._smallValues.size(),
546
598
  this._largeValues.size()
@@ -556,44 +608,48 @@ class IntegerBufferSet<Meta = any> {
556
608
  }
557
609
  }
558
610
  _recreateHeaps() {
559
- const sourceHeap =
560
- this._smallValues.size() < this._largeValues.size()
561
- ? this._smallValues
562
- : this._largeValues;
563
- const newSmallValues = new Heap<HeapItem>(
564
- [], // Initial data in the heap
565
- this._smallerComparator
566
- );
567
- const newLargeValues = new Heap<HeapItem>(
568
- [], // Initial datat in the heap
569
- this._greaterComparator
570
- );
571
- while (!sourceHeap.empty()) {
572
- const element = sourceHeap.pop()!;
573
- // Push all still valid elements to new heaps
574
- if (
575
- this._metaToPositionMap.get(this._indexToMetaMap.get(element.value)) !=
576
- null
577
- ) {
578
- newSmallValues.push(element);
579
- newLargeValues.push(element);
611
+ const { smallValues, largeValues } = this.initialize();
612
+ for (
613
+ let position = 0;
614
+ position < this._positionToMetaList.length;
615
+ position++
616
+ ) {
617
+ const meta = this._positionToMetaList[position];
618
+ let value = this.getMetaIndex(meta);
619
+
620
+ if (!meta || value === -1 || value == null) {
621
+ value = Number.MAX_SAFE_INTEGER - position;
580
622
  }
581
- }
582
- this._smallValues = newSmallValues;
583
- this._largeValues = newLargeValues;
584
- }
585
623
 
586
- _cleanHeap(heap: Heap<HeapItem>) {
587
- while (
588
- !heap.empty() &&
589
- this._metaToPositionMap.get(
590
- this._indexToMetaMap.get(heap.peek()!.value)
591
- ) == null
592
- ) {
593
- heap.pop();
624
+ const element = { position, value };
625
+ smallValues.push(element);
626
+ largeValues.push(element);
627
+ if (value > thresholdNumber) {
628
+ // @ts-ignore
629
+ this._setMetaPosition(value, position);
630
+ // @ts-ignore
631
+ this._setMetaIndex(value, value);
632
+ }
594
633
  }
634
+
635
+ this._largeValues.peek().value;
636
+
637
+ this._smallValues = smallValues;
638
+ this._largeValues = largeValues;
595
639
  }
596
640
 
641
+ // _cleanHeap(heap: Heap<HeapItem>) {
642
+ // while (
643
+ // !heap.empty() &&
644
+ // this._metaToPositionMap.get(
645
+ // this._indexToMetaMap.get(heap.peek()!.value)
646
+ // ) == null
647
+ // ) {
648
+ // console.log('pop ---', heap.peek()!.value);
649
+ // heap.pop();
650
+ // }
651
+ // }
652
+
597
653
  _smallerComparator(lhs: HeapItem, rhs: HeapItem) {
598
654
  return lhs.value < rhs.value;
599
655
  }