@bytecodealliance/preview2-shim 0.22.0 → 0.24.0
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 +148 -7
- package/dist/browser/cli.d.ts +22 -1
- package/dist/browser/cli.js +66 -34
- package/dist/browser/clocks.js +24 -5
- package/dist/browser/common.d.ts +5 -0
- package/dist/browser/common.js +16 -0
- package/dist/browser/filesystem.d.ts +55 -97
- package/dist/browser/filesystem.js +106 -287
- package/dist/browser/http.d.ts +9 -0
- package/dist/browser/http.js +446 -136
- package/dist/browser/in-memory-filesystem.d.ts +22 -0
- package/dist/browser/in-memory-filesystem.js +530 -0
- package/dist/browser/in-memory-http.d.ts +8 -0
- package/dist/browser/in-memory-http.js +11 -0
- package/dist/browser/in-memory-sockets.d.ts +34 -0
- package/dist/browser/in-memory-sockets.js +294 -0
- package/dist/browser/io.d.ts +11 -2
- package/dist/browser/io.js +200 -39
- package/dist/browser/random.js +5 -3
- package/dist/browser/sockets.d.ts +2 -1
- package/dist/browser/sockets.js +75 -56
- package/dist/common/instantiation.d.ts +4 -1
- package/dist/common/instantiation.js +85 -49
- package/dist/io/worker-socket-tcp.js +14 -12
- package/dist/io/worker-socket-udp.js +3 -4
- package/dist/io/worker-sockets.d.ts +4 -4
- package/dist/io/worker-sockets.js +29 -9
- package/dist/io/worker-thread.bundle.js +28 -13
- package/dist/nodejs/filesystem.d.ts +7 -0
- package/dist/nodejs/filesystem.js +65 -22
- package/dist/nodejs/sockets.d.ts +13 -0
- package/dist/nodejs/sockets.js +20 -0
- package/package.json +1 -1
- package/types/instantiation.d.ts +64 -12
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { checkedU64AsNumber } from "./common.js";
|
|
2
|
+
import { inputStreamCreate, outputStreamCreate, pollableCreate } from "./io.js";
|
|
3
|
+
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
4
|
+
const unsupported = () => {
|
|
5
|
+
throw "not-supported";
|
|
6
|
+
};
|
|
7
|
+
function addressKey(address) {
|
|
8
|
+
return `${address.tag}:${address.val.address.join(".")}:${address.val.port}`;
|
|
9
|
+
}
|
|
10
|
+
function takeBytes(chunks, length) {
|
|
11
|
+
const chunk = chunks.shift();
|
|
12
|
+
if (!chunk) {
|
|
13
|
+
throw { tag: "closed" };
|
|
14
|
+
}
|
|
15
|
+
const requested = checkedU64AsNumber(length, "length");
|
|
16
|
+
if (chunk.byteLength <= requested) {
|
|
17
|
+
return chunk;
|
|
18
|
+
}
|
|
19
|
+
chunks.unshift(chunk.subarray(requested));
|
|
20
|
+
return chunk.subarray(0, requested);
|
|
21
|
+
}
|
|
22
|
+
/** The browser-side endpoint of an in-memory TCP connection. */
|
|
23
|
+
export class InMemoryTcpClient {
|
|
24
|
+
#toServer = [];
|
|
25
|
+
#fromServer = [];
|
|
26
|
+
write(bytes) {
|
|
27
|
+
this.#toServer.push(new Uint8Array(bytes));
|
|
28
|
+
}
|
|
29
|
+
read(length = 4096n) {
|
|
30
|
+
return takeBytes(this.#fromServer, length);
|
|
31
|
+
}
|
|
32
|
+
_serverStreams() {
|
|
33
|
+
return [
|
|
34
|
+
inputStreamCreate({ blockingRead: (length) => takeBytes(this.#toServer, length) }),
|
|
35
|
+
outputStreamCreate({
|
|
36
|
+
write: (bytes) => this.#fromServer.push(new Uint8Array(bytes)),
|
|
37
|
+
}),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Deterministic, process-local TCP namespaces with a browser-side client API. */
|
|
42
|
+
export class InMemoryTcpSockets {
|
|
43
|
+
#pending = new Map();
|
|
44
|
+
tcp;
|
|
45
|
+
tcpCreateSocket;
|
|
46
|
+
constructor() {
|
|
47
|
+
const pending = this.#pending;
|
|
48
|
+
class TcpSocket {
|
|
49
|
+
#family;
|
|
50
|
+
#localAddress;
|
|
51
|
+
#remoteAddress;
|
|
52
|
+
#listening = false;
|
|
53
|
+
constructor(family) {
|
|
54
|
+
this.#family = family;
|
|
55
|
+
}
|
|
56
|
+
startBind(_network, localAddress) {
|
|
57
|
+
this.#localAddress = localAddress;
|
|
58
|
+
}
|
|
59
|
+
finishBind() {
|
|
60
|
+
if (!this.#localAddress) {
|
|
61
|
+
throw "not-in-progress";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
startConnect = unsupported;
|
|
65
|
+
finishConnect = unsupported;
|
|
66
|
+
startListen() {
|
|
67
|
+
if (!this.#localAddress) {
|
|
68
|
+
throw "invalid-state";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finishListen() {
|
|
72
|
+
this.#listening = true;
|
|
73
|
+
}
|
|
74
|
+
accept() {
|
|
75
|
+
if (!this.#listening || !this.#localAddress) {
|
|
76
|
+
throw "invalid-state";
|
|
77
|
+
}
|
|
78
|
+
const client = pending.get(addressKey(this.#localAddress))?.shift();
|
|
79
|
+
if (!client) {
|
|
80
|
+
throw "would-block";
|
|
81
|
+
}
|
|
82
|
+
const connected = new TcpSocket(this.#family);
|
|
83
|
+
connected.#localAddress = this.#localAddress;
|
|
84
|
+
const [input, output] = client._serverStreams();
|
|
85
|
+
return [connected, input, output];
|
|
86
|
+
}
|
|
87
|
+
localAddress() {
|
|
88
|
+
if (!this.#localAddress) {
|
|
89
|
+
throw "invalid-state";
|
|
90
|
+
}
|
|
91
|
+
return this.#localAddress;
|
|
92
|
+
}
|
|
93
|
+
remoteAddress() {
|
|
94
|
+
if (!this.#remoteAddress) {
|
|
95
|
+
throw "invalid-state";
|
|
96
|
+
}
|
|
97
|
+
return this.#remoteAddress;
|
|
98
|
+
}
|
|
99
|
+
isListening() {
|
|
100
|
+
return this.#listening;
|
|
101
|
+
}
|
|
102
|
+
addressFamily() {
|
|
103
|
+
return this.#family;
|
|
104
|
+
}
|
|
105
|
+
setListenBacklogSize = unsupported;
|
|
106
|
+
keepAliveEnabled = unsupported;
|
|
107
|
+
setKeepAliveEnabled = unsupported;
|
|
108
|
+
keepAliveIdleTime = unsupported;
|
|
109
|
+
setKeepAliveIdleTime = unsupported;
|
|
110
|
+
keepAliveInterval = unsupported;
|
|
111
|
+
setKeepAliveInterval = unsupported;
|
|
112
|
+
keepAliveCount = unsupported;
|
|
113
|
+
setKeepAliveCount = unsupported;
|
|
114
|
+
hopLimit = unsupported;
|
|
115
|
+
setHopLimit = unsupported;
|
|
116
|
+
receiveBufferSize = unsupported;
|
|
117
|
+
setReceiveBufferSize = unsupported;
|
|
118
|
+
sendBufferSize = unsupported;
|
|
119
|
+
setSendBufferSize = unsupported;
|
|
120
|
+
subscribe() {
|
|
121
|
+
return pollableCreate();
|
|
122
|
+
}
|
|
123
|
+
shutdown() { }
|
|
124
|
+
[symbolDispose]() { }
|
|
125
|
+
}
|
|
126
|
+
this.tcp = { TcpSocket };
|
|
127
|
+
this.tcpCreateSocket = {
|
|
128
|
+
createTcpSocket: (family) => new TcpSocket(family),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
connect(serverAddress) {
|
|
132
|
+
const client = new InMemoryTcpClient();
|
|
133
|
+
const key = addressKey(serverAddress);
|
|
134
|
+
const clients = this.#pending.get(key);
|
|
135
|
+
if (clients) {
|
|
136
|
+
clients.push(client);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.#pending.set(key, [client]);
|
|
140
|
+
}
|
|
141
|
+
return client;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** The browser-side endpoint of an in-memory UDP socket. */
|
|
145
|
+
export class InMemoryUdpClient {
|
|
146
|
+
#send;
|
|
147
|
+
#received = [];
|
|
148
|
+
constructor(send) {
|
|
149
|
+
this.#send = send;
|
|
150
|
+
}
|
|
151
|
+
send(bytes, serverAddress) {
|
|
152
|
+
this.#send(bytes, serverAddress);
|
|
153
|
+
}
|
|
154
|
+
read() {
|
|
155
|
+
return takeBytes(this.#received, 65535n);
|
|
156
|
+
}
|
|
157
|
+
_receive(bytes) {
|
|
158
|
+
this.#received.push(new Uint8Array(bytes));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Deterministic, process-local UDP namespaces with a browser-side client API. */
|
|
162
|
+
export class InMemoryUdpSockets {
|
|
163
|
+
#datagrams = new Map();
|
|
164
|
+
#clients = new Map();
|
|
165
|
+
udp;
|
|
166
|
+
udpCreateSocket;
|
|
167
|
+
constructor() {
|
|
168
|
+
const datagrams = this.#datagrams;
|
|
169
|
+
const clients = this.#clients;
|
|
170
|
+
class IncomingDatagramStream {
|
|
171
|
+
queue;
|
|
172
|
+
constructor(queue) {
|
|
173
|
+
this.queue = queue;
|
|
174
|
+
}
|
|
175
|
+
receive(maxResults) {
|
|
176
|
+
return this.queue.splice(0, checkedU64AsNumber(maxResults, "max results"));
|
|
177
|
+
}
|
|
178
|
+
subscribe() {
|
|
179
|
+
return pollableCreate();
|
|
180
|
+
}
|
|
181
|
+
[symbolDispose]() { }
|
|
182
|
+
}
|
|
183
|
+
class OutgoingDatagramStream {
|
|
184
|
+
remoteAddress;
|
|
185
|
+
#permit = 0;
|
|
186
|
+
constructor(remoteAddress) {
|
|
187
|
+
this.remoteAddress = remoteAddress;
|
|
188
|
+
}
|
|
189
|
+
checkSend() {
|
|
190
|
+
this.#permit = 1_024;
|
|
191
|
+
return 1024n;
|
|
192
|
+
}
|
|
193
|
+
send(outgoing) {
|
|
194
|
+
if (outgoing.length > this.#permit) {
|
|
195
|
+
throw new TypeError("datagram count exceeds the permit returned by checkSend");
|
|
196
|
+
}
|
|
197
|
+
this.#permit -= outgoing.length;
|
|
198
|
+
for (const datagram of outgoing) {
|
|
199
|
+
const destination = datagram.remoteAddress ?? this.remoteAddress;
|
|
200
|
+
if (!destination) {
|
|
201
|
+
throw "invalid-argument";
|
|
202
|
+
}
|
|
203
|
+
const client = clients.get(addressKey(destination));
|
|
204
|
+
if (!client) {
|
|
205
|
+
throw "remote-unreachable";
|
|
206
|
+
}
|
|
207
|
+
client._receive(datagram.data);
|
|
208
|
+
}
|
|
209
|
+
return BigInt(outgoing.length);
|
|
210
|
+
}
|
|
211
|
+
subscribe() {
|
|
212
|
+
return pollableCreate();
|
|
213
|
+
}
|
|
214
|
+
[symbolDispose]() { }
|
|
215
|
+
}
|
|
216
|
+
class UdpSocket {
|
|
217
|
+
#family;
|
|
218
|
+
#localAddress;
|
|
219
|
+
#remoteAddress;
|
|
220
|
+
constructor(family) {
|
|
221
|
+
this.#family = family;
|
|
222
|
+
}
|
|
223
|
+
startBind(_network, localAddress) {
|
|
224
|
+
this.#localAddress = localAddress;
|
|
225
|
+
}
|
|
226
|
+
finishBind() {
|
|
227
|
+
if (!this.#localAddress) {
|
|
228
|
+
throw "not-in-progress";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
stream(remoteAddress) {
|
|
232
|
+
if (!this.#localAddress) {
|
|
233
|
+
throw "invalid-state";
|
|
234
|
+
}
|
|
235
|
+
this.#remoteAddress = remoteAddress;
|
|
236
|
+
const key = addressKey(this.#localAddress);
|
|
237
|
+
let queue = datagrams.get(key);
|
|
238
|
+
if (!queue) {
|
|
239
|
+
queue = [];
|
|
240
|
+
datagrams.set(key, queue);
|
|
241
|
+
}
|
|
242
|
+
return [
|
|
243
|
+
new IncomingDatagramStream(queue),
|
|
244
|
+
new OutgoingDatagramStream(remoteAddress),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
localAddress() {
|
|
248
|
+
if (!this.#localAddress) {
|
|
249
|
+
throw "invalid-state";
|
|
250
|
+
}
|
|
251
|
+
return this.#localAddress;
|
|
252
|
+
}
|
|
253
|
+
remoteAddress() {
|
|
254
|
+
return this.#remoteAddress;
|
|
255
|
+
}
|
|
256
|
+
addressFamily() {
|
|
257
|
+
return this.#family;
|
|
258
|
+
}
|
|
259
|
+
unicastHopLimit = unsupported;
|
|
260
|
+
setUnicastHopLimit = unsupported;
|
|
261
|
+
receiveBufferSize = unsupported;
|
|
262
|
+
setReceiveBufferSize = unsupported;
|
|
263
|
+
sendBufferSize = unsupported;
|
|
264
|
+
setSendBufferSize = unsupported;
|
|
265
|
+
subscribe() {
|
|
266
|
+
return pollableCreate();
|
|
267
|
+
}
|
|
268
|
+
[symbolDispose]() { }
|
|
269
|
+
}
|
|
270
|
+
this.udp = {
|
|
271
|
+
IncomingDatagramStream,
|
|
272
|
+
OutgoingDatagramStream,
|
|
273
|
+
UdpSocket,
|
|
274
|
+
};
|
|
275
|
+
this.udpCreateSocket = {
|
|
276
|
+
createUdpSocket: (family) => new UdpSocket(family),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
createClient(localAddress) {
|
|
280
|
+
const client = new InMemoryUdpClient((bytes, serverAddress) => {
|
|
281
|
+
const key = addressKey(serverAddress);
|
|
282
|
+
const queue = this.#datagrams.get(key);
|
|
283
|
+
const incoming = { data: new Uint8Array(bytes), remoteAddress: localAddress };
|
|
284
|
+
if (queue) {
|
|
285
|
+
queue.push(incoming);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
this.#datagrams.set(key, [incoming]);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.#clients.set(addressKey(localAddress), client);
|
|
292
|
+
return client;
|
|
293
|
+
}
|
|
294
|
+
}
|
package/dist/browser/io.d.ts
CHANGED
|
@@ -8,13 +8,19 @@ type IOutputStream = StreamsNamespace.OutputStream;
|
|
|
8
8
|
export type InputStreamHandler = Partial<IInputStream> & Required<Pick<IInputStream, "blockingRead">> & {
|
|
9
9
|
drop?: () => void;
|
|
10
10
|
};
|
|
11
|
+
export interface PollableSource {
|
|
12
|
+
ready(): boolean;
|
|
13
|
+
wait(): Promise<void>;
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Handler interface for creating custom output streams
|
|
13
17
|
*/
|
|
14
18
|
export type OutputStreamHandler = Partial<IOutputStream> & Required<Pick<IOutputStream, "write">> & {
|
|
15
19
|
drop?: () => void;
|
|
16
20
|
};
|
|
21
|
+
export declare const ioErrorCreate: (message: string) => ErrorNamespace.Error;
|
|
17
22
|
declare class InputStream implements IInputStream {
|
|
23
|
+
#private;
|
|
18
24
|
[symbolDispose]: () => void;
|
|
19
25
|
id: number;
|
|
20
26
|
handler: InputStreamHandler;
|
|
@@ -27,6 +33,7 @@ declare class InputStream implements IInputStream {
|
|
|
27
33
|
}
|
|
28
34
|
export declare const inputStreamCreate: typeof InputStream._create;
|
|
29
35
|
declare class OutputStream implements IOutputStream {
|
|
36
|
+
#private;
|
|
30
37
|
[symbolDispose]: () => void;
|
|
31
38
|
id: number;
|
|
32
39
|
open: boolean;
|
|
@@ -40,7 +47,7 @@ declare class OutputStream implements IOutputStream {
|
|
|
40
47
|
writeZeroes(len: bigint): void;
|
|
41
48
|
blockingWriteZeroesAndFlush(len: bigint): void;
|
|
42
49
|
splice(src: InputStream, len: bigint): bigint;
|
|
43
|
-
blockingSplice(
|
|
50
|
+
blockingSplice(src: InputStream, len: bigint): bigint;
|
|
44
51
|
subscribe(): PollNamespace.Pollable | Pollable;
|
|
45
52
|
}
|
|
46
53
|
export declare const outputStreamCreate: typeof OutputStream._create;
|
|
@@ -49,9 +56,11 @@ export declare const streams: typeof StreamsNamespace;
|
|
|
49
56
|
declare class Pollable implements PollNamespace.Pollable {
|
|
50
57
|
#private;
|
|
51
58
|
[symbolDispose]: () => void;
|
|
52
|
-
static _create(
|
|
59
|
+
static _create(source?: Promise<void> | PollableSource): Pollable;
|
|
53
60
|
ready(): boolean;
|
|
54
61
|
block(): Promise<void>;
|
|
62
|
+
_onDispose(callback: () => void): void;
|
|
63
|
+
_invalidate(): void;
|
|
55
64
|
}
|
|
56
65
|
export declare const pollableCreate: typeof Pollable._create;
|
|
57
66
|
export declare const poll: typeof PollNamespace;
|
package/dist/browser/io.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import { checkedU64AsNumber } from "./common.js";
|
|
1
2
|
let id = 0;
|
|
2
3
|
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
4
|
+
const checkedLength = (len, name = "length") => checkedU64AsNumber(len, name);
|
|
5
|
+
function closed() {
|
|
6
|
+
throw { tag: "closed" };
|
|
7
|
+
}
|
|
3
8
|
class IoError extends Error {
|
|
4
9
|
toDebugString() {
|
|
5
10
|
return this.message;
|
|
6
11
|
}
|
|
7
12
|
}
|
|
13
|
+
export const ioErrorCreate = (message) => new IoError(message);
|
|
8
14
|
class InputStream {
|
|
9
15
|
id;
|
|
10
16
|
handler;
|
|
17
|
+
#open = true;
|
|
18
|
+
#children = new Set();
|
|
11
19
|
static _create(handler) {
|
|
12
20
|
const stream = new InputStream();
|
|
13
21
|
if (!handler) {
|
|
@@ -18,15 +26,27 @@ class InputStream {
|
|
|
18
26
|
return stream;
|
|
19
27
|
}
|
|
20
28
|
read(len) {
|
|
29
|
+
checkedLength(len);
|
|
30
|
+
if (!this.#open) {
|
|
31
|
+
closed();
|
|
32
|
+
}
|
|
21
33
|
if (this.handler.read) {
|
|
22
|
-
return this.handler.read(len);
|
|
34
|
+
return this.handler.read.call(this, len);
|
|
23
35
|
}
|
|
24
36
|
return this.handler.blockingRead.call(this, len);
|
|
25
37
|
}
|
|
26
38
|
blockingRead(len) {
|
|
39
|
+
checkedLength(len);
|
|
40
|
+
if (!this.#open) {
|
|
41
|
+
closed();
|
|
42
|
+
}
|
|
27
43
|
return this.handler.blockingRead.call(this, len);
|
|
28
44
|
}
|
|
29
45
|
skip(len) {
|
|
46
|
+
checkedLength(len);
|
|
47
|
+
if (!this.#open) {
|
|
48
|
+
closed();
|
|
49
|
+
}
|
|
30
50
|
if (this.handler.skip) {
|
|
31
51
|
return this.handler.skip.call(this, len);
|
|
32
52
|
}
|
|
@@ -37,6 +57,10 @@ class InputStream {
|
|
|
37
57
|
return this.blockingSkip.call(this, len);
|
|
38
58
|
}
|
|
39
59
|
blockingSkip(len) {
|
|
60
|
+
checkedLength(len);
|
|
61
|
+
if (!this.#open) {
|
|
62
|
+
closed();
|
|
63
|
+
}
|
|
40
64
|
if (this.handler.blockingSkip) {
|
|
41
65
|
return this.handler.blockingSkip.call(this, len);
|
|
42
66
|
}
|
|
@@ -44,12 +68,27 @@ class InputStream {
|
|
|
44
68
|
return BigInt(bytes.byteLength);
|
|
45
69
|
}
|
|
46
70
|
subscribe() {
|
|
47
|
-
if (this
|
|
48
|
-
return
|
|
71
|
+
if (!this.#open) {
|
|
72
|
+
return pollableCreate();
|
|
73
|
+
}
|
|
74
|
+
const pollable = this.handler.subscribe
|
|
75
|
+
? this.handler.subscribe.call(this)
|
|
76
|
+
: pollableCreate();
|
|
77
|
+
if (pollable instanceof Pollable) {
|
|
78
|
+
this.#children.add(pollable);
|
|
79
|
+
pollable._onDispose(() => this.#children.delete(pollable));
|
|
49
80
|
}
|
|
50
|
-
return
|
|
81
|
+
return pollable;
|
|
51
82
|
}
|
|
52
83
|
[symbolDispose]() {
|
|
84
|
+
if (!this.#open) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.#open = false;
|
|
88
|
+
for (const child of this.#children) {
|
|
89
|
+
child._invalidate();
|
|
90
|
+
}
|
|
91
|
+
this.#children.clear();
|
|
53
92
|
if (this.handler.drop) {
|
|
54
93
|
this.handler.drop.call(this);
|
|
55
94
|
}
|
|
@@ -62,6 +101,8 @@ class OutputStream {
|
|
|
62
101
|
id;
|
|
63
102
|
open;
|
|
64
103
|
handler;
|
|
104
|
+
#permit = 0n;
|
|
105
|
+
#children = new Set();
|
|
65
106
|
static _create(handler) {
|
|
66
107
|
const stream = new OutputStream();
|
|
67
108
|
if (!handler) {
|
|
@@ -74,56 +115,116 @@ class OutputStream {
|
|
|
74
115
|
}
|
|
75
116
|
checkWrite() {
|
|
76
117
|
if (!this.open) {
|
|
77
|
-
|
|
118
|
+
closed();
|
|
78
119
|
}
|
|
79
120
|
if (this.handler.checkWrite) {
|
|
80
|
-
|
|
121
|
+
const permit = this.handler.checkWrite.call(this);
|
|
122
|
+
checkedLength(permit, "write permit");
|
|
123
|
+
this.#permit = permit;
|
|
124
|
+
return permit;
|
|
81
125
|
}
|
|
82
|
-
|
|
126
|
+
this.#permit = 1000000n;
|
|
127
|
+
return this.#permit;
|
|
83
128
|
}
|
|
84
129
|
write(buf) {
|
|
130
|
+
if (!this.open) {
|
|
131
|
+
closed();
|
|
132
|
+
}
|
|
133
|
+
if (BigInt(buf.byteLength) > this.#permit) {
|
|
134
|
+
throw new Error("write exceeds the permit returned by checkWrite");
|
|
135
|
+
}
|
|
136
|
+
this.#permit -= BigInt(buf.byteLength);
|
|
85
137
|
this.handler.write.call(this, buf);
|
|
86
138
|
}
|
|
87
139
|
blockingWriteAndFlush(buf) {
|
|
140
|
+
if (!this.open) {
|
|
141
|
+
closed();
|
|
142
|
+
}
|
|
143
|
+
if (buf.byteLength > 4096) {
|
|
144
|
+
throw new RangeError("blockingWriteAndFlush accepts at most 4096 bytes");
|
|
145
|
+
}
|
|
88
146
|
if (this.handler.blockingWriteAndFlush) {
|
|
89
147
|
return this.handler.blockingWriteAndFlush.call(this, buf);
|
|
90
148
|
}
|
|
91
149
|
this.handler.write.call(this, buf);
|
|
150
|
+
if (this.handler.blockingFlush) {
|
|
151
|
+
this.handler.blockingFlush.call(this);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.handler.flush?.call(this);
|
|
155
|
+
}
|
|
92
156
|
}
|
|
93
157
|
flush() {
|
|
158
|
+
if (!this.open) {
|
|
159
|
+
closed();
|
|
160
|
+
}
|
|
161
|
+
this.#permit = 0n;
|
|
94
162
|
if (this.handler.flush) {
|
|
95
163
|
this.handler.flush.call(this);
|
|
96
164
|
}
|
|
97
165
|
}
|
|
98
166
|
blockingFlush() {
|
|
99
|
-
this.open
|
|
167
|
+
if (!this.open) {
|
|
168
|
+
closed();
|
|
169
|
+
}
|
|
100
170
|
if (this.handler.blockingFlush) {
|
|
101
171
|
this.handler.blockingFlush.call(this);
|
|
102
172
|
}
|
|
173
|
+
else {
|
|
174
|
+
this.handler.flush?.call(this);
|
|
175
|
+
}
|
|
103
176
|
}
|
|
104
177
|
writeZeroes(len) {
|
|
105
|
-
|
|
178
|
+
const length = checkedLength(len);
|
|
179
|
+
if (len > this.#permit) {
|
|
180
|
+
throw new Error("write exceeds the permit returned by checkWrite");
|
|
181
|
+
}
|
|
182
|
+
this.write.call(this, new Uint8Array(length));
|
|
106
183
|
}
|
|
107
184
|
blockingWriteZeroesAndFlush(len) {
|
|
108
|
-
|
|
185
|
+
const length = checkedLength(len);
|
|
186
|
+
if (length > 4096) {
|
|
187
|
+
throw new RangeError("blockingWriteZeroesAndFlush accepts at most 4096 bytes");
|
|
188
|
+
}
|
|
189
|
+
this.blockingWriteAndFlush.call(this, new Uint8Array(length));
|
|
109
190
|
}
|
|
110
191
|
splice(src, len) {
|
|
111
|
-
const spliceLen = Math.min(
|
|
192
|
+
const spliceLen = Math.min(checkedLength(len), Number(this.checkWrite.call(this)));
|
|
112
193
|
const bytes = src.read(BigInt(spliceLen));
|
|
113
194
|
this.write.call(this, bytes);
|
|
114
195
|
return BigInt(bytes.byteLength);
|
|
115
196
|
}
|
|
116
|
-
blockingSplice(
|
|
117
|
-
|
|
118
|
-
|
|
197
|
+
blockingSplice(src, len) {
|
|
198
|
+
const spliceLen = Math.min(checkedLength(len), Number(this.checkWrite.call(this)));
|
|
199
|
+
const bytes = src.blockingRead(BigInt(spliceLen));
|
|
200
|
+
this.write.call(this, bytes);
|
|
201
|
+
return BigInt(bytes.byteLength);
|
|
119
202
|
}
|
|
120
203
|
subscribe() {
|
|
121
|
-
if (this.
|
|
122
|
-
return
|
|
204
|
+
if (!this.open) {
|
|
205
|
+
return pollableCreate();
|
|
206
|
+
}
|
|
207
|
+
const pollable = this.handler.subscribe
|
|
208
|
+
? this.handler.subscribe.call(this)
|
|
209
|
+
: pollableCreate();
|
|
210
|
+
if (pollable instanceof Pollable) {
|
|
211
|
+
this.#children.add(pollable);
|
|
212
|
+
pollable._onDispose(() => this.#children.delete(pollable));
|
|
213
|
+
}
|
|
214
|
+
return pollable;
|
|
215
|
+
}
|
|
216
|
+
[symbolDispose]() {
|
|
217
|
+
if (!this.open) {
|
|
218
|
+
return;
|
|
123
219
|
}
|
|
124
|
-
|
|
220
|
+
this.open = false;
|
|
221
|
+
this.#permit = 0n;
|
|
222
|
+
for (const child of this.#children) {
|
|
223
|
+
child._invalidate();
|
|
224
|
+
}
|
|
225
|
+
this.#children.clear();
|
|
226
|
+
this.handler.drop?.call(this);
|
|
125
227
|
}
|
|
126
|
-
[symbolDispose]() { }
|
|
127
228
|
}
|
|
128
229
|
export const outputStreamCreate = OutputStream._create;
|
|
129
230
|
// @ts-expect-error - Deleting static method
|
|
@@ -133,33 +234,82 @@ export const error = {
|
|
|
133
234
|
};
|
|
134
235
|
export const streams = { InputStream, OutputStream };
|
|
135
236
|
class Pollable {
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
|
|
237
|
+
#source = { ready: () => true, wait: () => Promise.resolve() };
|
|
238
|
+
#invalid = false;
|
|
239
|
+
#disposed = false;
|
|
240
|
+
#wait = null;
|
|
241
|
+
#disposeCallbacks = [];
|
|
242
|
+
#wakeUnusable;
|
|
243
|
+
#unusable = new Promise((resolve) => (this.#wakeUnusable = resolve));
|
|
244
|
+
static _create(source) {
|
|
139
245
|
const pollable = new Pollable();
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
pollable.#promise = promise.then(() => {
|
|
145
|
-
pollable.#ready = true;
|
|
246
|
+
if (source instanceof Promise) {
|
|
247
|
+
let ready = false;
|
|
248
|
+
const wait = source.then(() => {
|
|
249
|
+
ready = true;
|
|
146
250
|
}, () => {
|
|
147
|
-
|
|
251
|
+
ready = true;
|
|
148
252
|
});
|
|
253
|
+
pollable.#source = { ready: () => ready, wait: () => wait };
|
|
254
|
+
}
|
|
255
|
+
else if (source) {
|
|
256
|
+
pollable.#source = source;
|
|
149
257
|
}
|
|
150
258
|
return pollable;
|
|
151
259
|
}
|
|
152
260
|
ready() {
|
|
153
|
-
|
|
261
|
+
this.#assertUsable();
|
|
262
|
+
return this.#source.ready();
|
|
154
263
|
}
|
|
155
264
|
block() {
|
|
156
|
-
|
|
265
|
+
this.#assertUsable();
|
|
266
|
+
if (this.#source.ready()) {
|
|
157
267
|
return Promise.resolve();
|
|
158
268
|
}
|
|
159
|
-
|
|
269
|
+
// Deduplicate simultaneous waiters, but discard a completed wait so a
|
|
270
|
+
// level-triggered source can be polled again after its event is consumed.
|
|
271
|
+
if (!this.#wait) {
|
|
272
|
+
this.#wait = Promise.race([
|
|
273
|
+
Promise.resolve(this.#source.wait()),
|
|
274
|
+
this.#unusable.then(() => this.#assertUsable()),
|
|
275
|
+
]).finally(() => {
|
|
276
|
+
this.#wait = null;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return this.#wait;
|
|
280
|
+
}
|
|
281
|
+
_onDispose(callback) {
|
|
282
|
+
if (this.#disposed) {
|
|
283
|
+
callback();
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
this.#disposeCallbacks.push(callback);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
_invalidate() {
|
|
290
|
+
if (this.#invalid || this.#disposed) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
this.#invalid = true;
|
|
294
|
+
this.#wakeUnusable();
|
|
295
|
+
}
|
|
296
|
+
#assertUsable() {
|
|
297
|
+
if (this.#disposed) {
|
|
298
|
+
throw new Error("pollable has been disposed");
|
|
299
|
+
}
|
|
300
|
+
if (this.#invalid) {
|
|
301
|
+
throw new Error("pollable's parent resource has been disposed");
|
|
302
|
+
}
|
|
160
303
|
}
|
|
161
304
|
[symbolDispose]() {
|
|
162
|
-
this.#
|
|
305
|
+
if (this.#disposed) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this.#disposed = true;
|
|
309
|
+
this.#wakeUnusable();
|
|
310
|
+
for (const callback of this.#disposeCallbacks.splice(0)) {
|
|
311
|
+
callback();
|
|
312
|
+
}
|
|
163
313
|
}
|
|
164
314
|
}
|
|
165
315
|
export const pollableCreate = Pollable._create;
|
|
@@ -179,19 +329,30 @@ function pollList(list) {
|
|
|
179
329
|
}
|
|
180
330
|
}
|
|
181
331
|
if (ready.length > 0) {
|
|
182
|
-
|
|
332
|
+
// Browser guests commonly use an immediately-ready timer alongside an
|
|
333
|
+
// asynchronous Web API pollable. Yield a host task so Fetch, timers, and
|
|
334
|
+
// other event sources can progress instead of starving in a sync loop.
|
|
335
|
+
return new Promise((resolve) => setTimeout(() => {
|
|
336
|
+
const result = [];
|
|
337
|
+
for (let i = 0; i < list.length; i++) {
|
|
338
|
+
if (list[i].ready()) {
|
|
339
|
+
result.push(i);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
resolve(new Uint32Array(result));
|
|
343
|
+
}, 0));
|
|
183
344
|
}
|
|
184
345
|
// None ready synchronously. Wait for the first to resolve via Promise.race,
|
|
185
346
|
// then sweep for any others that became ready concurrently.
|
|
186
|
-
return Promise.race(list.map((
|
|
187
|
-
const result = [
|
|
188
|
-
for (let
|
|
189
|
-
if (
|
|
190
|
-
result.push(
|
|
347
|
+
return Promise.race(list.map((pollable) => pollable.block())).then(() => {
|
|
348
|
+
const result = [];
|
|
349
|
+
for (let i = 0; i < list.length; i++) {
|
|
350
|
+
if (list[i].ready()) {
|
|
351
|
+
result.push(i);
|
|
191
352
|
}
|
|
192
353
|
}
|
|
193
354
|
return new Uint32Array(result);
|
|
194
|
-
})
|
|
355
|
+
});
|
|
195
356
|
}
|
|
196
357
|
function pollOne(poll) {
|
|
197
358
|
return poll.block();
|