fastds 0.0.5 → 0.0.6
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/README.md +391 -40
- package/build/binary-search.cjs +12 -3
- package/build/binary-search.cjs.map +1 -1
- package/build/binary-search.js +12 -3
- package/build/binary-search.js.map +1 -1
- package/build/ring-buffer.cjs +25 -25
- package/build/ring-buffer.cjs.map +1 -1
- package/build/ring-buffer.js +25 -25
- package/build/ring-buffer.js.map +1 -1
- package/package.json +5 -5
- package/src/binary-search.ts +129 -0
- package/src/ring-buffer.ts +27 -26
@@ -0,0 +1,129 @@
|
|
1
|
+
import { RingBuffer } from './ring-buffer.js';
|
2
|
+
|
3
|
+
export interface Comparator<T> {
|
4
|
+
(a: T, b: T): number;
|
5
|
+
}
|
6
|
+
|
7
|
+
export class BinarySearchArray<T> implements Iterable<T, void, unknown> {
|
8
|
+
#buffer: RingBuffer<T>;
|
9
|
+
#comparator: Comparator<T>;
|
10
|
+
|
11
|
+
constructor(comparator: Comparator<T>) {
|
12
|
+
this.#buffer = new RingBuffer();
|
13
|
+
this.#comparator = comparator;
|
14
|
+
}
|
15
|
+
|
16
|
+
readonly [Symbol.toStringTag] = 'BinarySearchArray';
|
17
|
+
|
18
|
+
get length(): number {
|
19
|
+
return this.#buffer.length;
|
20
|
+
}
|
21
|
+
|
22
|
+
at(index: number) {
|
23
|
+
return this.#buffer.peekAt(index);
|
24
|
+
}
|
25
|
+
|
26
|
+
lowerBound(value: T): number {
|
27
|
+
const length = this.#buffer.length;
|
28
|
+
if (length === 0) return 0;
|
29
|
+
|
30
|
+
const buffer = this.#buffer;
|
31
|
+
const comparator = this.#comparator;
|
32
|
+
let left = 0;
|
33
|
+
let right = length;
|
34
|
+
|
35
|
+
while (left < right) {
|
36
|
+
const mid = (left + right) >>> 1;
|
37
|
+
const midValue = buffer.peekAt(mid)!;
|
38
|
+
if (comparator(midValue, value) < 0) {
|
39
|
+
left = mid + 1;
|
40
|
+
} else {
|
41
|
+
right = mid;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
return left;
|
46
|
+
}
|
47
|
+
|
48
|
+
upperBound(value: T): number {
|
49
|
+
const length = this.#buffer.length;
|
50
|
+
if (length === 0) return 0;
|
51
|
+
|
52
|
+
const buffer = this.#buffer;
|
53
|
+
const comparator = this.#comparator;
|
54
|
+
let left = 0;
|
55
|
+
let right = length;
|
56
|
+
|
57
|
+
while (left < right) {
|
58
|
+
const mid = (left + right) >>> 1;
|
59
|
+
const midValue = buffer.peekAt(mid)!;
|
60
|
+
if (comparator(midValue, value) <= 0) {
|
61
|
+
left = mid + 1;
|
62
|
+
} else {
|
63
|
+
right = mid;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
return left;
|
68
|
+
}
|
69
|
+
|
70
|
+
indexOf(value: T, index = 0) {
|
71
|
+
const length = this.#buffer.length;
|
72
|
+
if (length === 0 || index >= length) return -1;
|
73
|
+
|
74
|
+
const buffer = this.#buffer;
|
75
|
+
const comparator = this.#comparator;
|
76
|
+
let left = index;
|
77
|
+
let right = length - 1;
|
78
|
+
|
79
|
+
while (left <= right) {
|
80
|
+
const mid = (left + right) >>> 1;
|
81
|
+
const midValue = buffer.peekAt(mid)!;
|
82
|
+
const cmp = comparator(midValue, value);
|
83
|
+
|
84
|
+
if (cmp === 0) {
|
85
|
+
return mid;
|
86
|
+
} else if (cmp < 0) {
|
87
|
+
left = mid + 1;
|
88
|
+
} else {
|
89
|
+
right = mid - 1;
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
return -1;
|
94
|
+
}
|
95
|
+
|
96
|
+
has(value: T): boolean {
|
97
|
+
return this.indexOf(value) !== -1;
|
98
|
+
}
|
99
|
+
|
100
|
+
insert(value: T) {
|
101
|
+
const index = this.lowerBound(value);
|
102
|
+
this.#buffer.setOne(index, value, true);
|
103
|
+
|
104
|
+
return index;
|
105
|
+
}
|
106
|
+
|
107
|
+
removeOne(index: number) {
|
108
|
+
return this.#buffer.removeOne(index);
|
109
|
+
}
|
110
|
+
|
111
|
+
removeFirst(value: T) {
|
112
|
+
const index = this.indexOf(value);
|
113
|
+
return this.#buffer.removeOne(index);
|
114
|
+
}
|
115
|
+
|
116
|
+
remove(index: number, count: number): this {
|
117
|
+
this.#buffer.deallocate(index, count);
|
118
|
+
|
119
|
+
return this;
|
120
|
+
}
|
121
|
+
|
122
|
+
iter() {
|
123
|
+
return this.#buffer.iter();
|
124
|
+
}
|
125
|
+
|
126
|
+
[Symbol.iterator](): Iterator<T> {
|
127
|
+
return this.#buffer[Symbol.iterator]();
|
128
|
+
}
|
129
|
+
}
|
package/src/ring-buffer.ts
CHANGED
@@ -117,7 +117,6 @@ export class RingBuffer<T> {
|
|
117
117
|
return false;
|
118
118
|
}
|
119
119
|
|
120
|
-
// Clamp index to valid range, like Array.splice()
|
121
120
|
index = Math.min(index, prevLength);
|
122
121
|
|
123
122
|
const buffer = this.#buffer;
|
@@ -258,12 +257,12 @@ export class RingBuffer<T> {
|
|
258
257
|
}
|
259
258
|
return -1;
|
260
259
|
}
|
260
|
+
|
261
261
|
const capacity = buffer.length;
|
262
262
|
const firstSegmentLength = capacity - head;
|
263
263
|
|
264
264
|
if (index < firstSegmentLength) {
|
265
|
-
const
|
266
|
-
const result = buffer.indexOf(value, startOffset);
|
265
|
+
const result = buffer.indexOf(value, head + index);
|
267
266
|
if (result !== -1) {
|
268
267
|
return result - head;
|
269
268
|
}
|
@@ -271,19 +270,12 @@ export class RingBuffer<T> {
|
|
271
270
|
}
|
272
271
|
|
273
272
|
if (index < length) {
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
return firstSegmentLength + result;
|
278
|
-
}
|
279
|
-
} else {
|
280
|
-
for (let i = 0; i < tail; i++) {
|
281
|
-
if (buffer[i] === value) {
|
282
|
-
return firstSegmentLength + i;
|
283
|
-
}
|
284
|
-
}
|
273
|
+
const result = buffer.indexOf(value, 0);
|
274
|
+
if (result !== -1 && result < tail) {
|
275
|
+
return firstSegmentLength + result;
|
285
276
|
}
|
286
277
|
}
|
278
|
+
|
287
279
|
return -1;
|
288
280
|
}
|
289
281
|
|
@@ -468,22 +460,27 @@ export class RingBuffer<T> {
|
|
468
460
|
}
|
469
461
|
|
470
462
|
push(value: T) {
|
471
|
-
const
|
472
|
-
if (
|
463
|
+
const nextTail = (this.#tail + 1) & this.#mask;
|
464
|
+
if (nextTail === this.#head) {
|
473
465
|
this.grow(this.#mask + 2);
|
466
|
+
this.#buffer[this.#tail] = value;
|
467
|
+
this.#tail = (this.#tail + 1) & this.#mask;
|
468
|
+
} else {
|
469
|
+
this.#buffer[this.#tail] = value;
|
470
|
+
this.#tail = nextTail;
|
474
471
|
}
|
475
|
-
this.#buffer[this.#tail] = value;
|
476
|
-
this.#tail = this.getTailOffset(1);
|
477
472
|
this.#length++;
|
478
473
|
return this;
|
479
474
|
}
|
480
475
|
|
481
476
|
unshift(value: T): this {
|
482
|
-
const
|
483
|
-
if (
|
477
|
+
const newHead = (this.#head - 1) & this.#mask;
|
478
|
+
if (newHead === this.#tail) {
|
484
479
|
this.grow(this.#mask + 2);
|
480
|
+
this.#head = (this.#head - 1) & this.#mask;
|
481
|
+
} else {
|
482
|
+
this.#head = newHead;
|
485
483
|
}
|
486
|
-
this.#head = this.getHeadOffset(-1);
|
487
484
|
this.#buffer[this.#head] = value;
|
488
485
|
this.#length++;
|
489
486
|
return this;
|
@@ -495,7 +492,7 @@ export class RingBuffer<T> {
|
|
495
492
|
}
|
496
493
|
const value = this.#buffer[this.#head];
|
497
494
|
this.#buffer[this.#head] = undefined;
|
498
|
-
this.#head = this
|
495
|
+
this.#head = (this.#head + 1) & this.#mask;
|
499
496
|
this.#length--;
|
500
497
|
return value;
|
501
498
|
}
|
@@ -504,7 +501,7 @@ export class RingBuffer<T> {
|
|
504
501
|
if (this.#head === this.#tail) {
|
505
502
|
return undefined;
|
506
503
|
}
|
507
|
-
this.#tail = this
|
504
|
+
this.#tail = (this.#tail - 1) & this.#mask;
|
508
505
|
const value = this.#buffer[this.#tail];
|
509
506
|
this.#buffer[this.#tail] = undefined;
|
510
507
|
this.#length--;
|
@@ -568,14 +565,18 @@ export class RingBuffer<T> {
|
|
568
565
|
|
569
566
|
[Symbol.iterator](): Iterator<T, void, unknown> {
|
570
567
|
const buffer = this.#buffer;
|
568
|
+
const mask = this.#mask;
|
569
|
+
const length = this.#length;
|
570
|
+
let count = 0;
|
571
571
|
let idx = this.#head;
|
572
572
|
return {
|
573
573
|
next: (): IteratorResult<T> => {
|
574
|
-
if (
|
574
|
+
if (count >= length) {
|
575
575
|
return { done: true, value: undefined };
|
576
576
|
}
|
577
|
-
const
|
578
|
-
|
577
|
+
const value = buffer[idx]!;
|
578
|
+
idx = (idx + 1) & mask;
|
579
|
+
count++;
|
579
580
|
return { done: false, value };
|
580
581
|
},
|
581
582
|
};
|