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