@thi.ng/csp 2.1.81 → 2.1.83
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/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/buffer.js +59 -54
- package/channel.js +485 -485
- package/mult.js +95 -95
- package/package.json +13 -10
- package/pubsub.js +84 -81
package/channel.js
CHANGED
|
@@ -7,511 +7,511 @@ import { delayed } from "@thi.ng/transducers/delayed";
|
|
|
7
7
|
import { range } from "@thi.ng/transducers/range";
|
|
8
8
|
import { isReduced, unreduced } from "@thi.ng/transducers/reduced";
|
|
9
9
|
import { FixedBuffer } from "./buffer.js";
|
|
10
|
-
var State
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})(State ||
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
close = args[2];
|
|
83
|
-
break;
|
|
84
|
-
default:
|
|
85
|
-
illegalArity(args.length);
|
|
10
|
+
var State = /* @__PURE__ */ ((State2) => {
|
|
11
|
+
State2[State2["OPEN"] = 0] = "OPEN";
|
|
12
|
+
State2[State2["CLOSED"] = 1] = "CLOSED";
|
|
13
|
+
State2[State2["DONE"] = 2] = "DONE";
|
|
14
|
+
return State2;
|
|
15
|
+
})(State || {});
|
|
16
|
+
class Channel {
|
|
17
|
+
static constantly(x, delay) {
|
|
18
|
+
const chan = new Channel(delay ? delayed(delay) : null);
|
|
19
|
+
chan.produce(() => x);
|
|
20
|
+
return chan;
|
|
21
|
+
}
|
|
22
|
+
static repeatedly(fn, delay) {
|
|
23
|
+
const chan = new Channel(delay ? delayed(delay) : null);
|
|
24
|
+
chan.produce(fn);
|
|
25
|
+
return chan;
|
|
26
|
+
}
|
|
27
|
+
static cycle(src, delay) {
|
|
28
|
+
return Channel.from(cycle(src), delay ? delayed(delay) : null);
|
|
29
|
+
}
|
|
30
|
+
static range(...args) {
|
|
31
|
+
const [from, to, step, delay] = args;
|
|
32
|
+
return Channel.from(
|
|
33
|
+
range(from, to, step),
|
|
34
|
+
delay !== void 0 ? delayed(delay) : null
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Constructs new channel which closes automatically after given period.
|
|
39
|
+
*
|
|
40
|
+
* @param delay - time in ms
|
|
41
|
+
*/
|
|
42
|
+
static timeout(delay) {
|
|
43
|
+
const chan = new Channel(`timeout-${Channel.NEXT_ID++}`);
|
|
44
|
+
setTimeout(() => chan.close(), delay);
|
|
45
|
+
return chan;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Shorthand for: `Channel.timeout(delay).take()`
|
|
49
|
+
*
|
|
50
|
+
* @param delay - time in ms
|
|
51
|
+
*/
|
|
52
|
+
static sleep(delay) {
|
|
53
|
+
return Channel.timeout(delay).read();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates new channel with single value from given promise, then closes
|
|
57
|
+
* automatically iff promise has been resolved.
|
|
58
|
+
*
|
|
59
|
+
* @param p - promise
|
|
60
|
+
*/
|
|
61
|
+
static fromPromise(p) {
|
|
62
|
+
const chan = new Channel();
|
|
63
|
+
p.then(
|
|
64
|
+
(x) => (async () => {
|
|
65
|
+
await chan.write(x);
|
|
66
|
+
await chan.close();
|
|
67
|
+
return x;
|
|
68
|
+
})()
|
|
69
|
+
);
|
|
70
|
+
return chan;
|
|
71
|
+
}
|
|
72
|
+
static from(...args) {
|
|
73
|
+
let close, tx;
|
|
74
|
+
switch (args.length) {
|
|
75
|
+
case 1:
|
|
76
|
+
break;
|
|
77
|
+
case 2:
|
|
78
|
+
if (typeof args[1] === "boolean") {
|
|
79
|
+
close = args[1];
|
|
80
|
+
} else {
|
|
81
|
+
tx = args[1];
|
|
86
82
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
* an array of `[value, channel]`. Channel order is repeatedly
|
|
95
|
-
* shuffled for each read attempt.
|
|
96
|
-
*
|
|
97
|
-
* @param chans - source channels
|
|
98
|
-
*/
|
|
99
|
-
static select(chans) {
|
|
100
|
-
return new Promise((resolve) => {
|
|
101
|
-
const _select = () => {
|
|
102
|
-
for (let c of shuffle(chans)) {
|
|
103
|
-
if (c.isReadable() || c.isClosed()) {
|
|
104
|
-
c.read().then((x) => resolve([x, c]));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
Channel.SCHEDULE.call(null, _select, 0);
|
|
109
|
-
};
|
|
110
|
-
Channel.SCHEDULE.call(null, _select, 0);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Takes an array of channels to merge into new channel. Any closed
|
|
115
|
-
* channels will be automatically removed from the input selection.
|
|
116
|
-
* Once all inputs are closed, the target channel will close too (by
|
|
117
|
-
* default).
|
|
118
|
-
*
|
|
119
|
-
* @remarks
|
|
120
|
-
* If `named` is true, the merged channel will have tuples of:
|
|
121
|
-
* `[src-id, val]` If false (default), only received values will be
|
|
122
|
-
* forwarded.
|
|
123
|
-
*
|
|
124
|
-
* @param chans - source channels
|
|
125
|
-
* @param out - result channel
|
|
126
|
-
* @param close - true, if result closes
|
|
127
|
-
* @param named - true, to emit labeled tuples
|
|
128
|
-
*/
|
|
129
|
-
static merge(chans, out, close = true, named = false) {
|
|
130
|
-
out = out || new Channel();
|
|
131
|
-
(async () => {
|
|
132
|
-
while (true) {
|
|
133
|
-
let [x, ch] = await Channel.select(chans);
|
|
134
|
-
if (x === undefined) {
|
|
135
|
-
chans.splice(chans.indexOf(ch), 1);
|
|
136
|
-
if (!chans.length) {
|
|
137
|
-
close && (await out.close());
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
await out.write(named ? [ch.id, x] : x);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
})();
|
|
146
|
-
return out;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Takes an array of channels to merge into new channel of tuples.
|
|
150
|
-
* Whereas `Channel.merge()` realizes a sequential merging with no
|
|
151
|
-
* guarantees about ordering of the output.
|
|
152
|
-
*
|
|
153
|
-
* @remarks
|
|
154
|
-
* The output channel of this function will collect values from all
|
|
155
|
-
* channels and a new tuple is emitted only once a new value has
|
|
156
|
-
* been read from ALL channels. Therefore the overall throughput is
|
|
157
|
-
* dictated by the slowest of the inputs.
|
|
158
|
-
*
|
|
159
|
-
* Once any of the inputs closes, the process is terminated and the
|
|
160
|
-
* output channel is closed too (by default).
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```ts
|
|
164
|
-
* Channel.mergeTuples([
|
|
165
|
-
* Channel.from([1, 2, 3]),
|
|
166
|
-
* Channel.from([10, 20, 30]),
|
|
167
|
-
* Channel.from([100, 200, 300])
|
|
168
|
-
* ]).consume();
|
|
169
|
-
*
|
|
170
|
-
* // chan-0 : [ 1, 10, 100 ]
|
|
171
|
-
* // chan-0 : [ 2, 20, 200 ]
|
|
172
|
-
* // chan-0 : [ 3, 30, 300 ]
|
|
173
|
-
* // chan-0 done
|
|
174
|
-
*
|
|
175
|
-
* Channel.mergeTuples([
|
|
176
|
-
* Channel.from([1, 2, 3]),
|
|
177
|
-
* Channel.from([10, 20, 30]),
|
|
178
|
-
* Channel.from([100, 200, 300])
|
|
179
|
-
* ], null, false).consume();
|
|
180
|
-
* ```
|
|
181
|
-
*
|
|
182
|
-
* @param chans - source channels
|
|
183
|
-
* @param out - result channel
|
|
184
|
-
* @param closeOnFirst - true, if result closes when first input is done
|
|
185
|
-
* @param closeOutput - true, if result closes when all inputs are done
|
|
186
|
-
*/
|
|
187
|
-
static mergeTuples(chans, out, closeOnFirst = true, closeOutput = true) {
|
|
188
|
-
out = out || new Channel();
|
|
189
|
-
(async () => {
|
|
190
|
-
let buf = [];
|
|
191
|
-
let orig = [...chans];
|
|
192
|
-
let sel = new Set(chans);
|
|
193
|
-
let n = chans.length;
|
|
194
|
-
while (true) {
|
|
195
|
-
let [x, ch] = await Channel.select([...sel]);
|
|
196
|
-
let idx = orig.indexOf(ch);
|
|
197
|
-
if (x === undefined) {
|
|
198
|
-
if (closeOnFirst || chans.length === 1) {
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
chans.splice(idx, 1);
|
|
202
|
-
}
|
|
203
|
-
buf[idx] = x;
|
|
204
|
-
sel.delete(ch);
|
|
205
|
-
if (--n === 0) {
|
|
206
|
-
await out.write(buf);
|
|
207
|
-
buf = [];
|
|
208
|
-
n = chans.length;
|
|
209
|
-
sel = new Set(chans);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
closeOutput && (await out.close());
|
|
213
|
-
})();
|
|
214
|
-
return out;
|
|
83
|
+
break;
|
|
84
|
+
case 3:
|
|
85
|
+
tx = args[1];
|
|
86
|
+
close = args[2];
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
illegalArity(args.length);
|
|
215
90
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
switch (args.length) {
|
|
237
|
-
case 0:
|
|
238
|
-
break;
|
|
239
|
-
case 1:
|
|
240
|
-
if (typeof a === "string") {
|
|
241
|
-
id = a;
|
|
242
|
-
}
|
|
243
|
-
else if (maybeBuffer(a)) {
|
|
244
|
-
buf = a;
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
tx = a;
|
|
248
|
-
}
|
|
249
|
-
break;
|
|
250
|
-
case 2:
|
|
251
|
-
if (typeof a === "string") {
|
|
252
|
-
id = a;
|
|
253
|
-
if (maybeBuffer(b)) {
|
|
254
|
-
buf = b;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
tx = b;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
[tx, err] = args;
|
|
262
|
-
}
|
|
263
|
-
break;
|
|
264
|
-
case 3:
|
|
265
|
-
if (isFunction(args[1]) && isFunction(args[2])) {
|
|
266
|
-
[id, tx, err] = args;
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
[id, buf, tx] = args;
|
|
270
|
-
}
|
|
271
|
-
break;
|
|
272
|
-
case 4:
|
|
273
|
-
[id, buf, tx, err] = args;
|
|
274
|
-
break;
|
|
275
|
-
default:
|
|
276
|
-
illegalArity(args.length);
|
|
91
|
+
const chan = new Channel(tx);
|
|
92
|
+
chan.into(args[0], close);
|
|
93
|
+
return chan;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Takes an array of channels and blocks until any of them becomes
|
|
97
|
+
* readable (or has been closed). The returned promised resolves into
|
|
98
|
+
* an array of `[value, channel]`. Channel order is repeatedly
|
|
99
|
+
* shuffled for each read attempt.
|
|
100
|
+
*
|
|
101
|
+
* @param chans - source channels
|
|
102
|
+
*/
|
|
103
|
+
static select(chans) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const _select = () => {
|
|
106
|
+
for (let c of shuffle(chans)) {
|
|
107
|
+
if (c.isReadable() || c.isClosed()) {
|
|
108
|
+
c.read().then((x) => resolve([x, c]));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
277
111
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
112
|
+
Channel.SCHEDULE.call(null, _select, 0);
|
|
113
|
+
};
|
|
114
|
+
Channel.SCHEDULE.call(null, _select, 0);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Takes an array of channels to merge into new channel. Any closed
|
|
119
|
+
* channels will be automatically removed from the input selection.
|
|
120
|
+
* Once all inputs are closed, the target channel will close too (by
|
|
121
|
+
* default).
|
|
122
|
+
*
|
|
123
|
+
* @remarks
|
|
124
|
+
* If `named` is true, the merged channel will have tuples of:
|
|
125
|
+
* `[src-id, val]` If false (default), only received values will be
|
|
126
|
+
* forwarded.
|
|
127
|
+
*
|
|
128
|
+
* @param chans - source channels
|
|
129
|
+
* @param out - result channel
|
|
130
|
+
* @param close - true, if result closes
|
|
131
|
+
* @param named - true, to emit labeled tuples
|
|
132
|
+
*/
|
|
133
|
+
static merge(chans, out, close = true, named = false) {
|
|
134
|
+
out = out || new Channel();
|
|
135
|
+
(async () => {
|
|
136
|
+
while (true) {
|
|
137
|
+
let [x, ch] = await Channel.select(chans);
|
|
138
|
+
if (x === void 0) {
|
|
139
|
+
chans.splice(chans.indexOf(ch), 1);
|
|
140
|
+
if (!chans.length) {
|
|
141
|
+
close && await out.close();
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
await out.write(named ? [ch.id, x] : x);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Takes an array of channels to merge into new channel of tuples.
|
|
153
|
+
* Whereas `Channel.merge()` realizes a sequential merging with no
|
|
154
|
+
* guarantees about ordering of the output.
|
|
155
|
+
*
|
|
156
|
+
* @remarks
|
|
157
|
+
* The output channel of this function will collect values from all
|
|
158
|
+
* channels and a new tuple is emitted only once a new value has
|
|
159
|
+
* been read from ALL channels. Therefore the overall throughput is
|
|
160
|
+
* dictated by the slowest of the inputs.
|
|
161
|
+
*
|
|
162
|
+
* Once any of the inputs closes, the process is terminated and the
|
|
163
|
+
* output channel is closed too (by default).
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* Channel.mergeTuples([
|
|
168
|
+
* Channel.from([1, 2, 3]),
|
|
169
|
+
* Channel.from([10, 20, 30]),
|
|
170
|
+
* Channel.from([100, 200, 300])
|
|
171
|
+
* ]).consume();
|
|
172
|
+
*
|
|
173
|
+
* // chan-0 : [ 1, 10, 100 ]
|
|
174
|
+
* // chan-0 : [ 2, 20, 200 ]
|
|
175
|
+
* // chan-0 : [ 3, 30, 300 ]
|
|
176
|
+
* // chan-0 done
|
|
177
|
+
*
|
|
178
|
+
* Channel.mergeTuples([
|
|
179
|
+
* Channel.from([1, 2, 3]),
|
|
180
|
+
* Channel.from([10, 20, 30]),
|
|
181
|
+
* Channel.from([100, 200, 300])
|
|
182
|
+
* ], null, false).consume();
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @param chans - source channels
|
|
186
|
+
* @param out - result channel
|
|
187
|
+
* @param closeOnFirst - true, if result closes when first input is done
|
|
188
|
+
* @param closeOutput - true, if result closes when all inputs are done
|
|
189
|
+
*/
|
|
190
|
+
static mergeTuples(chans, out, closeOnFirst = true, closeOutput = true) {
|
|
191
|
+
out = out || new Channel();
|
|
192
|
+
(async () => {
|
|
193
|
+
let buf = [];
|
|
194
|
+
let orig = [...chans];
|
|
195
|
+
let sel = new Set(chans);
|
|
196
|
+
let n = chans.length;
|
|
197
|
+
while (true) {
|
|
198
|
+
let [x, ch] = await Channel.select([...sel]);
|
|
199
|
+
let idx = orig.indexOf(ch);
|
|
200
|
+
if (x === void 0) {
|
|
201
|
+
if (closeOnFirst || chans.length === 1) {
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
chans.splice(idx, 1);
|
|
205
|
+
}
|
|
206
|
+
buf[idx] = x;
|
|
207
|
+
sel.delete(ch);
|
|
208
|
+
if (--n === 0) {
|
|
209
|
+
await out.write(buf);
|
|
210
|
+
buf = [];
|
|
211
|
+
n = chans.length;
|
|
212
|
+
sel = new Set(chans);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
closeOutput && await out.close();
|
|
216
|
+
})();
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
static MAX_WRITES = 1024;
|
|
220
|
+
static NEXT_ID = 0;
|
|
221
|
+
static SCHEDULE = typeof setImmediate === "function" ? setImmediate : setTimeout;
|
|
222
|
+
static RFN = [
|
|
223
|
+
() => null,
|
|
224
|
+
(acc) => acc,
|
|
225
|
+
(acc, x) => acc.push(x)
|
|
226
|
+
];
|
|
227
|
+
id;
|
|
228
|
+
onerror;
|
|
229
|
+
state;
|
|
230
|
+
buf;
|
|
231
|
+
tx;
|
|
232
|
+
writes;
|
|
233
|
+
reads;
|
|
234
|
+
txbuf;
|
|
235
|
+
isBusy;
|
|
236
|
+
constructor(...args) {
|
|
237
|
+
let id, buf, tx, err;
|
|
238
|
+
let [a, b] = args;
|
|
239
|
+
switch (args.length) {
|
|
240
|
+
case 0:
|
|
241
|
+
break;
|
|
242
|
+
case 1:
|
|
243
|
+
if (typeof a === "string") {
|
|
244
|
+
id = a;
|
|
245
|
+
} else if (maybeBuffer(a)) {
|
|
246
|
+
buf = a;
|
|
247
|
+
} else {
|
|
248
|
+
tx = a;
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
case 2:
|
|
252
|
+
if (typeof a === "string") {
|
|
253
|
+
id = a;
|
|
254
|
+
if (maybeBuffer(b)) {
|
|
255
|
+
buf = b;
|
|
256
|
+
} else {
|
|
257
|
+
tx = b;
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
[tx, err] = args;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case 3:
|
|
264
|
+
if (isFunction(args[1]) && isFunction(args[2])) {
|
|
265
|
+
[id, tx, err] = args;
|
|
266
|
+
} else {
|
|
267
|
+
[id, buf, tx] = args;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case 4:
|
|
271
|
+
[id, buf, tx, err] = args;
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
illegalArity(args.length);
|
|
319
275
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
276
|
+
this.id = id || `chan-${Channel.NEXT_ID++}`;
|
|
277
|
+
buf = buf || 1;
|
|
278
|
+
this.buf = typeof buf === "number" ? new FixedBuffer(buf) : buf;
|
|
279
|
+
this.writes = new DCons();
|
|
280
|
+
this.reads = new DCons();
|
|
281
|
+
this.txbuf = new DCons();
|
|
282
|
+
this.tx = tx ? tx(Channel.RFN) : null;
|
|
283
|
+
this.onerror = tx && (err || defaultErrorHandler);
|
|
284
|
+
this.state = 0 /* OPEN */;
|
|
285
|
+
this.isBusy = false;
|
|
286
|
+
}
|
|
287
|
+
channel() {
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
write(value) {
|
|
291
|
+
return new Promise((resolve) => {
|
|
292
|
+
if (this.state !== 0 /* OPEN */) {
|
|
293
|
+
resolve(false);
|
|
294
|
+
}
|
|
295
|
+
if (this.writes.length < Channel.MAX_WRITES) {
|
|
296
|
+
this.writes.push({
|
|
297
|
+
value: this.tx ? async () => {
|
|
298
|
+
try {
|
|
299
|
+
if (isReduced(this.tx[2](this.txbuf, value))) {
|
|
300
|
+
this.state = 1 /* CLOSED */;
|
|
301
|
+
}
|
|
302
|
+
} catch (e) {
|
|
303
|
+
this.onerror(e, this, value);
|
|
324
304
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
tryRead(timeout = 1000) {
|
|
330
|
-
return new Promise((resolve) => {
|
|
331
|
-
(async () => resolve((await Channel.select([this, Channel.timeout(timeout)]))[0]))();
|
|
305
|
+
} : () => value,
|
|
306
|
+
resolve
|
|
332
307
|
});
|
|
308
|
+
this.process();
|
|
309
|
+
} else {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`channel stalled (${Channel.MAX_WRITES} unprocessed writes)`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
read() {
|
|
317
|
+
return new Promise((resolve) => {
|
|
318
|
+
if (this.state === 2 /* DONE */) {
|
|
319
|
+
resolve(void 0);
|
|
320
|
+
}
|
|
321
|
+
this.reads.push(resolve);
|
|
322
|
+
this.process();
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
tryRead(timeout = 1e3) {
|
|
326
|
+
return new Promise((resolve) => {
|
|
327
|
+
(async () => resolve(
|
|
328
|
+
(await Channel.select([this, Channel.timeout(timeout)]))[0]
|
|
329
|
+
))();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
close(flush = false) {
|
|
333
|
+
if (this.state === 0 /* OPEN */) {
|
|
334
|
+
this.state = 1 /* CLOSED */;
|
|
335
|
+
flush && this.flush();
|
|
336
|
+
return this.process();
|
|
333
337
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
338
|
+
}
|
|
339
|
+
isClosed() {
|
|
340
|
+
return this.state !== 0 /* OPEN */;
|
|
341
|
+
}
|
|
342
|
+
isReadable() {
|
|
343
|
+
return this.state !== 2 /* DONE */ && this.buf && this.buf.length > 0 || this.writes && this.writes.length > 0 || this.txbuf && this.txbuf.length > 0;
|
|
344
|
+
}
|
|
345
|
+
consume(fn = (x) => console.log(this.id, ":", x)) {
|
|
346
|
+
return (async () => {
|
|
347
|
+
let x;
|
|
348
|
+
while ((x = null, x = await this.read()) !== void 0) {
|
|
349
|
+
await fn(x);
|
|
350
|
+
}
|
|
351
|
+
})();
|
|
352
|
+
}
|
|
353
|
+
produce(fn, close = true) {
|
|
354
|
+
return (async () => {
|
|
355
|
+
while (!this.isClosed()) {
|
|
356
|
+
const val = await fn();
|
|
357
|
+
if (val === void 0) {
|
|
358
|
+
close && await this.close();
|
|
359
|
+
break;
|
|
339
360
|
}
|
|
361
|
+
await this.write(val);
|
|
362
|
+
}
|
|
363
|
+
})();
|
|
364
|
+
}
|
|
365
|
+
consumeWhileReadable(fn = (x) => console.log(this.id, ":", x)) {
|
|
366
|
+
return (async () => {
|
|
367
|
+
let x;
|
|
368
|
+
while (this.isReadable()) {
|
|
369
|
+
x = await this.read();
|
|
370
|
+
if (x === void 0) {
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
await fn(x);
|
|
374
|
+
x = null;
|
|
375
|
+
}
|
|
376
|
+
})();
|
|
377
|
+
}
|
|
378
|
+
reduce(rfn, acc) {
|
|
379
|
+
return (async () => {
|
|
380
|
+
const [init, complete, reduce] = rfn;
|
|
381
|
+
acc = acc != null ? acc : init();
|
|
382
|
+
let x;
|
|
383
|
+
while ((x = null, x = await this.read()) !== void 0) {
|
|
384
|
+
acc = reduce(acc, x);
|
|
385
|
+
if (isReduced(acc)) {
|
|
386
|
+
acc = acc.deref();
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return unreduced(complete(acc));
|
|
391
|
+
})();
|
|
392
|
+
}
|
|
393
|
+
transduce(tx, rfn, acc) {
|
|
394
|
+
return (async () => {
|
|
395
|
+
const _rfn = tx(rfn);
|
|
396
|
+
return unreduced(_rfn[1](await this.reduce(_rfn, acc)));
|
|
397
|
+
})();
|
|
398
|
+
}
|
|
399
|
+
into(src, close = true) {
|
|
400
|
+
return (async () => {
|
|
401
|
+
for (let x of src) {
|
|
402
|
+
if (this.isClosed()) {
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
await this.write(x);
|
|
406
|
+
}
|
|
407
|
+
close && await this.close();
|
|
408
|
+
})();
|
|
409
|
+
}
|
|
410
|
+
pipe(dest, close = true) {
|
|
411
|
+
if (!(dest instanceof Channel)) {
|
|
412
|
+
dest = new Channel(dest);
|
|
340
413
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
consume(fn = (x) => console.log(this.id, ":", x)) {
|
|
350
|
-
return (async () => {
|
|
351
|
-
let x;
|
|
352
|
-
while (((x = null), (x = await this.read())) !== undefined) {
|
|
353
|
-
await fn(x);
|
|
354
|
-
}
|
|
355
|
-
})();
|
|
414
|
+
this.consume((x) => dest.write(x)).then(() => {
|
|
415
|
+
close && dest.close();
|
|
416
|
+
});
|
|
417
|
+
return dest;
|
|
418
|
+
}
|
|
419
|
+
split(pred, truthy, falsey, close = true) {
|
|
420
|
+
if (!(truthy instanceof Channel)) {
|
|
421
|
+
truthy = new Channel();
|
|
356
422
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
while (!this.isClosed()) {
|
|
360
|
-
const val = await fn();
|
|
361
|
-
if (val === undefined) {
|
|
362
|
-
close && (await this.close());
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
await this.write(val);
|
|
366
|
-
}
|
|
367
|
-
})();
|
|
423
|
+
if (!(falsey instanceof Channel)) {
|
|
424
|
+
falsey = new Channel();
|
|
368
425
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
426
|
+
this.consume((x) => (pred(x) ? truthy : falsey).write(x)).then(
|
|
427
|
+
() => {
|
|
428
|
+
close && (truthy.close(), falsey.close());
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
return [truthy, falsey];
|
|
432
|
+
}
|
|
433
|
+
concat(chans, close = true) {
|
|
434
|
+
return (async () => {
|
|
435
|
+
for (let c of chans) {
|
|
436
|
+
await c.consume((x) => this.write(x));
|
|
437
|
+
}
|
|
438
|
+
close && await this.close();
|
|
439
|
+
})();
|
|
440
|
+
}
|
|
441
|
+
release() {
|
|
442
|
+
if (this.state === 1 /* CLOSED */) {
|
|
443
|
+
this.state = 2 /* DONE */;
|
|
444
|
+
this.flush();
|
|
445
|
+
this.buf.release();
|
|
446
|
+
delete this.reads;
|
|
447
|
+
delete this.writes;
|
|
448
|
+
delete this.buf;
|
|
449
|
+
delete this.txbuf;
|
|
450
|
+
delete this.tx;
|
|
451
|
+
delete this.isBusy;
|
|
452
|
+
delete this.onerror;
|
|
381
453
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
454
|
+
}
|
|
455
|
+
async process() {
|
|
456
|
+
if (!this.isBusy) {
|
|
457
|
+
this.isBusy = true;
|
|
458
|
+
const { buf, txbuf, reads, writes } = this;
|
|
459
|
+
let doProcess = true;
|
|
460
|
+
while (doProcess) {
|
|
461
|
+
while (reads.length && (txbuf.length || buf.length)) {
|
|
462
|
+
if (txbuf.length) {
|
|
463
|
+
const val = txbuf.drop();
|
|
464
|
+
if (val !== void 0) {
|
|
465
|
+
reads.drop()(val);
|
|
393
466
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return (async () => {
|
|
399
|
-
const _rfn = tx(rfn);
|
|
400
|
-
return unreduced(_rfn[1](await this.reduce(_rfn, acc)));
|
|
401
|
-
})();
|
|
402
|
-
}
|
|
403
|
-
into(src, close = true) {
|
|
404
|
-
return (async () => {
|
|
405
|
-
for (let x of src) {
|
|
406
|
-
if (this.isClosed()) {
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
await this.write(x);
|
|
467
|
+
} else {
|
|
468
|
+
const val = await buf.drop().value();
|
|
469
|
+
if (val !== void 0) {
|
|
470
|
+
reads.drop()(val);
|
|
410
471
|
}
|
|
411
|
-
|
|
412
|
-
})();
|
|
413
|
-
}
|
|
414
|
-
pipe(dest, close = true) {
|
|
415
|
-
if (!(dest instanceof Channel)) {
|
|
416
|
-
dest = new Channel(dest);
|
|
417
|
-
}
|
|
418
|
-
this.consume((x) => dest.write(x)) // return undefined here?
|
|
419
|
-
.then(() => {
|
|
420
|
-
close && dest.close();
|
|
421
|
-
});
|
|
422
|
-
return dest;
|
|
423
|
-
}
|
|
424
|
-
split(pred, truthy, falsey, close = true) {
|
|
425
|
-
if (!(truthy instanceof Channel)) {
|
|
426
|
-
truthy = new Channel();
|
|
472
|
+
}
|
|
427
473
|
}
|
|
428
|
-
|
|
429
|
-
|
|
474
|
+
while (writes.length && !buf.isFull()) {
|
|
475
|
+
const put = writes.drop();
|
|
476
|
+
buf.push(put);
|
|
477
|
+
put.resolve(true);
|
|
430
478
|
}
|
|
431
|
-
this.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
return (async () => {
|
|
438
|
-
for (let c of chans) {
|
|
439
|
-
await c.consume((x) => this.write(x));
|
|
479
|
+
if (this.state === 1 /* CLOSED */) {
|
|
480
|
+
if (this.tx && !writes.length) {
|
|
481
|
+
try {
|
|
482
|
+
this.tx[1](this.txbuf);
|
|
483
|
+
} catch (e) {
|
|
484
|
+
this.onerror(e, this);
|
|
440
485
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
this.state = State.DONE;
|
|
447
|
-
this.flush();
|
|
448
|
-
this.buf.release();
|
|
449
|
-
delete this.reads;
|
|
450
|
-
delete this.writes;
|
|
451
|
-
delete this.buf;
|
|
452
|
-
delete this.txbuf;
|
|
453
|
-
delete this.tx;
|
|
454
|
-
delete this.isBusy;
|
|
455
|
-
delete this.onerror;
|
|
486
|
+
}
|
|
487
|
+
if (!this.isReadable()) {
|
|
488
|
+
this.release();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
456
491
|
}
|
|
492
|
+
doProcess = reads.length && (txbuf.length || buf.length) || writes.length && !buf.isFull();
|
|
493
|
+
}
|
|
494
|
+
this.isBusy = false;
|
|
457
495
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
while (doProcess) {
|
|
464
|
-
while (reads.length && (txbuf.length || buf.length)) {
|
|
465
|
-
if (txbuf.length) {
|
|
466
|
-
const val = txbuf.drop();
|
|
467
|
-
if (val !== undefined) {
|
|
468
|
-
reads.drop()(val);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
const val = await buf.drop().value();
|
|
473
|
-
if (val !== undefined) {
|
|
474
|
-
reads.drop()(val);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
while (writes.length && !buf.isFull()) {
|
|
479
|
-
const put = writes.drop();
|
|
480
|
-
buf.push(put);
|
|
481
|
-
put.resolve(true);
|
|
482
|
-
}
|
|
483
|
-
if (this.state === State.CLOSED) {
|
|
484
|
-
if (this.tx && !writes.length) {
|
|
485
|
-
try {
|
|
486
|
-
// finalize/complete transducer
|
|
487
|
-
this.tx[1](this.txbuf);
|
|
488
|
-
}
|
|
489
|
-
catch (e) {
|
|
490
|
-
this.onerror(e, this);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
if (!this.isReadable()) {
|
|
494
|
-
this.release();
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
doProcess =
|
|
499
|
-
(reads.length && (txbuf.length || buf.length)) ||
|
|
500
|
-
(writes.length && !buf.isFull());
|
|
501
|
-
}
|
|
502
|
-
this.isBusy = false;
|
|
503
|
-
}
|
|
496
|
+
}
|
|
497
|
+
flush() {
|
|
498
|
+
let op;
|
|
499
|
+
while (op = this.reads.drop()) {
|
|
500
|
+
op();
|
|
504
501
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
while ((op = this.reads.drop())) {
|
|
508
|
-
op();
|
|
509
|
-
}
|
|
510
|
-
while ((op = this.writes.drop())) {
|
|
511
|
-
op.resolve(false);
|
|
512
|
-
}
|
|
513
|
-
this.buf.release();
|
|
502
|
+
while (op = this.writes.drop()) {
|
|
503
|
+
op.resolve(false);
|
|
514
504
|
}
|
|
505
|
+
this.buf.release();
|
|
506
|
+
}
|
|
515
507
|
}
|
|
516
|
-
const defaultErrorHandler = (e, chan, val) => console.log(
|
|
508
|
+
const defaultErrorHandler = (e, chan, val) => console.log(
|
|
509
|
+
chan.id,
|
|
510
|
+
"error occurred",
|
|
511
|
+
e.message,
|
|
512
|
+
val !== void 0 ? val : ""
|
|
513
|
+
);
|
|
517
514
|
const maybeBuffer = (x) => x instanceof FixedBuffer || typeof x === "number";
|
|
515
|
+
export {
|
|
516
|
+
Channel
|
|
517
|
+
};
|