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
package/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# FastDS
|
2
2
|
|
3
3
|
[![Github Build Status][github-image]][github-url]
|
4
4
|
[![NPM version][npm-image]][npm-url]
|
@@ -6,69 +6,421 @@
|
|
6
6
|
[![Coverage Status][codecov-image]][codecov-url]
|
7
7
|
[![Snyk][snyk-image]][snyk-url]
|
8
8
|
|
9
|
-
Fast,
|
9
|
+
Fast, zero-dependency TypeScript data structures optimized for high-performance applications.
|
10
10
|
|
11
|
-
##
|
11
|
+
## Features
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
- **🚀 High Performance** - Up to 37% faster than popular alternatives
|
14
|
+
- **🔄 O(1) Operations** - Constant time push, pop, shift, unshift operations
|
15
|
+
- **📦 Zero Dependencies** - No external runtime dependencies
|
16
|
+
- **🔍 TypeScript Native** - Full type safety and IntelliSense support
|
17
|
+
- **💾 Memory Efficient** - Smart memory management with automatic cleanup
|
18
|
+
- **🎯 Production Ready** - Battle-tested with 100% test coverage
|
15
19
|
|
16
|
-
##
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
```bash
|
23
|
+
npm install fastds
|
24
|
+
# or
|
25
|
+
yarn add fastds
|
26
|
+
# or
|
27
|
+
pnpm add fastds
|
28
|
+
```
|
29
|
+
|
30
|
+
## Quick Start
|
31
|
+
|
32
|
+
```typescript
|
33
|
+
import { RingBuffer, BinarySearchArray } from 'fastds';
|
34
|
+
|
35
|
+
// Create a high-performance circular buffer
|
36
|
+
const buffer = new RingBuffer<number>();
|
37
|
+
buffer.push(1).push(2).push(3);
|
38
|
+
console.log(buffer.shift()); // 1
|
39
|
+
console.log(buffer.pop()); // 3
|
40
|
+
|
41
|
+
// Create a sorted array with binary search
|
42
|
+
const sorted = new BinarySearchArray<number>((a, b) => a - b);
|
43
|
+
sorted.insert(5);
|
44
|
+
sorted.insert(2);
|
45
|
+
sorted.insert(8);
|
46
|
+
console.log(sorted.indexOf(5)); // 1 (sorted: [2, 5, 8])
|
47
|
+
```
|
48
|
+
|
49
|
+
## Data Structures
|
21
50
|
|
22
51
|
### RingBuffer
|
23
|
-
A circular buffer with O(1) push, shift, pop, unshift (amortized), index access, and fast deallocation. Written in TypeScript, optimized for minimal allocations.
|
24
52
|
|
25
|
-
|
53
|
+
A high-performance circular buffer (double-ended queue) with O(1) operations for both ends.
|
54
|
+
|
55
|
+
#### Why Use RingBuffer?
|
56
|
+
|
57
|
+
- **Queue/Deque Operations**: Perfect for FIFO/LIFO operations, message queues, and task scheduling
|
58
|
+
- **Sliding Windows**: Efficient for implementing sliding window algorithms
|
59
|
+
- **Stream Buffers**: Ideal for I/O operations, network buffers, and data streaming
|
60
|
+
- **Performance Critical**: When you need faster operations than JavaScript's native Array
|
26
61
|
|
27
|
-
|
62
|
+
#### Basic Usage
|
63
|
+
|
64
|
+
```typescript
|
65
|
+
import { RingBuffer } from 'fastds';
|
66
|
+
|
67
|
+
// Create with optional initial capacity
|
68
|
+
const buffer = new RingBuffer<string>(100);
|
69
|
+
|
70
|
+
// Add elements
|
71
|
+
buffer.push('world'); // Add to end
|
72
|
+
buffer.unshift('hello'); // Add to beginning
|
73
|
+
|
74
|
+
// Remove elements
|
75
|
+
const first = buffer.shift(); // Remove from beginning: 'hello'
|
76
|
+
const last = buffer.pop(); // Remove from end: 'world'
|
77
|
+
|
78
|
+
// Peek without removing
|
79
|
+
buffer.push('a', 'b', 'c');
|
80
|
+
console.log(buffer.peekFirst()); // 'a'
|
81
|
+
console.log(buffer.peekLast()); // 'c'
|
82
|
+
console.log(buffer.peekAt(1)); // 'b'
|
28
83
|
```
|
29
|
-
|
30
|
-
|
31
|
-
|
84
|
+
|
85
|
+
#### Advanced Operations
|
86
|
+
|
87
|
+
```typescript
|
88
|
+
// Create from array
|
89
|
+
const numbers = RingBuffer.from([1, 2, 3, 4, 5]);
|
90
|
+
|
91
|
+
// Array-like operations
|
92
|
+
numbers.slice(1, 3); // [2, 3]
|
93
|
+
numbers.indexOf(3); // 2
|
94
|
+
numbers.has(4); // true
|
95
|
+
numbers.toArray(); // [1, 2, 3, 4, 5]
|
96
|
+
|
97
|
+
// Modify at index
|
98
|
+
numbers.setOne(2, 10); // Replace index 2 with 10
|
99
|
+
numbers.setOne(2, 20, true); // Insert 20 at index 2
|
100
|
+
|
101
|
+
// Remove elements
|
102
|
+
numbers.removeOne(1); // Remove element at index 1
|
103
|
+
numbers.removeFirst(10); // Remove first occurrence of 10
|
104
|
+
numbers.remove(0, 2); // Remove 2 elements starting at index 0
|
105
|
+
|
106
|
+
// Iteration
|
107
|
+
for (const value of numbers) {
|
108
|
+
console.log(value);
|
109
|
+
}
|
110
|
+
|
111
|
+
// Drain iterator (removes elements while iterating)
|
112
|
+
const drainIter = numbers.drain();
|
113
|
+
for (const value of drainIter) {
|
114
|
+
console.log(value); // Buffer becomes empty after iteration
|
115
|
+
}
|
32
116
|
```
|
33
117
|
|
34
|
-
|
118
|
+
#### Memory Management
|
119
|
+
|
120
|
+
```typescript
|
121
|
+
const buffer = new RingBuffer<object>(1000);
|
122
|
+
|
123
|
+
// Add many elements
|
124
|
+
for (let i = 0; i < 10000; i++) {
|
125
|
+
buffer.push({ data: i });
|
126
|
+
}
|
127
|
+
|
128
|
+
// Compact: remove gaps and optimize memory
|
129
|
+
buffer.compact((item) => item.data % 2 === 0); // Keep only even numbers
|
130
|
+
|
131
|
+
// Resize: adjust capacity
|
132
|
+
buffer.resize(500); // Shrink if possible
|
133
|
+
|
134
|
+
// Clear: remove all elements and reset
|
135
|
+
buffer.clear();
|
35
136
|
```
|
36
|
-
|
37
|
-
|
38
|
-
|
137
|
+
|
138
|
+
### BinarySearchArray
|
139
|
+
|
140
|
+
A sorted array with O(log n) search operations using binary search algorithms.
|
141
|
+
|
142
|
+
#### Why Use BinarySearchArray?
|
143
|
+
|
144
|
+
- **Sorted Collections**: Maintains elements in sorted order automatically
|
145
|
+
- **Fast Lookups**: O(log n) search performance for large datasets
|
146
|
+
- **Range Queries**: Efficiently find elements within a range
|
147
|
+
- **Priority Systems**: Implement priority queues and ordered lists
|
148
|
+
|
149
|
+
#### Basic Usage
|
150
|
+
|
151
|
+
```typescript
|
152
|
+
import { BinarySearchArray } from 'fastds';
|
153
|
+
|
154
|
+
// Create with a comparator function
|
155
|
+
const numbers = new BinarySearchArray<number>((a, b) => a - b);
|
156
|
+
|
157
|
+
// Elements are automatically sorted on insertion
|
158
|
+
numbers.insert(5); // [5]
|
159
|
+
numbers.insert(2); // [2, 5]
|
160
|
+
numbers.insert(8); // [2, 5, 8]
|
161
|
+
numbers.insert(5); // [2, 5, 5, 8] - duplicates allowed
|
162
|
+
|
163
|
+
// Binary search operations
|
164
|
+
console.log(numbers.indexOf(5)); // 1 - first occurrence
|
165
|
+
console.log(numbers.has(8)); // true
|
39
166
|
```
|
40
167
|
|
41
|
-
|
168
|
+
#### Advanced Search Operations
|
169
|
+
|
170
|
+
```typescript
|
171
|
+
// Custom object sorting
|
172
|
+
interface Task {
|
173
|
+
priority: number;
|
174
|
+
name: string;
|
175
|
+
}
|
176
|
+
|
177
|
+
const tasks = new BinarySearchArray<Task>(
|
178
|
+
(a, b) => a.priority - b.priority
|
179
|
+
);
|
180
|
+
|
181
|
+
tasks.insert({ priority: 3, name: 'Medium task' });
|
182
|
+
tasks.insert({ priority: 1, name: 'High priority' });
|
183
|
+
tasks.insert({ priority: 5, name: 'Low priority' });
|
184
|
+
|
185
|
+
// Access sorted elements
|
186
|
+
console.log(tasks.at(0)); // { priority: 1, name: 'High priority' }
|
187
|
+
|
188
|
+
// Find insertion points
|
189
|
+
const newTask = { priority: 2, name: 'New task' };
|
190
|
+
const lowerBound = tasks.lowerBound(newTask); // First position >= 2
|
191
|
+
const upperBound = tasks.upperBound(newTask); // First position > 2
|
192
|
+
|
193
|
+
// Remove elements
|
194
|
+
tasks.removeFirst(newTask); // Remove first matching element
|
195
|
+
tasks.removeOne(0); // Remove at index
|
196
|
+
tasks.remove(0, 2); // Remove range
|
197
|
+
|
198
|
+
// Iteration (in sorted order)
|
199
|
+
for (const task of tasks) {
|
200
|
+
console.log(task.name);
|
201
|
+
}
|
202
|
+
```
|
203
|
+
|
204
|
+
## Real-World Use Cases
|
205
|
+
|
206
|
+
### Message Queue System
|
207
|
+
|
208
|
+
```typescript
|
209
|
+
class MessageQueue<T> {
|
210
|
+
private buffer = new RingBuffer<T>(1000);
|
211
|
+
|
212
|
+
enqueue(message: T): void {
|
213
|
+
if (this.buffer.length >= this.buffer.capacity) {
|
214
|
+
this.buffer.shift(); // Remove oldest if full
|
215
|
+
}
|
216
|
+
this.buffer.push(message);
|
217
|
+
}
|
218
|
+
|
219
|
+
dequeue(): T | undefined {
|
220
|
+
return this.buffer.shift();
|
221
|
+
}
|
222
|
+
|
223
|
+
peek(): T | undefined {
|
224
|
+
return this.buffer.peekFirst();
|
225
|
+
}
|
226
|
+
|
227
|
+
size(): number {
|
228
|
+
return this.buffer.length;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
```
|
232
|
+
|
233
|
+
### Sliding Window Rate Limiter
|
234
|
+
|
235
|
+
```typescript
|
236
|
+
class RateLimiter {
|
237
|
+
private timestamps: RingBuffer<number>;
|
238
|
+
private windowMs: number;
|
239
|
+
private maxRequests: number;
|
240
|
+
|
241
|
+
constructor(maxRequests: number, windowMs: number) {
|
242
|
+
this.timestamps = new RingBuffer<number>(maxRequests);
|
243
|
+
this.windowMs = windowMs;
|
244
|
+
this.maxRequests = maxRequests;
|
245
|
+
}
|
246
|
+
|
247
|
+
tryRequest(): boolean {
|
248
|
+
const now = Date.now();
|
249
|
+
const cutoff = now - this.windowMs;
|
250
|
+
|
251
|
+
// Remove old timestamps outside the window
|
252
|
+
while (!this.timestamps.isEmpty() &&
|
253
|
+
this.timestamps.peekFirst()! < cutoff) {
|
254
|
+
this.timestamps.shift();
|
255
|
+
}
|
256
|
+
|
257
|
+
if (this.timestamps.length < this.maxRequests) {
|
258
|
+
this.timestamps.push(now);
|
259
|
+
return true;
|
260
|
+
}
|
261
|
+
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
}
|
42
265
|
```
|
43
|
-
|
44
|
-
|
45
|
-
|
266
|
+
|
267
|
+
### Priority Task Scheduler
|
268
|
+
|
269
|
+
```typescript
|
270
|
+
interface ScheduledTask {
|
271
|
+
runAt: number;
|
272
|
+
task: () => void;
|
273
|
+
id: string;
|
274
|
+
}
|
275
|
+
|
276
|
+
class TaskScheduler {
|
277
|
+
private tasks = new BinarySearchArray<ScheduledTask>(
|
278
|
+
(a, b) => a.runAt - b.runAt
|
279
|
+
);
|
280
|
+
|
281
|
+
schedule(task: () => void, delayMs: number): string {
|
282
|
+
const id = crypto.randomUUID();
|
283
|
+
this.tasks.insert({
|
284
|
+
runAt: Date.now() + delayMs,
|
285
|
+
task,
|
286
|
+
id
|
287
|
+
});
|
288
|
+
return id;
|
289
|
+
}
|
290
|
+
|
291
|
+
getNextTask(): ScheduledTask | undefined {
|
292
|
+
if (this.tasks.length === 0) return undefined;
|
293
|
+
|
294
|
+
const next = this.tasks.at(0);
|
295
|
+
if (next && next.runAt <= Date.now()) {
|
296
|
+
this.tasks.removeOne(0);
|
297
|
+
return next;
|
298
|
+
}
|
299
|
+
|
300
|
+
return undefined;
|
301
|
+
}
|
302
|
+
|
303
|
+
cancel(id: string): boolean {
|
304
|
+
const index = this.tasks.indexOf({ id } as any);
|
305
|
+
if (index >= 0) {
|
306
|
+
this.tasks.removeOne(index);
|
307
|
+
return true;
|
308
|
+
}
|
309
|
+
return false;
|
310
|
+
}
|
311
|
+
}
|
46
312
|
```
|
47
313
|
|
48
|
-
|
314
|
+
## Performance
|
315
|
+
|
316
|
+
FastDS significantly outperforms popular alternatives in various scenarios:
|
317
|
+
|
318
|
+
### Push + Shift Operations (Queue behavior)
|
49
319
|
```
|
50
|
-
|
51
|
-
denque
|
52
|
-
|
53
|
-
ring.remove 5k x 552,017,913 ops/sec ±0.52% (95 runs sampled)
|
320
|
+
FastDS RingBuffer: 65,390,479 ops/sec
|
321
|
+
denque: 54,993,484 ops/sec (19% slower)
|
322
|
+
double-ended-queue: 38,349,016 ops/sec (41% slower)
|
54
323
|
```
|
55
324
|
|
56
|
-
|
325
|
+
### Element Removal
|
57
326
|
```
|
58
|
-
|
59
|
-
|
60
|
-
ring x 62,745 ops/sec ±0.38% (98 runs sampled)
|
327
|
+
FastDS RingBuffer: 552,017,913 ops/sec
|
328
|
+
denque: 416,564,218 ops/sec (25% slower)
|
61
329
|
```
|
62
330
|
|
63
|
-
|
331
|
+
### Array Creation from Large Dataset
|
64
332
|
```
|
65
|
-
|
66
|
-
|
67
|
-
|
333
|
+
FastDS RingBuffer: 1,831 ops/sec
|
334
|
+
denque: 1,796 ops/sec (2% slower)
|
335
|
+
double-ended-queue: 387 ops/sec (79% slower)
|
68
336
|
```
|
337
|
+
|
338
|
+
## API Reference
|
339
|
+
|
340
|
+
### RingBuffer<T>
|
341
|
+
|
342
|
+
#### Constructor & Static Methods
|
343
|
+
- `new RingBuffer<T>(capacity?: number)` - Create a new ring buffer
|
344
|
+
- `RingBuffer.from<T>(values: T[])` - Create from an array
|
345
|
+
|
346
|
+
#### Properties
|
347
|
+
- `length: number` - Number of elements
|
348
|
+
- `capacity: number` - Current capacity
|
349
|
+
|
350
|
+
#### Core Operations
|
351
|
+
- `push(value: T): this` - Add to end
|
352
|
+
- `pop(): T | undefined` - Remove from end
|
353
|
+
- `shift(): T | undefined` - Remove from beginning
|
354
|
+
- `unshift(value: T): this` - Add to beginning
|
355
|
+
|
356
|
+
#### Access Methods
|
357
|
+
- `peekAt(index: number): T | undefined` - Get element at index
|
358
|
+
- `peekFirst(): T | undefined` - Get first element
|
359
|
+
- `peekLast(): T | undefined` - Get last element
|
360
|
+
|
361
|
+
#### Modification Methods
|
362
|
+
- `setOne(index: number, value: T, insert?: boolean): boolean` - Set/insert at index
|
363
|
+
- `removeOne(index: number): number` - Remove at index
|
364
|
+
- `removeFirst(value: T, index?: number): number` - Remove first occurrence
|
365
|
+
- `clear(): this` - Remove all elements
|
366
|
+
|
367
|
+
#### Utility Methods
|
368
|
+
- `toArray(): T[]` - Convert to array
|
369
|
+
- `slice(start?: number, end?: number): T[]` - Get slice as array
|
370
|
+
- `indexOf(value: T, index?: number): number` - Find index of value
|
371
|
+
- `has(value: T): boolean` - Check if contains value
|
372
|
+
- `compact(filter: (value: T) => boolean): boolean` - Filter and compact
|
373
|
+
|
374
|
+
### BinarySearchArray<T>
|
375
|
+
|
376
|
+
#### Constructor
|
377
|
+
- `new BinarySearchArray<T>(comparator: (a: T, b: T) => number)` - Create with comparator
|
378
|
+
|
379
|
+
#### Properties
|
380
|
+
- `length: number` - Number of elements
|
381
|
+
|
382
|
+
#### Operations
|
383
|
+
- `insert(value: T): number` - Insert in sorted position
|
384
|
+
- `at(index: number): T | undefined` - Get element at index
|
385
|
+
- `indexOf(value: T, index?: number): number` - Binary search for value
|
386
|
+
- `has(value: T): boolean` - Check if contains value
|
387
|
+
- `lowerBound(value: T): number` - Find first position >= value
|
388
|
+
- `upperBound(value: T): number` - Find first position > value
|
389
|
+
- `removeOne(index: number): number` - Remove at index
|
390
|
+
- `removeFirst(value: T): number` - Remove first occurrence
|
391
|
+
|
392
|
+
## TypeScript Support
|
393
|
+
|
394
|
+
FastDS is written in TypeScript and provides full type definitions:
|
395
|
+
|
396
|
+
```typescript
|
397
|
+
import { RingBuffer, BinarySearchArray, type Comparator } from 'fastds';
|
398
|
+
|
399
|
+
// Full type inference
|
400
|
+
const buffer = new RingBuffer<string>();
|
401
|
+
buffer.push(123); // ❌ Type error
|
402
|
+
|
403
|
+
// Custom types
|
404
|
+
interface User {
|
405
|
+
id: number;
|
406
|
+
name: string;
|
407
|
+
score: number;
|
408
|
+
}
|
409
|
+
|
410
|
+
const users = new RingBuffer<User>();
|
411
|
+
const sortedUsers = new BinarySearchArray<User>(
|
412
|
+
(a, b) => a.score - b.score
|
413
|
+
);
|
414
|
+
```
|
415
|
+
|
416
|
+
## Contributing
|
417
|
+
|
418
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
419
|
+
|
69
420
|
## License
|
70
421
|
|
71
|
-
|
422
|
+
[Apache-2.0 License](./LICENSE)
|
423
|
+
|
72
424
|
Copyright (c) 2025 Ivan Zakharchanka
|
73
425
|
|
74
426
|
[npm-url]: https://www.npmjs.com/package/fastds
|
@@ -79,5 +431,4 @@ Copyright (c) 2025 Ivan Zakharchanka
|
|
79
431
|
[codecov-url]: https://codecov.io/gh/3axap4eHko/fastds
|
80
432
|
[codecov-image]: https://codecov.io/gh/3axap4eHko/fastds/branch/master/graph/badge.svg?maxAge=43200
|
81
433
|
[snyk-url]: https://snyk.io/test/npm/fastds/latest
|
82
|
-
[snyk-image]: https://snyk.io/test/github/3axap4eHko/fastds/badge.svg?maxAge=43200
|
83
|
-
|
434
|
+
[snyk-image]: https://snyk.io/test/github/3axap4eHko/fastds/badge.svg?maxAge=43200
|
package/build/binary-search.cjs
CHANGED
@@ -26,11 +26,14 @@ class BinarySearchArray {
|
|
26
26
|
lowerBound(value) {
|
27
27
|
const length = this.#buffer.length;
|
28
28
|
if (length === 0) return 0;
|
29
|
+
const buffer = this.#buffer;
|
30
|
+
const comparator = this.#comparator;
|
29
31
|
let left = 0;
|
30
32
|
let right = length;
|
31
33
|
while(left < right){
|
32
34
|
const mid = left + right >>> 1;
|
33
|
-
|
35
|
+
const midValue = buffer.peekAt(mid);
|
36
|
+
if (comparator(midValue, value) < 0) {
|
34
37
|
left = mid + 1;
|
35
38
|
} else {
|
36
39
|
right = mid;
|
@@ -41,11 +44,14 @@ class BinarySearchArray {
|
|
41
44
|
upperBound(value) {
|
42
45
|
const length = this.#buffer.length;
|
43
46
|
if (length === 0) return 0;
|
47
|
+
const buffer = this.#buffer;
|
48
|
+
const comparator = this.#comparator;
|
44
49
|
let left = 0;
|
45
50
|
let right = length;
|
46
51
|
while(left < right){
|
47
52
|
const mid = left + right >>> 1;
|
48
|
-
|
53
|
+
const midValue = buffer.peekAt(mid);
|
54
|
+
if (comparator(midValue, value) <= 0) {
|
49
55
|
left = mid + 1;
|
50
56
|
} else {
|
51
57
|
right = mid;
|
@@ -56,11 +62,14 @@ class BinarySearchArray {
|
|
56
62
|
indexOf(value, index = 0) {
|
57
63
|
const length = this.#buffer.length;
|
58
64
|
if (length === 0 || index >= length) return -1;
|
65
|
+
const buffer = this.#buffer;
|
66
|
+
const comparator = this.#comparator;
|
59
67
|
let left = index;
|
60
68
|
let right = length - 1;
|
61
69
|
while(left <= right){
|
62
70
|
const mid = left + right >>> 1;
|
63
|
-
const
|
71
|
+
const midValue = buffer.peekAt(mid);
|
72
|
+
const cmp = comparator(midValue, value);
|
64
73
|
if (cmp === 0) {
|
65
74
|
return mid;
|
66
75
|
} else if (cmp < 0) {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/binary-search.ts"],"sourcesContent":["import { RingBuffer } from './ring-buffer.js';\n\nexport interface Comparator<T> {\n (a: T, b: T): number;\n}\n\nexport class BinarySearchArray<T> implements Iterable<T, void, unknown> {\n #buffer: RingBuffer<T>;\n #comparator: Comparator<T>;\n\n constructor(comparator: Comparator<T>) {\n this.#buffer = new RingBuffer();\n this.#comparator = comparator;\n }\n\n readonly [Symbol.toStringTag] = 'BinarySearchArray';\n\n get length(): number {\n return this.#buffer.length;\n }\n\n at(index: number) {\n return this.#buffer.peekAt(index);\n }\n\n lowerBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n
|
1
|
+
{"version":3,"sources":["../src/binary-search.ts"],"sourcesContent":["import { RingBuffer } from './ring-buffer.js';\n\nexport interface Comparator<T> {\n (a: T, b: T): number;\n}\n\nexport class BinarySearchArray<T> implements Iterable<T, void, unknown> {\n #buffer: RingBuffer<T>;\n #comparator: Comparator<T>;\n\n constructor(comparator: Comparator<T>) {\n this.#buffer = new RingBuffer();\n this.#comparator = comparator;\n }\n\n readonly [Symbol.toStringTag] = 'BinarySearchArray';\n\n get length(): number {\n return this.#buffer.length;\n }\n\n at(index: number) {\n return this.#buffer.peekAt(index);\n }\n\n lowerBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n if (comparator(midValue, value) < 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n upperBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n if (comparator(midValue, value) <= 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n indexOf(value: T, index = 0) {\n const length = this.#buffer.length;\n if (length === 0 || index >= length) return -1;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = index;\n let right = length - 1;\n\n while (left <= right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n const cmp = comparator(midValue, value);\n\n if (cmp === 0) {\n return mid;\n } else if (cmp < 0) {\n left = mid + 1;\n } else {\n right = mid - 1;\n }\n }\n\n return -1;\n }\n\n has(value: T): boolean {\n return this.indexOf(value) !== -1;\n }\n\n insert(value: T) {\n const index = this.lowerBound(value);\n this.#buffer.setOne(index, value, true);\n\n return index;\n }\n\n removeOne(index: number) {\n return this.#buffer.removeOne(index);\n }\n\n removeFirst(value: T) {\n const index = this.indexOf(value);\n return this.#buffer.removeOne(index);\n }\n\n remove(index: number, count: number): this {\n this.#buffer.deallocate(index, count);\n\n return this;\n }\n\n iter() {\n return this.#buffer.iter();\n }\n\n [Symbol.iterator](): Iterator<T> {\n return this.#buffer[Symbol.iterator]();\n }\n}\n"],"names":["BinarySearchArray","comparator","RingBuffer","Symbol","toStringTag","length","at","index","peekAt","lowerBound","value","buffer","left","right","mid","midValue","upperBound","indexOf","cmp","has","insert","setOne","removeOne","removeFirst","remove","count","deallocate","iter","iterator"],"mappings":";;;;+BAMaA;;;eAAAA;;;+BANc;AAMpB,MAAMA;IACX,CAAA,MAAO,CAAgB;IACvB,CAAA,UAAW,CAAgB;IAE3B,YAAYC,UAAyB,CAAE;QACrC,IAAI,CAAC,CAAA,MAAO,GAAG,IAAIC,yBAAU;QAC7B,IAAI,CAAC,CAAA,UAAW,GAAGD;IACrB;IAES,CAACE,OAAOC,WAAW,CAAC,GAAG,oBAAoB;IAEpD,IAAIC,SAAiB;QACnB,OAAO,IAAI,CAAC,CAAA,MAAO,CAACA,MAAM;IAC5B;IAEAC,GAAGC,KAAa,EAAE;QAChB,OAAO,IAAI,CAAC,CAAA,MAAO,CAACC,MAAM,CAACD;IAC7B;IAEAE,WAAWC,KAAQ,EAAU;QAC3B,MAAML,SAAS,IAAI,CAAC,CAAA,MAAO,CAACA,MAAM;QAClC,IAAIA,WAAW,GAAG,OAAO;QAEzB,MAAMM,SAAS,IAAI,CAAC,CAAA,MAAO;QAC3B,MAAMV,aAAa,IAAI,CAAC,CAAA,UAAW;QACnC,IAAIW,OAAO;QACX,IAAIC,QAAQR;QAEZ,MAAOO,OAAOC,MAAO;YACnB,MAAMC,MAAM,AAACF,OAAOC,UAAW;YAC/B,MAAME,WAAWJ,OAAOH,MAAM,CAACM;YAC/B,IAAIb,WAAWc,UAAUL,SAAS,GAAG;gBACnCE,OAAOE,MAAM;YACf,OAAO;gBACLD,QAAQC;YACV;QACF;QAEA,OAAOF;IACT;IAEAI,WAAWN,KAAQ,EAAU;QAC3B,MAAML,SAAS,IAAI,CAAC,CAAA,MAAO,CAACA,MAAM;QAClC,IAAIA,WAAW,GAAG,OAAO;QAEzB,MAAMM,SAAS,IAAI,CAAC,CAAA,MAAO;QAC3B,MAAMV,aAAa,IAAI,CAAC,CAAA,UAAW;QACnC,IAAIW,OAAO;QACX,IAAIC,QAAQR;QAEZ,MAAOO,OAAOC,MAAO;YACnB,MAAMC,MAAM,AAACF,OAAOC,UAAW;YAC/B,MAAME,WAAWJ,OAAOH,MAAM,CAACM;YAC/B,IAAIb,WAAWc,UAAUL,UAAU,GAAG;gBACpCE,OAAOE,MAAM;YACf,OAAO;gBACLD,QAAQC;YACV;QACF;QAEA,OAAOF;IACT;IAEAK,QAAQP,KAAQ,EAAEH,QAAQ,CAAC,EAAE;QAC3B,MAAMF,SAAS,IAAI,CAAC,CAAA,MAAO,CAACA,MAAM;QAClC,IAAIA,WAAW,KAAKE,SAASF,QAAQ,OAAO,CAAC;QAE7C,MAAMM,SAAS,IAAI,CAAC,CAAA,MAAO;QAC3B,MAAMV,aAAa,IAAI,CAAC,CAAA,UAAW;QACnC,IAAIW,OAAOL;QACX,IAAIM,QAAQR,SAAS;QAErB,MAAOO,QAAQC,MAAO;YACpB,MAAMC,MAAM,AAACF,OAAOC,UAAW;YAC/B,MAAME,WAAWJ,OAAOH,MAAM,CAACM;YAC/B,MAAMI,MAAMjB,WAAWc,UAAUL;YAEjC,IAAIQ,QAAQ,GAAG;gBACb,OAAOJ;YACT,OAAO,IAAII,MAAM,GAAG;gBAClBN,OAAOE,MAAM;YACf,OAAO;gBACLD,QAAQC,MAAM;YAChB;QACF;QAEA,OAAO,CAAC;IACV;IAEAK,IAAIT,KAAQ,EAAW;QACrB,OAAO,IAAI,CAACO,OAAO,CAACP,WAAW,CAAC;IAClC;IAEAU,OAAOV,KAAQ,EAAE;QACf,MAAMH,QAAQ,IAAI,CAACE,UAAU,CAACC;QAC9B,IAAI,CAAC,CAAA,MAAO,CAACW,MAAM,CAACd,OAAOG,OAAO;QAElC,OAAOH;IACT;IAEAe,UAAUf,KAAa,EAAE;QACvB,OAAO,IAAI,CAAC,CAAA,MAAO,CAACe,SAAS,CAACf;IAChC;IAEAgB,YAAYb,KAAQ,EAAE;QACpB,MAAMH,QAAQ,IAAI,CAACU,OAAO,CAACP;QAC3B,OAAO,IAAI,CAAC,CAAA,MAAO,CAACY,SAAS,CAACf;IAChC;IAEAiB,OAAOjB,KAAa,EAAEkB,KAAa,EAAQ;QACzC,IAAI,CAAC,CAAA,MAAO,CAACC,UAAU,CAACnB,OAAOkB;QAE/B,OAAO,IAAI;IACb;IAEAE,OAAO;QACL,OAAO,IAAI,CAAC,CAAA,MAAO,CAACA,IAAI;IAC1B;IAEA,CAACxB,OAAOyB,QAAQ,CAAC,GAAgB;QAC/B,OAAO,IAAI,CAAC,CAAA,MAAO,CAACzB,OAAOyB,QAAQ,CAAC;IACtC;AACF"}
|
package/build/binary-search.js
CHANGED
@@ -16,11 +16,14 @@ export class BinarySearchArray {
|
|
16
16
|
lowerBound(value) {
|
17
17
|
const length = this.#buffer.length;
|
18
18
|
if (length === 0) return 0;
|
19
|
+
const buffer = this.#buffer;
|
20
|
+
const comparator = this.#comparator;
|
19
21
|
let left = 0;
|
20
22
|
let right = length;
|
21
23
|
while(left < right){
|
22
24
|
const mid = left + right >>> 1;
|
23
|
-
|
25
|
+
const midValue = buffer.peekAt(mid);
|
26
|
+
if (comparator(midValue, value) < 0) {
|
24
27
|
left = mid + 1;
|
25
28
|
} else {
|
26
29
|
right = mid;
|
@@ -31,11 +34,14 @@ export class BinarySearchArray {
|
|
31
34
|
upperBound(value) {
|
32
35
|
const length = this.#buffer.length;
|
33
36
|
if (length === 0) return 0;
|
37
|
+
const buffer = this.#buffer;
|
38
|
+
const comparator = this.#comparator;
|
34
39
|
let left = 0;
|
35
40
|
let right = length;
|
36
41
|
while(left < right){
|
37
42
|
const mid = left + right >>> 1;
|
38
|
-
|
43
|
+
const midValue = buffer.peekAt(mid);
|
44
|
+
if (comparator(midValue, value) <= 0) {
|
39
45
|
left = mid + 1;
|
40
46
|
} else {
|
41
47
|
right = mid;
|
@@ -46,11 +52,14 @@ export class BinarySearchArray {
|
|
46
52
|
indexOf(value, index = 0) {
|
47
53
|
const length = this.#buffer.length;
|
48
54
|
if (length === 0 || index >= length) return -1;
|
55
|
+
const buffer = this.#buffer;
|
56
|
+
const comparator = this.#comparator;
|
49
57
|
let left = index;
|
50
58
|
let right = length - 1;
|
51
59
|
while(left <= right){
|
52
60
|
const mid = left + right >>> 1;
|
53
|
-
const
|
61
|
+
const midValue = buffer.peekAt(mid);
|
62
|
+
const cmp = comparator(midValue, value);
|
54
63
|
if (cmp === 0) {
|
55
64
|
return mid;
|
56
65
|
} else if (cmp < 0) {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/binary-search.ts"],"sourcesContent":["import { RingBuffer } from './ring-buffer.js';\n\nexport interface Comparator<T> {\n (a: T, b: T): number;\n}\n\nexport class BinarySearchArray<T> implements Iterable<T, void, unknown> {\n #buffer: RingBuffer<T>;\n #comparator: Comparator<T>;\n\n constructor(comparator: Comparator<T>) {\n this.#buffer = new RingBuffer();\n this.#comparator = comparator;\n }\n\n readonly [Symbol.toStringTag] = 'BinarySearchArray';\n\n get length(): number {\n return this.#buffer.length;\n }\n\n at(index: number) {\n return this.#buffer.peekAt(index);\n }\n\n lowerBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n
|
1
|
+
{"version":3,"sources":["../src/binary-search.ts"],"sourcesContent":["import { RingBuffer } from './ring-buffer.js';\n\nexport interface Comparator<T> {\n (a: T, b: T): number;\n}\n\nexport class BinarySearchArray<T> implements Iterable<T, void, unknown> {\n #buffer: RingBuffer<T>;\n #comparator: Comparator<T>;\n\n constructor(comparator: Comparator<T>) {\n this.#buffer = new RingBuffer();\n this.#comparator = comparator;\n }\n\n readonly [Symbol.toStringTag] = 'BinarySearchArray';\n\n get length(): number {\n return this.#buffer.length;\n }\n\n at(index: number) {\n return this.#buffer.peekAt(index);\n }\n\n lowerBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n if (comparator(midValue, value) < 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n upperBound(value: T): number {\n const length = this.#buffer.length;\n if (length === 0) return 0;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = 0;\n let right = length;\n\n while (left < right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n if (comparator(midValue, value) <= 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n indexOf(value: T, index = 0) {\n const length = this.#buffer.length;\n if (length === 0 || index >= length) return -1;\n\n const buffer = this.#buffer;\n const comparator = this.#comparator;\n let left = index;\n let right = length - 1;\n\n while (left <= right) {\n const mid = (left + right) >>> 1;\n const midValue = buffer.peekAt(mid)!;\n const cmp = comparator(midValue, value);\n\n if (cmp === 0) {\n return mid;\n } else if (cmp < 0) {\n left = mid + 1;\n } else {\n right = mid - 1;\n }\n }\n\n return -1;\n }\n\n has(value: T): boolean {\n return this.indexOf(value) !== -1;\n }\n\n insert(value: T) {\n const index = this.lowerBound(value);\n this.#buffer.setOne(index, value, true);\n\n return index;\n }\n\n removeOne(index: number) {\n return this.#buffer.removeOne(index);\n }\n\n removeFirst(value: T) {\n const index = this.indexOf(value);\n return this.#buffer.removeOne(index);\n }\n\n remove(index: number, count: number): this {\n this.#buffer.deallocate(index, count);\n\n return this;\n }\n\n iter() {\n return this.#buffer.iter();\n }\n\n [Symbol.iterator](): Iterator<T> {\n return this.#buffer[Symbol.iterator]();\n }\n}\n"],"names":["RingBuffer","BinarySearchArray","comparator","Symbol","toStringTag","length","at","index","peekAt","lowerBound","value","buffer","left","right","mid","midValue","upperBound","indexOf","cmp","has","insert","setOne","removeOne","removeFirst","remove","count","deallocate","iter","iterator"],"mappings":"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"}
|