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

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,19 @@ 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
+ const position = this._metaToPositionMap.get(replacedMeta);
337
+ return position;
338
+ }
327
339
 
328
340
  if (!safeRange) {
329
341
  // far from min
@@ -372,6 +384,8 @@ class IntegerBufferSet<Meta = any> {
372
384
  const replacedMeta = this._indexToMetaMap.get(indexToReplace);
373
385
  const position = this._metaToPositionMap.get(replacedMeta);
374
386
 
387
+ // console.log('index ', indexToReplace, replacedMeta, position)
388
+
375
389
  return position;
376
390
  }
377
391
 
@@ -379,23 +393,30 @@ class IntegerBufferSet<Meta = any> {
379
393
  const indices = new Array(this.bufferSize);
380
394
  for (let idx = 0; idx < indices.length; idx++) {
381
395
  const meta = this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
396
+ // console.log('ix ', idx,this.getMetaIndex(meta) )
382
397
  const targetIndex = this.getMetaIndex(meta);
383
398
  indices[idx] = targetIndex;
384
399
  }
385
400
 
401
+ // console.log(
402
+ // 'indices ',
403
+ // this._positionToMetaList,
404
+ // this._onTheFlyIndices.slice(),
405
+ // indices
406
+ // );
407
+
386
408
  const _arr = new Array(indices.length);
387
409
  const _available = [];
388
410
  const indexToMetaMap = new Map();
389
411
  const metaToIndexMap = new Map();
390
- const metaToPositionMap = new Map();
412
+
391
413
  for (let idx = 0; idx < indices.length; idx++) {
392
414
  const currentIndex = indices[idx];
393
415
  const currentMeta = this._metaExtractor(currentIndex);
416
+ // console.log("current ", currentIndex, currentMeta)
394
417
  if (currentMeta == null) continue;
395
-
396
418
  indexToMetaMap.set(currentIndex, currentMeta);
397
419
  metaToIndexMap.set(currentMeta, currentIndex);
398
-
399
420
  if (currentMeta === this._positionToMetaList[idx]) {
400
421
  _arr[idx] = currentMeta;
401
422
  continue;
@@ -409,72 +430,49 @@ class IntegerBufferSet<Meta = any> {
409
430
  _available.push(currentMeta);
410
431
  }
411
432
 
412
- const { smallValues, largeValues } = this.initialize();
413
433
  const positionToMetaList = [];
434
+ this._indexToMetaMap = indexToMetaMap;
435
+ this.replaceMetaToIndexMap(metaToIndexMap);
414
436
 
415
437
  for (let position = 0; position < indices.length; position++) {
416
- const value = indices[position];
417
438
  if (_arr[position] != null) {
418
439
  positionToMetaList[position] = _arr[position];
419
- metaToPositionMap.set(_arr[position], position);
420
- const element = { position, value };
421
- smallValues.push(element);
422
- largeValues.push(element);
423
440
  continue;
424
441
  }
425
442
  const meta = _available.shift();
426
443
  if (meta != null) {
427
444
  positionToMetaList[position] = meta;
428
- metaToPositionMap.set(meta, position);
429
-
430
- const element = { position, value };
431
- smallValues.push(element);
432
- largeValues.push(element);
433
445
  }
434
446
  }
435
447
 
436
448
  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
449
 
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
- }
450
+ return this.getIndices();
463
451
  }
464
452
 
465
453
  // key point: `meta` should be preserved..
466
454
  getIndices() {
455
+ const { smallValues, largeValues } = this.initialize();
456
+
467
457
  try {
468
- const indices = new Array(this.bufferSize);
458
+ const indices = new Array(this._positionToMetaList.length);
459
+ const metaToPositionMap = new Map();
469
460
  for (let idx = 0; idx < indices.length; idx++) {
470
461
  const meta =
471
462
  this._onTheFlyIndices[idx] || this._positionToMetaList[idx];
472
463
  const targetIndex = this.getMetaIndex(meta);
473
464
  // which means source data has changed. such as one element has been deleted
474
- if (meta !== this.getIndexMeta(targetIndex)) {
465
+ if (
466
+ !this.isThresholdMeta(meta) &&
467
+ meta != this.getIndexMeta(targetIndex)
468
+ ) {
475
469
  return this.shuffle();
476
470
  }
477
- if (meta != null) {
471
+ if (meta != null && !this.isThresholdMeta(meta)) {
472
+ const element = { position: idx, value: targetIndex };
473
+ smallValues.push(element);
474
+ largeValues.push(element);
475
+ metaToPositionMap.set(meta, idx);
478
476
  indices[idx] = {
479
477
  meta,
480
478
  targetIndex,
@@ -482,13 +480,19 @@ class IntegerBufferSet<Meta = any> {
482
480
  };
483
481
  }
484
482
  }
485
- // clear on the fly indices after return indices.
486
- this._onTheFlyIndices = [];
483
+ this._smallValues = smallValues;
484
+ this._largeValues = largeValues;
485
+ this._metaToPositionMap = metaToPositionMap;
486
+ this._positionToMetaList = indices.map((v) => v?.meta);
487
+ this.resetOnTheFlies();
487
488
 
488
489
  return indices;
489
490
  } catch (err) {
490
- this.readyToStartNextLoop();
491
+ console.log('err ', err);
491
492
  return this._positionToMetaList;
493
+ } finally {
494
+ this.readyToStartNextLoop();
495
+ // clear on the fly indices after return indices.
492
496
  }
493
497
  }
494
498
 
@@ -500,12 +504,45 @@ class IntegerBufferSet<Meta = any> {
500
504
  }
501
505
 
502
506
  _setMetaPosition(meta: Meta, position: number) {
503
- const prevMetaOnPosition = this._positionToMetaList[position];
504
- if (prevMetaOnPosition) this._metaToPositionMap.delete(prevMetaOnPosition);
507
+ // do not delete meta2position; because getPosition will get by meta first...
508
+ // const prevMetaOnPosition = this._positionToMetaList[position];
509
+ // if (prevMetaOnPosition) this._metaToPositionMap.delete(prevMetaOnPosition);
505
510
  this._positionToMetaList[position] = meta;
506
511
  this._metaToPositionMap.set(meta, position);
507
512
  }
508
513
 
514
+ commitPosition(props: {
515
+ newIndex: number;
516
+ position: number;
517
+ meta: Meta;
518
+ safeRange: SafeRange;
519
+ }) {
520
+ const { newIndex, safeRange, position, meta } = props;
521
+ const onTheFlyPositionMeta = this._onTheFlyIndices[position];
522
+ let positionToReplace = position;
523
+
524
+ // console.log('position ', newIndex, position);
525
+
526
+ if (onTheFlyPositionMeta) {
527
+ // such as place item 11 twice...
528
+ if (onTheFlyPositionMeta === meta) return position;
529
+ if (this._isOnTheFlyFull)
530
+ return this.getFliedPosition(newIndex, safeRange);
531
+ positionToReplace = this._replaceFurthestIndexPosition(
532
+ newIndex,
533
+ safeRange
534
+ );
535
+
536
+ while (this._onTheFlyIndices[positionToReplace]) {
537
+ positionToReplace = this._replaceFurthestIndexPosition(
538
+ newIndex,
539
+ safeRange
540
+ );
541
+ }
542
+ }
543
+ return positionToReplace;
544
+ }
545
+
509
546
  /**
510
547
  *
511
548
  * @param meta
@@ -539,8 +576,16 @@ class IntegerBufferSet<Meta = any> {
539
576
  _cleanHeaps() {
540
577
  // We not usually only remove object from one heap while moving value.
541
578
  // Here we make sure that there is no stale data on top of heaps.
542
- this._cleanHeap(this._smallValues);
543
- this._cleanHeap(this._largeValues);
579
+ // this._cleanHeap(this._smallValues);
580
+ // this._cleanHeap(this._largeValues);
581
+
582
+ for (let idx = 0; idx < this._positionToMetaList.length; idx++) {
583
+ if (this._positionToMetaList[idx] == null) {
584
+ this._recreateHeaps();
585
+ return;
586
+ }
587
+ }
588
+
544
589
  const minHeapSize = Math.min(
545
590
  this._smallValues.size(),
546
591
  this._largeValues.size()
@@ -556,44 +601,48 @@ class IntegerBufferSet<Meta = any> {
556
601
  }
557
602
  }
558
603
  _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);
604
+ const { smallValues, largeValues } = this.initialize();
605
+ for (
606
+ let position = 0;
607
+ position < this._positionToMetaList.length;
608
+ position++
609
+ ) {
610
+ const meta = this._positionToMetaList[position];
611
+ let value = this.getMetaIndex(meta);
612
+
613
+ if (!meta || value === -1 || value == null) {
614
+ value = Number.MAX_SAFE_INTEGER - position;
580
615
  }
581
- }
582
- this._smallValues = newSmallValues;
583
- this._largeValues = newLargeValues;
584
- }
585
616
 
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();
617
+ const element = { position, value };
618
+ smallValues.push(element);
619
+ largeValues.push(element);
620
+ if (value > thresholdNumber) {
621
+ // @ts-ignore
622
+ this._setMetaPosition(value, position);
623
+ // @ts-ignore
624
+ this._setMetaIndex(value, value);
625
+ }
594
626
  }
627
+
628
+ this._largeValues.peek().value;
629
+
630
+ this._smallValues = smallValues;
631
+ this._largeValues = largeValues;
595
632
  }
596
633
 
634
+ // _cleanHeap(heap: Heap<HeapItem>) {
635
+ // while (
636
+ // !heap.empty() &&
637
+ // this._metaToPositionMap.get(
638
+ // this._indexToMetaMap.get(heap.peek()!.value)
639
+ // ) == null
640
+ // ) {
641
+ // console.log('pop ---', heap.peek()!.value);
642
+ // heap.pop();
643
+ // }
644
+ // }
645
+
597
646
  _smallerComparator(lhs: HeapItem, rhs: HeapItem) {
598
647
  return lhs.value < rhs.value;
599
648
  }