@peerbit/stream 4.4.0-780f7ce → 4.4.0-9b0640c
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/dist/src/index.d.ts +2 -10
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -79
- package/dist/src/index.js.map +1 -1
- package/dist/src/pushable-lanes.d.ts +22 -48
- package/dist/src/pushable-lanes.d.ts.map +1 -1
- package/dist/src/pushable-lanes.js +57 -183
- package/dist/src/pushable-lanes.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +11 -108
- package/src/pushable-lanes.ts +116 -285
package/src/pushable-lanes.ts
CHANGED
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
// pushable-lanes.ts
|
|
2
|
-
// A multi-lane async pushable with starvation-free scheduling.
|
|
3
|
-
// Inspired by it-pushable (MIT) and extended for multi-lane fairness.
|
|
4
|
-
//
|
|
5
|
-
// Features:
|
|
6
|
-
// - Async iterator you can .push() into
|
|
7
|
-
// - N lanes (priorities) with starvation-free scheduling (Weighted Round-Robin)
|
|
8
|
-
// - Optional strict priority mode for legacy behavior
|
|
9
|
-
// - Optional high-water mark (bytes) with overflow policy
|
|
10
|
-
//
|
|
11
|
-
// Usage:
|
|
12
|
-
// const p = pushableLanes<Uint8Array>({ lanes: 2 }); // default fairness = 'wrr'
|
|
13
|
-
// p.push(new Uint8Array([1]), 1); // slower lane
|
|
14
|
-
// p.push(new Uint8Array([0]), 0); // faster lane
|
|
15
|
-
// for await (const chunk of p) { ... }
|
|
16
|
-
//
|
|
17
|
-
// // Backpressure example (throw if > 8MB buffered):
|
|
18
|
-
// const q = pushableLanes<Uint8Array>({ lanes: 3, maxBufferedBytes: 8 * 1024 * 1024, overflow: 'throw' });
|
|
19
|
-
//
|
|
20
|
-
// Notes:
|
|
21
|
-
// - T must have a .byteLength number property (e.g. Uint8Array).
|
|
22
|
-
// - Lane indices are 0..(lanes-1). Defaults to lane 0.
|
|
23
1
|
import GenericFIFO from "fast-fifo";
|
|
24
2
|
import defer from "p-defer";
|
|
25
3
|
|
|
26
|
-
// -----------------------------
|
|
27
|
-
// Errors & shared option types
|
|
28
|
-
// -----------------------------
|
|
29
|
-
|
|
30
4
|
export class AbortError extends Error {
|
|
31
5
|
type: string;
|
|
32
6
|
code: string;
|
|
@@ -42,107 +16,66 @@ export interface AbortOptions {
|
|
|
42
16
|
signal?: AbortSignal;
|
|
43
17
|
}
|
|
44
18
|
|
|
45
|
-
// -----------------------------
|
|
46
|
-
// Public API interfaces
|
|
47
|
-
// -----------------------------
|
|
48
|
-
|
|
49
19
|
/**
|
|
50
20
|
* An iterable that you can push values into.
|
|
51
21
|
*/
|
|
52
22
|
export interface PushableLanes<T, R = void, N = unknown>
|
|
53
23
|
extends AsyncGenerator<T, R, N> {
|
|
54
24
|
/**
|
|
55
|
-
* End the iterable after all values in the buffer (if any) have been yielded.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
25
|
+
* End the iterable after all values in the buffer (if any) have been yielded. If an
|
|
26
|
+
* error is passed the buffer is cleared immediately and the next iteration will
|
|
27
|
+
* throw the passed error
|
|
58
28
|
*/
|
|
59
29
|
end(err?: Error): this;
|
|
60
30
|
|
|
61
31
|
/**
|
|
62
|
-
* Push a value into the iterable. Values are yielded in
|
|
63
|
-
* Values not yet consumed
|
|
32
|
+
* Push a value into the iterable. Values are yielded from the iterable in the order
|
|
33
|
+
* they are pushed. Values not yet consumed from the iterable are buffered.
|
|
64
34
|
*/
|
|
65
35
|
push(value: T, lane?: number): this;
|
|
66
36
|
|
|
67
37
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
38
|
+
* Returns a promise that resolves when the underlying queue becomes empty (e.g.
|
|
39
|
+
* this.readableLength === 0).
|
|
40
|
+
*
|
|
41
|
+
* If an AbortSignal is passed as an option and that signal aborts, it only
|
|
42
|
+
* causes the returned promise to reject - it does not end the pushable.
|
|
71
43
|
*/
|
|
72
44
|
onEmpty(options?: AbortOptions): Promise<void>;
|
|
73
45
|
|
|
74
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* This property contains the total number of bytes in the queue ready to be read.
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
|
|
75
51
|
get readableLength(): number;
|
|
76
52
|
|
|
77
53
|
/**
|
|
78
|
-
* Get readable length for
|
|
54
|
+
* Get readable length for specific lane
|
|
55
|
+
* @param lane
|
|
56
|
+
* @returns readable length for the lane
|
|
79
57
|
*/
|
|
80
58
|
getReadableLength(lane?: number): number;
|
|
81
59
|
}
|
|
82
60
|
|
|
83
|
-
/** How to distribute turns between lanes. */
|
|
84
|
-
export type FairnessMode = "priority" | "wrr";
|
|
85
|
-
|
|
86
|
-
/** What to do when buffer would exceed `maxBufferedBytes`. */
|
|
87
|
-
export type OverflowPolicy = "throw" | "drop-newest";
|
|
88
|
-
|
|
89
61
|
export interface Options {
|
|
90
62
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
63
|
+
* A function called after *all* values have been yielded from the iterator (including
|
|
64
|
+
* buffered values). In the case when the iterator is ended with an error it will be
|
|
65
|
+
* passed the error as a parameter.
|
|
93
66
|
*/
|
|
94
67
|
onEnd?(err?: Error): void;
|
|
95
68
|
|
|
96
69
|
/**
|
|
97
|
-
*
|
|
98
|
-
* Default: 1
|
|
70
|
+
* How many lanes, lane 0 is fastest and will drain before lane 1 is consumed
|
|
99
71
|
*/
|
|
100
72
|
lanes?: number;
|
|
101
|
-
|
|
102
73
|
/**
|
|
103
|
-
* Optional hook invoked on every successful push with the value and lane
|
|
104
|
-
* Useful for metrics/telemetry.
|
|
74
|
+
* Optional hook invoked on every successful push with the value and lane
|
|
105
75
|
*/
|
|
106
76
|
onPush?(value: { byteLength: number }, lane: number): void;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Fairness mode:
|
|
110
|
-
* - 'priority': strict priority (original behavior).
|
|
111
|
-
* - 'wrr': weighted round-robin (starvation-free).
|
|
112
|
-
* Default: 'wrr'
|
|
113
|
-
*/
|
|
114
|
-
fairness?: FairnessMode;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Weights per lane if fairness === 'wrr'. Larger weight = more service.
|
|
118
|
-
* Must have length === lanes and each weight >= 1.
|
|
119
|
-
* If omitted, weights are auto-generated from `bias`.
|
|
120
|
-
*/
|
|
121
|
-
weights?: number[];
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Bias factor for auto-generated weights when fairness === 'wrr'.
|
|
125
|
-
* For lanes L, weight[i] = floor(bias^(L-1-i)) with a minimum of 1.
|
|
126
|
-
* Default: 2 (e.g., lanes=4 -> [8,4,2,1])
|
|
127
|
-
*/
|
|
128
|
-
bias?: number;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Optional high-water mark in **bytes** across all lanes.
|
|
132
|
-
* If a `push` would exceed this many buffered bytes:
|
|
133
|
-
* - overflow: 'throw' -> throw an Error (default policy)
|
|
134
|
-
* - overflow: 'drop-newest' -> silently drop this pushed item
|
|
135
|
-
*/
|
|
136
|
-
maxBufferedBytes?: number;
|
|
137
|
-
|
|
138
|
-
/** Overflow policy when `maxBufferedBytes` would be exceeded. Default: 'throw' */
|
|
139
|
-
overflow?: OverflowPolicy;
|
|
140
77
|
}
|
|
141
78
|
|
|
142
|
-
// -----------------------------
|
|
143
|
-
// Internal queue primitives
|
|
144
|
-
// -----------------------------
|
|
145
|
-
|
|
146
79
|
export interface DoneResult {
|
|
147
80
|
done: true;
|
|
148
81
|
}
|
|
@@ -159,185 +92,94 @@ export interface Next<T> {
|
|
|
159
92
|
}
|
|
160
93
|
|
|
161
94
|
/**
|
|
162
|
-
*
|
|
95
|
+
* Fifo but with total readableLength counter
|
|
163
96
|
*/
|
|
164
|
-
class
|
|
165
|
-
|
|
166
|
-
|
|
97
|
+
class Uint8ArrayFifo<T extends { byteLength: number }> extends GenericFIFO<
|
|
98
|
+
Next<T>
|
|
99
|
+
> {
|
|
100
|
+
size: number = 0;
|
|
167
101
|
push(val: Next<T>): void {
|
|
168
|
-
if (val.value)
|
|
102
|
+
if (val.value) {
|
|
103
|
+
this.size += val.value.byteLength;
|
|
104
|
+
}
|
|
169
105
|
return super.push(val);
|
|
170
106
|
}
|
|
171
107
|
|
|
172
108
|
shift(): Next<T> | undefined {
|
|
173
109
|
const shifted = super.shift();
|
|
174
|
-
if (shifted?.value)
|
|
110
|
+
if (shifted?.value) {
|
|
111
|
+
this.size -= shifted.value.byteLength;
|
|
112
|
+
}
|
|
175
113
|
return shifted;
|
|
176
114
|
}
|
|
177
115
|
}
|
|
178
116
|
|
|
179
117
|
/**
|
|
180
|
-
* A
|
|
181
|
-
*
|
|
182
|
-
*
|
|
118
|
+
* A queue consisting of multiple 'lanes' with different priority to be emptied.
|
|
119
|
+
* The lane with index 0 will empty before lane with index 1 etc..
|
|
120
|
+
* TODO add an additional proprty to control whether we we pick objects from slower lanes
|
|
121
|
+
* so no lane get really "stuck"
|
|
183
122
|
*/
|
|
184
|
-
class
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
constructor(init: {
|
|
191
|
-
lanes: number;
|
|
192
|
-
fairness?: FairnessMode;
|
|
193
|
-
weights?: number[];
|
|
194
|
-
bias?: number;
|
|
195
|
-
}) {
|
|
196
|
-
const L = Math.max(1, init.lanes | 0);
|
|
197
|
-
this.mode = init.fairness ?? "wrr";
|
|
198
|
-
this.lanes = Array.from({ length: L }, () => new ByteFifo<T>());
|
|
199
|
-
|
|
200
|
-
if (this.mode === "wrr") {
|
|
201
|
-
const bias = init.bias ?? 2;
|
|
202
|
-
const auto = Array.from({ length: L }, (_, i) =>
|
|
203
|
-
Math.max(1, Math.floor(Math.pow(bias, L - 1 - i))),
|
|
204
|
-
);
|
|
205
|
-
const w = normalizeWeights(init.weights ?? auto, L);
|
|
206
|
-
|
|
207
|
-
// Build a simple round-robin schedule by repeating lanes according to weight.
|
|
208
|
-
this.schedule = [];
|
|
209
|
-
for (let i = 0; i < L; i++) {
|
|
210
|
-
for (let k = 0; k < w[i]; k++) this.schedule.push(i);
|
|
211
|
-
}
|
|
212
|
-
// Edge case: if all weights collapsed to zero (shouldn't), fall back to priority.
|
|
213
|
-
if (this.schedule.length === 0) {
|
|
214
|
-
this.schedule = Array.from({ length: L }, (_, i) => i);
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
// strict priority
|
|
218
|
-
this.schedule = Array.from({ length: L }, (_, i) => i);
|
|
123
|
+
class Uint8arrayPriorityQueue<T extends { byteLength: number }> {
|
|
124
|
+
lanes: Uint8ArrayFifo<T>[];
|
|
125
|
+
constructor(options: { lanes: number } = { lanes: 1 }) {
|
|
126
|
+
this.lanes = new Array(options.lanes);
|
|
127
|
+
for (let i = 0; i < this.lanes.length; i++) {
|
|
128
|
+
this.lanes[i] = new Uint8ArrayFifo();
|
|
219
129
|
}
|
|
220
130
|
}
|
|
221
131
|
|
|
222
|
-
get size()
|
|
132
|
+
get size() {
|
|
223
133
|
let sum = 0;
|
|
224
|
-
for (const lane of this.lanes)
|
|
134
|
+
for (const lane of this.lanes) {
|
|
135
|
+
sum += lane.size;
|
|
136
|
+
}
|
|
225
137
|
return sum;
|
|
226
138
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
push(val: Next<T>, lane: number): void {
|
|
230
|
-
const idx = clampLane(lane, this.lanes.length);
|
|
231
|
-
this.lanes[idx].push(val);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** True if all lanes are empty. */
|
|
235
|
-
isEmpty(): boolean {
|
|
236
|
-
for (const lane of this.lanes) if (!lane.isEmpty()) return false;
|
|
237
|
-
return true;
|
|
139
|
+
push(val: Next<T>, lane: number) {
|
|
140
|
+
return this.lanes[lane].push(val);
|
|
238
141
|
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Dequeue the next value by fairness rules.
|
|
242
|
-
* Ensures progress even if some schedule slots map to empty lanes.
|
|
243
|
-
*/
|
|
244
142
|
shift(): Next<T> | undefined {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (item) return item;
|
|
143
|
+
// fetch the first non undefined item.
|
|
144
|
+
// by iterating from index 0 up we define that lanes with lower index have higher prioirity
|
|
145
|
+
for (const lane of this.lanes) {
|
|
146
|
+
const element = lane.shift();
|
|
147
|
+
if (element) {
|
|
148
|
+
return element;
|
|
252
149
|
}
|
|
253
|
-
return undefined;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// WRR mode: use rotating schedule
|
|
257
|
-
const L = this.schedule.length;
|
|
258
|
-
for (let probes = 0; probes < L; probes++) {
|
|
259
|
-
const laneIdx = this.schedule[this.cursor];
|
|
260
|
-
this.cursor = (this.cursor + 1) % L;
|
|
261
|
-
|
|
262
|
-
const item = this.lanes[laneIdx].shift();
|
|
263
|
-
if (item) return item;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// (very unlikely) nothing was found despite size>0 – linear scan fallback
|
|
267
|
-
for (let i = 0; i < this.lanes.length; i++) {
|
|
268
|
-
const item = this.lanes[i].shift();
|
|
269
|
-
if (item) return item;
|
|
270
150
|
}
|
|
271
151
|
return undefined;
|
|
272
152
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
const w = weights.map((x) => (x && x > 0 ? Math.floor(x) : 0));
|
|
282
|
-
if (w.every((x) => x === 0)) {
|
|
283
|
-
// ensure at least 1 for all lanes to retain progress guarantees
|
|
284
|
-
return Array.from({ length: lanes }, () => 1);
|
|
153
|
+
isEmpty(): boolean {
|
|
154
|
+
for (const lane of this.lanes) {
|
|
155
|
+
if (!lane.isEmpty()) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
285
160
|
}
|
|
286
|
-
return w;
|
|
287
161
|
}
|
|
288
162
|
|
|
289
|
-
function clampLane(lane: number, lanes: number): number {
|
|
290
|
-
if (!Number.isFinite(lane)) return 0;
|
|
291
|
-
lane = lane | 0;
|
|
292
|
-
if (lane < 0) return 0;
|
|
293
|
-
if (lane >= lanes) return lanes - 1;
|
|
294
|
-
return lane;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// -----------------------------
|
|
298
|
-
// Factory
|
|
299
|
-
// -----------------------------
|
|
300
|
-
|
|
301
163
|
export function pushableLanes<T extends { byteLength: number } = Uint8Array>(
|
|
302
164
|
options: Options = {},
|
|
303
165
|
): PushableLanes<T> {
|
|
304
166
|
return _pushable<Uint8Array, T, PushableLanes<T>>(options);
|
|
305
167
|
}
|
|
306
168
|
|
|
307
|
-
//
|
|
308
|
-
// Core implementation
|
|
309
|
-
// -----------------------------
|
|
310
|
-
// Based on it-pushable, adapted to multi-lane buffered queues with fairness.
|
|
311
|
-
// Important invariants:
|
|
312
|
-
// - We resolve the internal "drain" promise whenever the buffer *becomes empty*.
|
|
313
|
-
// - After end(err), the iterator finishes; push() becomes a no-op.
|
|
314
|
-
|
|
169
|
+
// Modified from https://github.com/alanshaw/it-pushable
|
|
315
170
|
function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
316
171
|
options?: Options,
|
|
317
172
|
): ReturnType {
|
|
318
173
|
options = options ?? {};
|
|
319
174
|
let onEnd = options.onEnd;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
lanes: options.lanes ?? 1,
|
|
325
|
-
fairness: options.fairness ?? "wrr",
|
|
326
|
-
weights: options.weights,
|
|
327
|
-
bias: options.bias ?? 2,
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// After end(err) we may swap buffer to a simple ByteFifo to deliver the terminal signal/error.
|
|
331
|
-
const isLaneQueue = (buffer: any): buffer is LaneQueue<PushType> =>
|
|
332
|
-
buffer instanceof LaneQueue;
|
|
333
|
-
|
|
175
|
+
let buffer: Uint8arrayPriorityQueue<PushType> | Uint8ArrayFifo<PushType> =
|
|
176
|
+
new Uint8arrayPriorityQueue<PushType>(
|
|
177
|
+
options.lanes ? { lanes: options.lanes } : undefined,
|
|
178
|
+
);
|
|
334
179
|
let pushable: any;
|
|
335
180
|
let onNext: ((next: Next<PushType>, lane: number) => ReturnType) | null;
|
|
336
|
-
let ended
|
|
337
|
-
let drain = defer
|
|
338
|
-
|
|
339
|
-
const maxBytes = options.maxBufferedBytes;
|
|
340
|
-
const overflow: OverflowPolicy = options.overflow ?? "throw";
|
|
181
|
+
let ended: boolean;
|
|
182
|
+
let drain = defer();
|
|
341
183
|
|
|
342
184
|
const getNext = (): NextResult<ValueType> => {
|
|
343
185
|
const next: Next<PushType> | undefined = buffer.shift();
|
|
@@ -345,9 +187,11 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
345
187
|
if (next == null) {
|
|
346
188
|
return { done: true };
|
|
347
189
|
}
|
|
190
|
+
|
|
348
191
|
if (next.error != null) {
|
|
349
192
|
throw next.error;
|
|
350
193
|
}
|
|
194
|
+
|
|
351
195
|
return {
|
|
352
196
|
done: next.done === true,
|
|
353
197
|
// @ts-expect-error if done is false, value will be present
|
|
@@ -360,6 +204,7 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
360
204
|
if (!buffer.isEmpty()) {
|
|
361
205
|
return getNext();
|
|
362
206
|
}
|
|
207
|
+
|
|
363
208
|
if (ended) {
|
|
364
209
|
return { done: true };
|
|
365
210
|
}
|
|
@@ -368,20 +213,23 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
368
213
|
onNext = (next: Next<PushType>, lane: number) => {
|
|
369
214
|
onNext = null;
|
|
370
215
|
buffer.push(next, lane);
|
|
216
|
+
|
|
371
217
|
try {
|
|
372
218
|
resolve(getNext());
|
|
373
219
|
} catch (err: any) {
|
|
374
220
|
reject(err);
|
|
375
221
|
}
|
|
222
|
+
|
|
376
223
|
return pushable;
|
|
377
224
|
};
|
|
378
225
|
});
|
|
379
226
|
} finally {
|
|
380
|
-
// If buffer is empty after this turn, resolve the drain promise (in a microtask)
|
|
381
227
|
if (buffer.isEmpty()) {
|
|
228
|
+
// settle promise in the microtask queue to give consumers a chance to
|
|
229
|
+
// await after calling .push
|
|
382
230
|
queueMicrotask(() => {
|
|
383
231
|
drain.resolve();
|
|
384
|
-
drain = defer
|
|
232
|
+
drain = defer();
|
|
385
233
|
});
|
|
386
234
|
}
|
|
387
235
|
}
|
|
@@ -391,69 +239,46 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
391
239
|
if (onNext != null) {
|
|
392
240
|
return onNext(next, lane);
|
|
393
241
|
}
|
|
242
|
+
|
|
394
243
|
buffer.push(next, lane);
|
|
395
244
|
return pushable;
|
|
396
245
|
};
|
|
397
246
|
|
|
398
247
|
const bufferError = (err: Error): ReturnType => {
|
|
399
|
-
|
|
400
|
-
|
|
248
|
+
buffer = new Uint8ArrayFifo();
|
|
249
|
+
|
|
401
250
|
if (onNext != null) {
|
|
402
251
|
return onNext({ error: err }, 0);
|
|
403
252
|
}
|
|
253
|
+
|
|
404
254
|
buffer.push({ error: err });
|
|
405
255
|
return pushable;
|
|
406
256
|
};
|
|
407
257
|
|
|
408
|
-
const totalBufferedBytes = (): number => {
|
|
409
|
-
if (isLaneQueue(buffer)) return buffer.size;
|
|
410
|
-
return (buffer as ByteFifo<PushType>).size;
|
|
411
|
-
};
|
|
412
|
-
|
|
413
258
|
const push = (value: PushType, lane: number = 0): ReturnType => {
|
|
414
259
|
if (ended) {
|
|
415
|
-
// Ignore pushes after end() for safety (consistent with it-pushable).
|
|
416
260
|
return pushable;
|
|
417
261
|
}
|
|
418
262
|
|
|
419
|
-
// Simple backpressure: enforce HWM if configured
|
|
420
|
-
if (maxBytes != null && maxBytes > 0) {
|
|
421
|
-
const wouldBe = totalBufferedBytes() + value.byteLength;
|
|
422
|
-
if (wouldBe > maxBytes) {
|
|
423
|
-
if (overflow === "drop-newest") {
|
|
424
|
-
// silently drop this item
|
|
425
|
-
return pushable;
|
|
426
|
-
}
|
|
427
|
-
// default 'throw'
|
|
428
|
-
throw new Error(
|
|
429
|
-
`pushableLanes buffer overflow: ${wouldBe} bytes > maxBufferedBytes=${maxBytes}`,
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
263
|
const out = bufferNext({ done: false, value }, lane);
|
|
435
|
-
options?.onPush?.(
|
|
436
|
-
value,
|
|
437
|
-
clampLane(lane, isLaneQueue(buffer) ? buffer.lanes.length : 1),
|
|
438
|
-
);
|
|
264
|
+
options?.onPush?.(value, lane);
|
|
439
265
|
return out;
|
|
440
266
|
};
|
|
441
|
-
|
|
442
267
|
const end = (err?: Error): ReturnType => {
|
|
443
268
|
if (ended) return pushable;
|
|
444
269
|
ended = true;
|
|
270
|
+
|
|
445
271
|
return err != null ? bufferError(err) : bufferNext({ done: true }, 0);
|
|
446
272
|
};
|
|
447
|
-
|
|
448
273
|
const _return = (): DoneResult => {
|
|
449
|
-
|
|
450
|
-
buffer = new ByteFifo<PushType>();
|
|
274
|
+
buffer = new Uint8ArrayFifo();
|
|
451
275
|
end();
|
|
276
|
+
|
|
452
277
|
return { done: true };
|
|
453
278
|
};
|
|
454
|
-
|
|
455
279
|
const _throw = (err: Error): DoneResult => {
|
|
456
280
|
end(err);
|
|
281
|
+
|
|
457
282
|
return { done: true };
|
|
458
283
|
};
|
|
459
284
|
|
|
@@ -466,41 +291,45 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
466
291
|
throw: _throw,
|
|
467
292
|
push,
|
|
468
293
|
end,
|
|
469
|
-
|
|
470
294
|
get readableLength(): number {
|
|
471
|
-
return
|
|
295
|
+
return buffer.size;
|
|
472
296
|
},
|
|
473
297
|
|
|
474
298
|
getReadableLength(lane?: number): number {
|
|
475
|
-
if (lane == null)
|
|
476
|
-
|
|
477
|
-
const idx = clampLane(lane, buffer.lanes.length);
|
|
478
|
-
return buffer.lanes[idx].size;
|
|
299
|
+
if (lane == null) {
|
|
300
|
+
return buffer.size;
|
|
479
301
|
}
|
|
480
|
-
// After end/error we swap to a ByteFifo: only "total" makes sense.
|
|
481
|
-
return (buffer as ByteFifo<PushType>).size;
|
|
482
|
-
},
|
|
483
302
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
303
|
+
if (buffer instanceof Uint8arrayPriorityQueue) {
|
|
304
|
+
return buffer.lanes[lane].size;
|
|
305
|
+
}
|
|
306
|
+
return buffer.size; // we can only arrive here if we are "done" or "err" or "end" where we reasign the buffer to a simple one and put 1 message into it
|
|
307
|
+
},
|
|
308
|
+
onEmpty: async (options?: AbortOptions) => {
|
|
309
|
+
const signal = options?.signal;
|
|
310
|
+
signal?.throwIfAborted();
|
|
487
311
|
|
|
488
|
-
if (buffer.isEmpty())
|
|
312
|
+
if (buffer.isEmpty()) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
489
315
|
|
|
490
316
|
let cancel: Promise<void> | undefined;
|
|
491
317
|
let listener: (() => void) | undefined;
|
|
492
318
|
|
|
493
319
|
if (signal != null) {
|
|
494
|
-
cancel = new Promise
|
|
495
|
-
listener = () =>
|
|
496
|
-
|
|
320
|
+
cancel = new Promise((resolve, reject) => {
|
|
321
|
+
listener = () => {
|
|
322
|
+
reject(new AbortError());
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
signal.addEventListener("abort", listener);
|
|
497
326
|
});
|
|
498
327
|
}
|
|
499
328
|
|
|
500
329
|
try {
|
|
501
330
|
await Promise.race([drain.promise, cancel]);
|
|
502
331
|
} finally {
|
|
503
|
-
if (listener != null) {
|
|
332
|
+
if (listener != null && signal != null) {
|
|
504
333
|
signal?.removeEventListener("abort", listener);
|
|
505
334
|
}
|
|
506
335
|
}
|
|
@@ -511,7 +340,6 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
511
340
|
return pushable;
|
|
512
341
|
}
|
|
513
342
|
|
|
514
|
-
// Wrap with onEnd notifier
|
|
515
343
|
const _pushable = pushable;
|
|
516
344
|
|
|
517
345
|
pushable = {
|
|
@@ -523,36 +351,39 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
523
351
|
},
|
|
524
352
|
throw(err: Error) {
|
|
525
353
|
_pushable.throw(err);
|
|
354
|
+
|
|
526
355
|
if (onEnd != null) {
|
|
527
356
|
onEnd(err);
|
|
528
357
|
onEnd = undefined;
|
|
529
358
|
}
|
|
359
|
+
|
|
530
360
|
return { done: true };
|
|
531
361
|
},
|
|
532
362
|
return() {
|
|
533
363
|
_pushable.return();
|
|
364
|
+
|
|
534
365
|
if (onEnd != null) {
|
|
535
366
|
onEnd();
|
|
536
367
|
onEnd = undefined;
|
|
537
368
|
}
|
|
369
|
+
|
|
538
370
|
return { done: true };
|
|
539
371
|
},
|
|
540
372
|
push,
|
|
541
|
-
end(err
|
|
373
|
+
end(err: Error) {
|
|
542
374
|
_pushable.end(err);
|
|
375
|
+
|
|
543
376
|
if (onEnd != null) {
|
|
544
377
|
onEnd(err);
|
|
545
378
|
onEnd = undefined;
|
|
546
379
|
}
|
|
380
|
+
|
|
547
381
|
return pushable;
|
|
548
382
|
},
|
|
549
383
|
get readableLength() {
|
|
550
384
|
return _pushable.readableLength;
|
|
551
385
|
},
|
|
552
|
-
|
|
553
|
-
return _pushable.getReadableLength(lane);
|
|
554
|
-
},
|
|
555
|
-
onEmpty(opts?: AbortOptions) {
|
|
386
|
+
onEmpty: (opts?: AbortOptions) => {
|
|
556
387
|
return _pushable.onEmpty(opts);
|
|
557
388
|
},
|
|
558
389
|
};
|