@zerodds/wire 0.0.0-stage → 1.0.0-rc.7
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 +13 -2
- package/dist/branded.d.ts +98 -0
- package/dist/branded.js +64 -0
- package/dist/dds_any.d.ts +47 -0
- package/dist/dds_any.js +41 -0
- package/dist/endianness.d.ts +22 -0
- package/dist/endianness.js +35 -0
- package/dist/equal.d.ts +46 -0
- package/dist/equal.js +273 -0
- package/dist/error.d.ts +52 -0
- package/dist/error.js +78 -0
- package/dist/float128.d.ts +15 -0
- package/dist/float128.js +181 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +33 -0
- package/dist/operations.d.ts +180 -0
- package/dist/operations.js +74 -0
- package/dist/policy.d.ts +133 -0
- package/dist/policy.js +184 -0
- package/dist/reader.d.ts +605 -0
- package/dist/reader.js +1345 -0
- package/dist/registry.d.ts +84 -0
- package/dist/registry.js +166 -0
- package/dist/test_backend.d.ts +9 -0
- package/dist/test_backend.js +173 -0
- package/dist/types.d.ts +246 -0
- package/dist/types.js +3 -0
- package/dist/wasm.d.ts +203 -0
- package/dist/wasm.js +194 -0
- package/dist/writer.d.ts +662 -0
- package/dist/writer.js +1333 -0
- package/package.json +27 -4
package/dist/wasm.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WASM-Bindings Profile: handles, samples, and the layer that guards
|
|
3
|
+
* them.
|
|
4
|
+
*
|
|
5
|
+
* DDS-TS 1.0 Annex C. A TypeScript application talks to a DDS
|
|
6
|
+
* implementation compiled to WebAssembly through the operations of
|
|
7
|
+
* §C.2 — create a participant, a topic, a writer, a reader; write XCDR2
|
|
8
|
+
* octets; take them back. This file declares those operations, the
|
|
9
|
+
* handle and sample types they use, and {@link bindWasmBackend}, which
|
|
10
|
+
* wraps a raw module export table in the run-time invariants Annex C
|
|
11
|
+
* requires and the type system cannot state.
|
|
12
|
+
*
|
|
13
|
+
* # What crosses the boundary
|
|
14
|
+
*
|
|
15
|
+
* `Uint8Array`, always. §C.3.1 makes the XCDR2 octet sequence the
|
|
16
|
+
* canonical wire form and forbids exposing a JavaScript object graph as
|
|
17
|
+
* one: a binding that passed `{ x: 1 }` across and serialised on the far
|
|
18
|
+
* side would have two serialisers, and the one on the JavaScript side
|
|
19
|
+
* would be the one no other vendor implements. The generated codec turns
|
|
20
|
+
* a sample into octets before it gets here.
|
|
21
|
+
*
|
|
22
|
+
* A boxed `any` is no exception (§C.3.3). `boxAny` on the producing
|
|
23
|
+
* side, the generated serialiser, the octets, the generated
|
|
24
|
+
* deserialiser, `unboxAny` on the consuming side — the box is a
|
|
25
|
+
* TypeScript-side narrowing device and never a wire object of its own.
|
|
26
|
+
*
|
|
27
|
+
* # Why the brands are string literals
|
|
28
|
+
*
|
|
29
|
+
* The same reason as `./branded.js` gives for `Char`: a
|
|
30
|
+
* `unique symbol` brand is nominal across module boundaries, so a
|
|
31
|
+
* handle from vendor A's binding would not satisfy vendor B's parameter
|
|
32
|
+
* even though both are indices into the same resource table. The tags
|
|
33
|
+
* below — `"participant"`, `"topic"`, `"publisher"`, `"subscriber"`,
|
|
34
|
+
* `"writer"`, `"reader"` — and the property name `__dds_brand` are
|
|
35
|
+
* normative (§C.1.1).
|
|
36
|
+
*
|
|
37
|
+
* The brand is compile-time only. `0 as DataReaderHandle` type-checks,
|
|
38
|
+
* and 0 is the invalid-handle sentinel, so the check that matters is the
|
|
39
|
+
* run-time one in {@link bindWasmBackend}.
|
|
40
|
+
*/
|
|
41
|
+
/** A domain participant (§C.1.1). */
|
|
42
|
+
export type ParticipantHandle = number & {
|
|
43
|
+
readonly __dds_brand: "participant";
|
|
44
|
+
};
|
|
45
|
+
/** A topic (§C.1.1). */
|
|
46
|
+
export type TopicHandle = number & {
|
|
47
|
+
readonly __dds_brand: "topic";
|
|
48
|
+
};
|
|
49
|
+
/** A publisher (§C.1.1). */
|
|
50
|
+
export type PublisherHandle = number & {
|
|
51
|
+
readonly __dds_brand: "publisher";
|
|
52
|
+
};
|
|
53
|
+
/** A subscriber (§C.1.1). */
|
|
54
|
+
export type SubscriberHandle = number & {
|
|
55
|
+
readonly __dds_brand: "subscriber";
|
|
56
|
+
};
|
|
57
|
+
/** A data writer (§C.1.1). */
|
|
58
|
+
export type DataWriterHandle = number & {
|
|
59
|
+
readonly __dds_brand: "writer";
|
|
60
|
+
};
|
|
61
|
+
/** A data reader (§C.1.1). */
|
|
62
|
+
export type DataReaderHandle = number & {
|
|
63
|
+
readonly __dds_brand: "reader";
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* An OMG DDSI-RTPS GUID: a 12-octet `GuidPrefix` and a 32-bit
|
|
67
|
+
* `EntityId`.
|
|
68
|
+
*
|
|
69
|
+
* DDS-TS 1.0 §C.1.2. The whole sixteen octets and not a 64-bit subset:
|
|
70
|
+
* the prefix carries the vendor id, the host id, the application id and
|
|
71
|
+
* the instance id (DDSI-RTPS 2.5 §8.2.4), and truncating it loses the
|
|
72
|
+
* vendor and host components — which is exactly what tells two writers
|
|
73
|
+
* apart in a multi-vendor or multi-host deployment.
|
|
74
|
+
*/
|
|
75
|
+
export interface DdsGuid {
|
|
76
|
+
/** Twelve octets. */
|
|
77
|
+
readonly prefix: Uint8Array;
|
|
78
|
+
/** The entity id, as a 32-bit unsigned value. */
|
|
79
|
+
readonly entityId: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A GUID from its two parts, or a `RangeError`.
|
|
83
|
+
*
|
|
84
|
+
* DDS-TS 1.0 §C.1.2.
|
|
85
|
+
*/
|
|
86
|
+
export declare function makeDdsGuid(prefix: Uint8Array, entityId: number): DdsGuid;
|
|
87
|
+
/**
|
|
88
|
+
* What DDS knows about a sample besides its octets.
|
|
89
|
+
*
|
|
90
|
+
* DDS-TS 1.0 §C.1.2. A strict subset of the OMG DDS 1.4 `SampleInfo`
|
|
91
|
+
* (§2.2.2.5.4): the fields this Profile exposes and no others. A
|
|
92
|
+
* `validData` of false is a sample that carries only an instance state
|
|
93
|
+
* change — a dispose, a writer leaving — and whose `bytes` say nothing.
|
|
94
|
+
*/
|
|
95
|
+
export interface SampleInfo {
|
|
96
|
+
readonly validData: boolean;
|
|
97
|
+
readonly sampleState: "read" | "not_read";
|
|
98
|
+
readonly viewState: "new" | "not_new";
|
|
99
|
+
readonly instanceState: "alive" | "not_alive_disposed" | "not_alive_no_writers";
|
|
100
|
+
readonly sourceTimestampNs: bigint;
|
|
101
|
+
readonly sequenceNumber: bigint;
|
|
102
|
+
/** The full RTPS GUID of the writer that published it. */
|
|
103
|
+
readonly publicationHandle: DdsGuid;
|
|
104
|
+
/** Opaque, and 64 bits wide. */
|
|
105
|
+
readonly instanceHandle: bigint;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* One sample: the XCDR2 octets exactly as they crossed the wire, and
|
|
109
|
+
* what DDS knows about them.
|
|
110
|
+
*
|
|
111
|
+
* DDS-TS 1.0 §C.1.2.
|
|
112
|
+
*/
|
|
113
|
+
export interface Sample {
|
|
114
|
+
readonly bytes: Uint8Array;
|
|
115
|
+
readonly info: SampleInfo;
|
|
116
|
+
}
|
|
117
|
+
/** Called when a reader has data (§C.1.3). */
|
|
118
|
+
export type DataAvailableCallback = (r: DataReaderHandle) => void;
|
|
119
|
+
/**
|
|
120
|
+
* The raw module surface — a `.wasm` export table, an N-API `.node`
|
|
121
|
+
* module, or a reference implementation in TypeScript.
|
|
122
|
+
*
|
|
123
|
+
* Handles are plain numbers here: branding is the binding layer's, and
|
|
124
|
+
* a WebAssembly export cannot carry a TypeScript type anyway. Everything
|
|
125
|
+
* that Annex C requires *around* these calls — the invalid-handle
|
|
126
|
+
* sentinel, the cascade on delete, the double-delete, the deferral of
|
|
127
|
+
* listener delivery — is done by {@link bindWasmBackend} rather than
|
|
128
|
+
* asked of each module, so that a browser build and a Node build behave
|
|
129
|
+
* the same way without implementing the same rules twice (§C.4.2).
|
|
130
|
+
*/
|
|
131
|
+
export interface WasmBackend {
|
|
132
|
+
createParticipant(domainId: number): number;
|
|
133
|
+
deleteParticipant(p: number): void;
|
|
134
|
+
createTopic(p: number, name: string, typeName: string): number;
|
|
135
|
+
deleteTopic(t: number): void;
|
|
136
|
+
createPublisher(p: number): number;
|
|
137
|
+
createSubscriber(p: number): number;
|
|
138
|
+
deletePublisher(pub: number): void;
|
|
139
|
+
deleteSubscriber(sub: number): void;
|
|
140
|
+
createDataWriter(pub: number, topic: number): number;
|
|
141
|
+
createDataReader(sub: number, topic: number): number;
|
|
142
|
+
writeSample(w: number, xcdr2: Uint8Array): void;
|
|
143
|
+
takeSamples(r: number, max: number): ReadonlyArray<Sample>;
|
|
144
|
+
deleteDataWriter(w: number): void;
|
|
145
|
+
deleteDataReader(r: number): void;
|
|
146
|
+
setDataAvailableListener(r: number, cb: ((r: number) => void) | null): void;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The operations of §C.2, with branded handles and the Annex C
|
|
150
|
+
* invariants enforced.
|
|
151
|
+
*
|
|
152
|
+
* The signatures are normative; the WebAssembly export names behind them
|
|
153
|
+
* are not. A Node.js binding built on N-API exposes this same interface
|
|
154
|
+
* so that an application retargets between browser and Node by changing
|
|
155
|
+
* an import and nothing else (§C.4.2).
|
|
156
|
+
*/
|
|
157
|
+
export interface DdsBindings {
|
|
158
|
+
createParticipant(domainId: number): ParticipantHandle;
|
|
159
|
+
deleteParticipant(p: ParticipantHandle): void;
|
|
160
|
+
createTopic(p: ParticipantHandle, name: string, typeName: string): TopicHandle;
|
|
161
|
+
deleteTopic(t: TopicHandle): void;
|
|
162
|
+
createPublisher(p: ParticipantHandle): PublisherHandle;
|
|
163
|
+
createSubscriber(p: ParticipantHandle): SubscriberHandle;
|
|
164
|
+
deletePublisher(pub: PublisherHandle): void;
|
|
165
|
+
deleteSubscriber(sub: SubscriberHandle): void;
|
|
166
|
+
createDataWriter(pub: PublisherHandle, topic: TopicHandle): DataWriterHandle;
|
|
167
|
+
createDataReader(sub: SubscriberHandle, topic: TopicHandle): DataReaderHandle;
|
|
168
|
+
writeSample(w: DataWriterHandle, xcdr2: Uint8Array): void;
|
|
169
|
+
takeSamples(r: DataReaderHandle, max: number): ReadonlyArray<Sample>;
|
|
170
|
+
deleteDataWriter(w: DataWriterHandle): void;
|
|
171
|
+
deleteDataReader(r: DataReaderHandle): void;
|
|
172
|
+
setDataAvailableListener(r: DataReaderHandle, cb: DataAvailableCallback | null): void;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Wrap a raw module in the invariants Annex C states.
|
|
176
|
+
*
|
|
177
|
+
* Five of them, and each one is a rule the type system cannot carry:
|
|
178
|
+
*
|
|
179
|
+
* 1. **The zero sentinel** (§C.1.1). `0 as DataReaderHandle` compiles,
|
|
180
|
+
* so every operation checks the handle it receives and rejects 0 —
|
|
181
|
+
* and anything that is not a non-negative 32-bit integer — with a
|
|
182
|
+
* `RangeError`.
|
|
183
|
+
* 2. **Liveness** (§C.5.3). A handle that has been deleted throws when
|
|
184
|
+
* it is used again, naming the handle. A double delete throws for
|
|
185
|
+
* the same reason: silently succeeding would let a caller believe it
|
|
186
|
+
* had released something twice as often as it had.
|
|
187
|
+
* 3. **The cascade** (§C.5.3). Deleting a participant releases
|
|
188
|
+
* everything created from it — topics, publishers, subscribers, and
|
|
189
|
+
* the writers and readers under those — and a call naming any of
|
|
190
|
+
* them afterwards throws rather than reaching a module whose
|
|
191
|
+
* resource is gone.
|
|
192
|
+
* 4. **Borrowed buffers** (§C.3.2). The octets handed to `writeSample`
|
|
193
|
+
* are copied before they cross, so a module that retained a
|
|
194
|
+
* reference past the call would be holding a buffer the caller
|
|
195
|
+
* cannot observe; and samples coming back are copied again, so what
|
|
196
|
+
* the caller holds is owned by JavaScript and not a view into a
|
|
197
|
+
* WebAssembly memory that may be resized under it.
|
|
198
|
+
* 5. **Deferred delivery** (§C.2.4). A `DataAvailable` listener runs on
|
|
199
|
+
* the host event loop, never inside the synchronous `writeSample`
|
|
200
|
+
* that produced the sample. The wrapper queues a microtask, so the
|
|
201
|
+
* callback runs after the caller's current turn finishes.
|
|
202
|
+
*/
|
|
203
|
+
export declare function bindWasmBackend(backend: WasmBackend): DdsBindings;
|
package/dist/wasm.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 ZeroDDS Contributors
|
|
3
|
+
/**
|
|
4
|
+
* A GUID from its two parts, or a `RangeError`.
|
|
5
|
+
*
|
|
6
|
+
* DDS-TS 1.0 §C.1.2.
|
|
7
|
+
*/
|
|
8
|
+
export function makeDdsGuid(prefix, entityId) {
|
|
9
|
+
if (prefix.length !== 12) {
|
|
10
|
+
throw new RangeError("DdsGuid.prefix requires 12 bytes");
|
|
11
|
+
}
|
|
12
|
+
if (!Number.isInteger(entityId) || entityId < 0 || entityId > 0xffffffff) {
|
|
13
|
+
throw new RangeError("DdsGuid.entityId requires uint32");
|
|
14
|
+
}
|
|
15
|
+
return { prefix, entityId };
|
|
16
|
+
}
|
|
17
|
+
/** The table key. Two kinds may hold the same number, so both are in it. */
|
|
18
|
+
function slot(kind, value) {
|
|
19
|
+
return `${kind}:${value}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a raw module in the invariants Annex C states.
|
|
23
|
+
*
|
|
24
|
+
* Five of them, and each one is a rule the type system cannot carry:
|
|
25
|
+
*
|
|
26
|
+
* 1. **The zero sentinel** (§C.1.1). `0 as DataReaderHandle` compiles,
|
|
27
|
+
* so every operation checks the handle it receives and rejects 0 —
|
|
28
|
+
* and anything that is not a non-negative 32-bit integer — with a
|
|
29
|
+
* `RangeError`.
|
|
30
|
+
* 2. **Liveness** (§C.5.3). A handle that has been deleted throws when
|
|
31
|
+
* it is used again, naming the handle. A double delete throws for
|
|
32
|
+
* the same reason: silently succeeding would let a caller believe it
|
|
33
|
+
* had released something twice as often as it had.
|
|
34
|
+
* 3. **The cascade** (§C.5.3). Deleting a participant releases
|
|
35
|
+
* everything created from it — topics, publishers, subscribers, and
|
|
36
|
+
* the writers and readers under those — and a call naming any of
|
|
37
|
+
* them afterwards throws rather than reaching a module whose
|
|
38
|
+
* resource is gone.
|
|
39
|
+
* 4. **Borrowed buffers** (§C.3.2). The octets handed to `writeSample`
|
|
40
|
+
* are copied before they cross, so a module that retained a
|
|
41
|
+
* reference past the call would be holding a buffer the caller
|
|
42
|
+
* cannot observe; and samples coming back are copied again, so what
|
|
43
|
+
* the caller holds is owned by JavaScript and not a view into a
|
|
44
|
+
* WebAssembly memory that may be resized under it.
|
|
45
|
+
* 5. **Deferred delivery** (§C.2.4). A `DataAvailable` listener runs on
|
|
46
|
+
* the host event loop, never inside the synchronous `writeSample`
|
|
47
|
+
* that produced the sample. The wrapper queues a microtask, so the
|
|
48
|
+
* callback runs after the caller's current turn finishes.
|
|
49
|
+
*/
|
|
50
|
+
export function bindWasmBackend(backend) {
|
|
51
|
+
const live = new Map();
|
|
52
|
+
/** Record a handle the module just produced. */
|
|
53
|
+
function born(kind, value, parents) {
|
|
54
|
+
if (!Number.isInteger(value) || value <= 0 || value > 0xffffffff) {
|
|
55
|
+
throw new RangeError(`${kind}: the backend answered with ${value}, which is not a handle`);
|
|
56
|
+
}
|
|
57
|
+
live.set(slot(kind, value), { kind, value, parents });
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/** Check a handle a caller passed in, and answer with its number. */
|
|
61
|
+
function check(kind, handle) {
|
|
62
|
+
if (!Number.isInteger(handle) || handle < 0 || handle > 0xffffffff) {
|
|
63
|
+
throw new RangeError(`${kind} handle ${String(handle)} is not a handle`);
|
|
64
|
+
}
|
|
65
|
+
if (handle === 0) {
|
|
66
|
+
throw new RangeError(`${kind} handle 0 is the invalid-handle sentinel`);
|
|
67
|
+
}
|
|
68
|
+
if (!live.has(slot(kind, handle))) {
|
|
69
|
+
throw new RangeError(`${kind} handle ${handle} is not live`);
|
|
70
|
+
}
|
|
71
|
+
return handle;
|
|
72
|
+
}
|
|
73
|
+
/** Forget a handle and everything created from it. */
|
|
74
|
+
function bury(kind, handle) {
|
|
75
|
+
const key = slot(kind, handle);
|
|
76
|
+
live.delete(key);
|
|
77
|
+
for (const [child, resource] of [...live]) {
|
|
78
|
+
if (resource.parents.includes(key)) {
|
|
79
|
+
bury(resource.kind, resource.value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A copy of the caller's octets.
|
|
85
|
+
*
|
|
86
|
+
* `slice` and not the array itself: §C.3.2 makes the buffer borrowed
|
|
87
|
+
* for the duration of the call, and the copy is what makes that true
|
|
88
|
+
* of a module that does not read the clause.
|
|
89
|
+
*/
|
|
90
|
+
function borrowed(octets) {
|
|
91
|
+
return octets.slice();
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
createParticipant(domainId) {
|
|
95
|
+
if (!Number.isInteger(domainId) || domainId < 0) {
|
|
96
|
+
throw new RangeError(`domain ${String(domainId)} is not a domain id`);
|
|
97
|
+
}
|
|
98
|
+
return born("participant", backend.createParticipant(domainId), []);
|
|
99
|
+
},
|
|
100
|
+
deleteParticipant(p) {
|
|
101
|
+
const handle = check("participant", p);
|
|
102
|
+
backend.deleteParticipant(handle);
|
|
103
|
+
bury("participant", handle);
|
|
104
|
+
},
|
|
105
|
+
createTopic(p, name, typeName) {
|
|
106
|
+
const participant = check("participant", p);
|
|
107
|
+
return born("topic", backend.createTopic(participant, name, typeName), [
|
|
108
|
+
slot("participant", participant),
|
|
109
|
+
]);
|
|
110
|
+
},
|
|
111
|
+
deleteTopic(t) {
|
|
112
|
+
const handle = check("topic", t);
|
|
113
|
+
backend.deleteTopic(handle);
|
|
114
|
+
bury("topic", handle);
|
|
115
|
+
},
|
|
116
|
+
createPublisher(p) {
|
|
117
|
+
const participant = check("participant", p);
|
|
118
|
+
return born("publisher", backend.createPublisher(participant), [
|
|
119
|
+
slot("participant", participant),
|
|
120
|
+
]);
|
|
121
|
+
},
|
|
122
|
+
createSubscriber(p) {
|
|
123
|
+
const participant = check("participant", p);
|
|
124
|
+
return born("subscriber", backend.createSubscriber(participant), [
|
|
125
|
+
slot("participant", participant),
|
|
126
|
+
]);
|
|
127
|
+
},
|
|
128
|
+
deletePublisher(pub) {
|
|
129
|
+
const handle = check("publisher", pub);
|
|
130
|
+
backend.deletePublisher(handle);
|
|
131
|
+
bury("publisher", handle);
|
|
132
|
+
},
|
|
133
|
+
deleteSubscriber(sub) {
|
|
134
|
+
const handle = check("subscriber", sub);
|
|
135
|
+
backend.deleteSubscriber(handle);
|
|
136
|
+
bury("subscriber", handle);
|
|
137
|
+
},
|
|
138
|
+
createDataWriter(pub, topic) {
|
|
139
|
+
const publisher = check("publisher", pub);
|
|
140
|
+
const t = check("topic", topic);
|
|
141
|
+
return born("writer", backend.createDataWriter(publisher, t), [
|
|
142
|
+
slot("publisher", publisher),
|
|
143
|
+
slot("topic", t),
|
|
144
|
+
]);
|
|
145
|
+
},
|
|
146
|
+
createDataReader(sub, topic) {
|
|
147
|
+
const subscriber = check("subscriber", sub);
|
|
148
|
+
const t = check("topic", topic);
|
|
149
|
+
return born("reader", backend.createDataReader(subscriber, t), [
|
|
150
|
+
slot("subscriber", subscriber),
|
|
151
|
+
slot("topic", t),
|
|
152
|
+
]);
|
|
153
|
+
},
|
|
154
|
+
writeSample(w, xcdr2) {
|
|
155
|
+
backend.writeSample(check("writer", w), borrowed(xcdr2));
|
|
156
|
+
},
|
|
157
|
+
takeSamples(r, max) {
|
|
158
|
+
if (!Number.isInteger(max) || max < 0) {
|
|
159
|
+
throw new RangeError(`take of ${String(max)} samples is not a count`);
|
|
160
|
+
}
|
|
161
|
+
return backend.takeSamples(check("reader", r), max).map((sample) => ({
|
|
162
|
+
bytes: sample.bytes.slice(),
|
|
163
|
+
info: sample.info,
|
|
164
|
+
}));
|
|
165
|
+
},
|
|
166
|
+
deleteDataWriter(w) {
|
|
167
|
+
const handle = check("writer", w);
|
|
168
|
+
backend.deleteDataWriter(handle);
|
|
169
|
+
bury("writer", handle);
|
|
170
|
+
},
|
|
171
|
+
deleteDataReader(r) {
|
|
172
|
+
const handle = check("reader", r);
|
|
173
|
+
backend.deleteDataReader(handle);
|
|
174
|
+
bury("reader", handle);
|
|
175
|
+
},
|
|
176
|
+
setDataAvailableListener(r, cb) {
|
|
177
|
+
const handle = check("reader", r);
|
|
178
|
+
if (cb === null) {
|
|
179
|
+
backend.setDataAvailableListener(handle, null);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
backend.setDataAvailableListener(handle, (reader) => {
|
|
183
|
+
// The host event loop, not this turn. §C.2.4 forbids delivery
|
|
184
|
+
// inside the synchronous `writeSample` that produced the
|
|
185
|
+
// sample, and a backend that calls straight through would
|
|
186
|
+
// otherwise re-enter the application from the middle of its
|
|
187
|
+
// own write.
|
|
188
|
+
queueMicrotask(() => {
|
|
189
|
+
cb(reader);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|