@sofa-buffers/corelib 0.8.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/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/index.cjs +1963 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +825 -0
- package/dist/index.global.js +1968 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +1936 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-format constants for SofaBuffers.
|
|
3
|
+
*
|
|
4
|
+
* The format is specified, language-neutrally, in the
|
|
5
|
+
* {@link https://github.com/sofa-buffers/documentation | SofaBuffers documentation}.
|
|
6
|
+
* Every field on the wire begins with a varint header `(id << 3) | wireType`,
|
|
7
|
+
* so the wire type lives in the low three bits and the field id in the rest.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The SofaBuffers API version this library implements. The generator and other
|
|
11
|
+
* tooling read it to verify compatibility. Bumped only on a breaking API change.
|
|
12
|
+
*/
|
|
13
|
+
declare const API_VERSION = 1;
|
|
14
|
+
/** The three low bits of a field header: what kind of field follows. */
|
|
15
|
+
declare const WireType: {
|
|
16
|
+
/** Unsigned varint scalar. */
|
|
17
|
+
readonly Unsigned: 0;
|
|
18
|
+
/** Signed varint scalar (zig-zag encoded). */
|
|
19
|
+
readonly Signed: 1;
|
|
20
|
+
/** Fixed-length value: fp32, fp64, string or blob (see {@link FixlenSubtype}). */
|
|
21
|
+
readonly Fixlen: 2;
|
|
22
|
+
/** Array of unsigned varints. */
|
|
23
|
+
readonly ArrayUnsigned: 3;
|
|
24
|
+
/** Array of signed (zig-zag) varints. */
|
|
25
|
+
readonly ArraySigned: 4;
|
|
26
|
+
/** Array of fixed-length values (fp32 / fp64 only). */
|
|
27
|
+
readonly ArrayFixlen: 5;
|
|
28
|
+
/** Opens a nested sequence (new id scope). */
|
|
29
|
+
readonly SequenceStart: 6;
|
|
30
|
+
/** Closes the current sequence. Encoded as the single byte `0x07`. */
|
|
31
|
+
readonly SequenceEnd: 7;
|
|
32
|
+
};
|
|
33
|
+
/** A field's wire type: one of the {@link WireType} values. */
|
|
34
|
+
type WireType = (typeof WireType)[keyof typeof WireType];
|
|
35
|
+
/** The three low bits of a fixlen length header: which fixed-length type. */
|
|
36
|
+
declare const FixlenSubtype: {
|
|
37
|
+
/** IEEE-754 32-bit float, little-endian. */
|
|
38
|
+
readonly Fp32: 0;
|
|
39
|
+
/** IEEE-754 64-bit double, little-endian. */
|
|
40
|
+
readonly Fp64: 1;
|
|
41
|
+
/** UTF-8 string (no null terminator). */
|
|
42
|
+
readonly String: 2;
|
|
43
|
+
/** Arbitrary binary data. */
|
|
44
|
+
readonly Blob: 3;
|
|
45
|
+
};
|
|
46
|
+
/** A fixed-length value's type: one of the {@link FixlenSubtype} values. */
|
|
47
|
+
type FixlenSubtype = (typeof FixlenSubtype)[keyof typeof FixlenSubtype];
|
|
48
|
+
/** Which element kind an array field carries (reported to {@link Visitor.arrayBegin}). */
|
|
49
|
+
declare const ArrayKind: {
|
|
50
|
+
/** Unsigned-integer elements. */
|
|
51
|
+
readonly Unsigned: 0;
|
|
52
|
+
/** Signed-integer (zig-zag) elements. */
|
|
53
|
+
readonly Signed: 1;
|
|
54
|
+
/** IEEE-754 32-bit float elements. */
|
|
55
|
+
readonly Fp32: 2;
|
|
56
|
+
/** IEEE-754 64-bit double elements. */
|
|
57
|
+
readonly Fp64: 3;
|
|
58
|
+
};
|
|
59
|
+
/** An array field's element kind: one of the {@link ArrayKind} values. */
|
|
60
|
+
type ArrayKind = (typeof ArrayKind)[keyof typeof ArrayKind];
|
|
61
|
+
/** Largest permitted field id and fixlen length / array count: `INT32_MAX`. */
|
|
62
|
+
declare const ID_MAX = 2147483647;
|
|
63
|
+
/** Largest permitted fixlen byte length: `INT32_MAX`. */
|
|
64
|
+
declare const FIXLEN_MAX = 2147483647;
|
|
65
|
+
/** Largest permitted array element count: `INT32_MAX`. */
|
|
66
|
+
declare const ARRAY_MAX = 2147483647;
|
|
67
|
+
/** Largest unsigned 64-bit value (`2^64 - 1`). */
|
|
68
|
+
declare const U64_MAX = 18446744073709551615n;
|
|
69
|
+
/** Smallest signed 64-bit value (`-2^63`). */
|
|
70
|
+
declare const I64_MIN = -9223372036854775808n;
|
|
71
|
+
/** Largest signed 64-bit value (`2^63 - 1`). */
|
|
72
|
+
declare const I64_MAX = 9223372036854775807n;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum nested-sequence depth (§4.9 / §6.2). An encoder must not open more
|
|
75
|
+
* than this many nested sequences, and a decoder must reject a message that
|
|
76
|
+
* nests deeper with an `InvalidMessage` error rather than risk unbounded
|
|
77
|
+
* recursion / stack growth.
|
|
78
|
+
*/
|
|
79
|
+
declare const MAX_DEPTH = 255;
|
|
80
|
+
/**
|
|
81
|
+
* The terminal outcome of a decode (MESSAGE_SPEC §7), reported identically for
|
|
82
|
+
* one-shot and streaming decode with **no** finish / finalize / end promotion
|
|
83
|
+
* step:
|
|
84
|
+
*
|
|
85
|
+
* - `Complete` — the bytes ended exactly at a field boundary: a valid message.
|
|
86
|
+
* - `Incomplete` — the bytes ended *inside* a field (an unterminated varint, a
|
|
87
|
+
* payload shorter than its declared length, an array that runs off the end, or
|
|
88
|
+
* a nested sequence never closed). **Not an error** — more bytes could
|
|
89
|
+
* complete it, and the caller owns end-of-input.
|
|
90
|
+
* - `Invalid` — the bytes are malformed regardless of what follows.
|
|
91
|
+
*
|
|
92
|
+
* {@link IStream.end} returns `Complete` or `Incomplete` (an `Invalid` message
|
|
93
|
+
* has already thrown from {@link IStream.feed}); the one-shot {@link decode} /
|
|
94
|
+
* {@link Cursor} path signals `Incomplete` and `Invalid` by throwing a
|
|
95
|
+
* {@link SofabError} whose `code` is {@link SofabErrorCode.Incomplete} or
|
|
96
|
+
* {@link SofabErrorCode.InvalidMsg}, and `Complete` by returning normally.
|
|
97
|
+
*/
|
|
98
|
+
declare const DecodeStatus: {
|
|
99
|
+
/** The bytes ended exactly at a field boundary — a valid message. */
|
|
100
|
+
readonly Complete: "COMPLETE";
|
|
101
|
+
/** The bytes ended inside a field; more bytes could complete it (not an error). */
|
|
102
|
+
readonly Incomplete: "INCOMPLETE";
|
|
103
|
+
/** The bytes are malformed regardless of what follows. */
|
|
104
|
+
readonly Invalid: "INVALID";
|
|
105
|
+
};
|
|
106
|
+
/** A decode's terminal outcome: one of the {@link DecodeStatus} values. */
|
|
107
|
+
type DecodeStatus = (typeof DecodeStatus)[keyof typeof DecodeStatus];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Error handling for SofaBuffers.
|
|
111
|
+
*
|
|
112
|
+
* Both encoder and decoder report problems through a single {@link SofabError}
|
|
113
|
+
* carrying a {@link SofabErrorCode}, mirroring the C reference's `sofab_ret_t`
|
|
114
|
+
* return codes so the failure modes line up across the language family.
|
|
115
|
+
*
|
|
116
|
+
* The decoder distinguishes two *kinds* of decode failure (MESSAGE_SPEC §7):
|
|
117
|
+
* {@link SofabErrorCode.InvalidMsg} for input that is malformed regardless of
|
|
118
|
+
* what follows (`INVALID`), and {@link SofabErrorCode.Incomplete} for input that
|
|
119
|
+
* merely ends inside a field (`INCOMPLETE`) — a truncation that more bytes could
|
|
120
|
+
* complete, and so is *not* the same as a malformed message. A third code,
|
|
121
|
+
* {@link SofabErrorCode.LimitExceeded}, is orthogonal to both: it reports a
|
|
122
|
+
* receiver-configured decode limit being hit — *policy*, not a property of the
|
|
123
|
+
* bytes — so it is kept distinct from `InvalidMsg`.
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* The cause of a {@link SofabError}. `Argument`, `Usage`, `BufferFull` and
|
|
127
|
+
* `InvalidMsg` match the C reference's `sofab_ret_t` codes; `Incomplete` is the
|
|
128
|
+
* finish-less INCOMPLETE decode outcome (MESSAGE_SPEC §7), a distinct,
|
|
129
|
+
* more-bytes-could-complete-it signal split out from `InvalidMsg`.
|
|
130
|
+
*/
|
|
131
|
+
declare const SofabErrorCode: {
|
|
132
|
+
/** A caller argument was invalid (e.g. id out of range, empty array). */
|
|
133
|
+
readonly Argument: "ARGUMENT";
|
|
134
|
+
/** The API was used incorrectly (e.g. unbalanced sequence end). */
|
|
135
|
+
readonly Usage: "USAGE";
|
|
136
|
+
/** The output buffer is full and no flush sink was provided. */
|
|
137
|
+
readonly BufferFull: "BUFFER_FULL";
|
|
138
|
+
/** The input being decoded is malformed regardless of what follows (`INVALID`). */
|
|
139
|
+
readonly InvalidMsg: "INVALID_MSG";
|
|
140
|
+
/**
|
|
141
|
+
* The input being decoded ends inside a field (`INCOMPLETE`, MESSAGE_SPEC §7):
|
|
142
|
+
* an unterminated varint, a payload shorter than its declared length, an array
|
|
143
|
+
* that runs off the end, or a nested sequence never closed. Not a malformed
|
|
144
|
+
* message — more bytes could complete it, and the caller owns end-of-input.
|
|
145
|
+
*/
|
|
146
|
+
readonly Incomplete: "INCOMPLETE";
|
|
147
|
+
/**
|
|
148
|
+
* A receiver-configured decode limit was exceeded — a dynamic array, string or
|
|
149
|
+
* blob on the wire claims more elements / bytes than the caller's
|
|
150
|
+
* {@link DecodeLimits} (`maxArrayCount` / `maxStringLen` / `maxBlobLen`)
|
|
151
|
+
* allows. Deliberately distinct from {@link SofabErrorCode.InvalidMsg}:
|
|
152
|
+
* exceeding a limit is *policy*, not wire malformation — the identical bytes
|
|
153
|
+
* decode fine under a looser limit — so differential fuzzing must not read it
|
|
154
|
+
* as a conformance divergence. The decoder never clamps or truncates; it
|
|
155
|
+
* rejects, before the offending field is materialized.
|
|
156
|
+
*/
|
|
157
|
+
readonly LimitExceeded: "LIMIT_EXCEEDED";
|
|
158
|
+
};
|
|
159
|
+
/** A {@link SofabError}'s cause: one of the {@link SofabErrorCode} values. */
|
|
160
|
+
type SofabErrorCode = (typeof SofabErrorCode)[keyof typeof SofabErrorCode];
|
|
161
|
+
/** The single error type thrown by the encoder and decoder. */
|
|
162
|
+
declare class SofabError extends Error {
|
|
163
|
+
/** The machine-readable cause. */
|
|
164
|
+
readonly code: SofabErrorCode;
|
|
165
|
+
constructor(code: SofabErrorCode, message: string);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A 64-bit integer value carried as two unsigned 32-bit halves.
|
|
170
|
+
*
|
|
171
|
+
* SofaBuffers accepts `bigint` at every 64-bit surface for ergonomics, but a
|
|
172
|
+
* `bigint` in the encode/decode hot path is expensive — especially on
|
|
173
|
+
* JavaScriptCore (Bun), where profiling showed the `bigint` split/materialise
|
|
174
|
+
* and zig-zag operations dominating the 64-bit array codecs. `Long` lets a
|
|
175
|
+
* caller (and the generated code) stay entirely on `number` arithmetic: the
|
|
176
|
+
* codec reads `.low`/`.high` directly, and any `bigint`↔`Long` conversion
|
|
177
|
+
* happens once at the API boundary rather than once per encode.
|
|
178
|
+
*
|
|
179
|
+
* Values are stored as raw two's-complement bits; the same `Long` serves an
|
|
180
|
+
* unsigned or signed field — {@link Long.toBigInt} takes the signedness.
|
|
181
|
+
*/
|
|
182
|
+
declare class Long {
|
|
183
|
+
/** Low 32 bits (unsigned). */
|
|
184
|
+
readonly low: number;
|
|
185
|
+
/** High 32 bits (unsigned). */
|
|
186
|
+
readonly high: number;
|
|
187
|
+
constructor(low: number, high: number);
|
|
188
|
+
/**
|
|
189
|
+
* The 64-bit zero. `Long` is immutable (readonly halves), so this single shared
|
|
190
|
+
* instance is safe to reuse anywhere a zero default is needed — generated code
|
|
191
|
+
* uses it for fixed-count array defaults and pad-fill instead of
|
|
192
|
+
* `Long.fromValue(0)`, which would run `bigint` arithmetic per call on the hot
|
|
193
|
+
* decode/encode path.
|
|
194
|
+
*/
|
|
195
|
+
static readonly ZERO: Long;
|
|
196
|
+
/** Construct from raw 32-bit halves. */
|
|
197
|
+
static fromBits(low: number, high: number): Long;
|
|
198
|
+
/** Split a `bigint` into its low/high 32-bit halves (two's complement). */
|
|
199
|
+
static fromBigInt(value: bigint): Long;
|
|
200
|
+
/** From an integer `number` (`|n| < 2^53`); sign handled via `bigint` once. */
|
|
201
|
+
static fromNumber(n: number): Long;
|
|
202
|
+
/** Accept a `Long` as-is, or convert a `bigint` / `number` once. */
|
|
203
|
+
static fromValue(v: Long | bigint | number): Long;
|
|
204
|
+
/** Materialise as a `bigint`. `signed` reads the high bit as two's complement. */
|
|
205
|
+
toBigInt(signed?: boolean): bigint;
|
|
206
|
+
/** Decimal string (`signed` interprets the high bit as two's complement). */
|
|
207
|
+
toString(signed?: boolean): string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The output drain used for streaming encodes.
|
|
212
|
+
*
|
|
213
|
+
* When an {@link OStream} is given a `FlushSink`, it writes into a small caller
|
|
214
|
+
* buffer and hands each filled region to the sink, so a message can be far
|
|
215
|
+
* larger than the buffer — larger than RAM, even. The chunk passed to the sink
|
|
216
|
+
* is only valid for the duration of the call; copy it if you need to retain it.
|
|
217
|
+
*/
|
|
218
|
+
type FlushSink = (chunk: Uint8Array) => void;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The SofaBuffers encoder.
|
|
222
|
+
*
|
|
223
|
+
* `OStream` writes fields into a byte buffer. Two modes:
|
|
224
|
+
*
|
|
225
|
+
* - **In-memory** (`new OStream()`): an auto-growing buffer; call
|
|
226
|
+
* {@link OStream.bytes} for the finished message.
|
|
227
|
+
* - **Streaming** (`new OStream(buffer, offset?, flush?)`): writes into a
|
|
228
|
+
* caller-provided buffer and, when it fills, hands the produced bytes to the
|
|
229
|
+
* `flush` sink and continues — so the buffer can be much smaller than the
|
|
230
|
+
* message. `offset` reserves room at the front for a lower-layer header.
|
|
231
|
+
*
|
|
232
|
+
* Generated code typically writes one field per message field; the methods map
|
|
233
|
+
* one-to-one onto the wire types. Problems throw {@link SofabError}.
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Encoder for the SofaBuffers wire format. Each `write*` method appends one
|
|
238
|
+
* field and maps one-to-one onto a wire type. Construct it in-memory (an
|
|
239
|
+
* auto-growing buffer, read back with {@link OStream.bytes}) or in streaming
|
|
240
|
+
* mode over a caller-provided buffer that drains to a {@link FlushSink} as it
|
|
241
|
+
* fills, so the message can outgrow the buffer. Invalid arguments and a full
|
|
242
|
+
* buffer with no sink throw {@link SofabError}.
|
|
243
|
+
*/
|
|
244
|
+
declare class OStream {
|
|
245
|
+
private buf;
|
|
246
|
+
private pos;
|
|
247
|
+
private start;
|
|
248
|
+
private readonly flushSink;
|
|
249
|
+
private readonly canGrow;
|
|
250
|
+
private depth;
|
|
251
|
+
private kernel;
|
|
252
|
+
/** In-memory encoder backed by an auto-growing buffer. */
|
|
253
|
+
constructor();
|
|
254
|
+
/** Streaming encoder over a caller buffer, optionally draining to `flush`. */
|
|
255
|
+
constructor(buffer: Uint8Array, offset?: number, flush?: FlushSink);
|
|
256
|
+
/** Bytes currently held in the buffer (since construction or the last flush). */
|
|
257
|
+
get bytesUsed(): number;
|
|
258
|
+
/**
|
|
259
|
+
* The encoded message so far, as a view into the working buffer.
|
|
260
|
+
* Meaningful for the in-memory mode; in streaming mode it is only the
|
|
261
|
+
* not-yet-flushed tail. The view is valid until the next write.
|
|
262
|
+
*/
|
|
263
|
+
bytes(): Uint8Array;
|
|
264
|
+
/** Drain buffered bytes to the flush sink (no-op without one). */
|
|
265
|
+
flush(): void;
|
|
266
|
+
/**
|
|
267
|
+
* Install a fresh output buffer to write into, mid-stream. Intended for the
|
|
268
|
+
* streaming (flush-sink) mode: call it from inside your flush callback to hand
|
|
269
|
+
* the encoder a new buffer for the next batch of bytes, so encoding continues
|
|
270
|
+
* without interruption. `offset` reserves space at the front of the new
|
|
271
|
+
* buffer. Any not-yet-flushed bytes in the old buffer are dropped, so
|
|
272
|
+
* {@link flush} first (the flush callback fires before you swap).
|
|
273
|
+
*/
|
|
274
|
+
setBuffer(buffer: Uint8Array, offset?: number): void;
|
|
275
|
+
/**
|
|
276
|
+
* Rewind the encoder to empty, reusing the existing buffer. Lets a caller pool
|
|
277
|
+
* one OStream across many messages instead of allocating a fresh buffer per
|
|
278
|
+
* encode. Any view previously returned by {@link bytes} is invalidated.
|
|
279
|
+
*/
|
|
280
|
+
reset(): void;
|
|
281
|
+
/** Write an unsigned integer field. */
|
|
282
|
+
writeUnsigned(id: number, value: number | bigint): void;
|
|
283
|
+
/** Write a signed integer field (zig-zag encoded). */
|
|
284
|
+
writeSigned(id: number, value: number | bigint): void;
|
|
285
|
+
/** Write a boolean field (encoded as the unsigned value 0 or 1). */
|
|
286
|
+
writeBoolean(id: number, value: boolean): void;
|
|
287
|
+
/** Write an IEEE-754 32-bit float field. */
|
|
288
|
+
writeFp32(id: number, value: number): void;
|
|
289
|
+
/** Write an IEEE-754 64-bit double field. */
|
|
290
|
+
writeFp64(id: number, value: number): void;
|
|
291
|
+
/** Write a UTF-8 string field. */
|
|
292
|
+
writeString(id: number, text: string): void;
|
|
293
|
+
/** Write a blob (arbitrary bytes) field. */
|
|
294
|
+
writeBlob(id: number, data: Uint8Array): void;
|
|
295
|
+
/** Write a fixed-length field of the given subtype from raw bytes. */
|
|
296
|
+
writeFixlen(id: number, data: Uint8Array, subtype: FixlenSubtype): void;
|
|
297
|
+
/** Write an array of unsigned integers (each a varint). */
|
|
298
|
+
writeUnsignedArray(id: number, values: ArrayLike<number | bigint>): void;
|
|
299
|
+
/** Write an array of signed integers (each zig-zag + varint). */
|
|
300
|
+
writeSignedArray(id: number, values: ArrayLike<number | bigint>): void;
|
|
301
|
+
/**
|
|
302
|
+
* Write an unsigned 64-bit array from {@link Long}[] — the `bigint`-free path.
|
|
303
|
+
* Produces the identical wire to {@link writeUnsignedArray}; reads each Long's
|
|
304
|
+
* 32-bit halves directly, so no `bigint` is created per element.
|
|
305
|
+
*/
|
|
306
|
+
writeUnsignedArrayLong(id: number, values: readonly Long[]): void;
|
|
307
|
+
/**
|
|
308
|
+
* Write a signed 64-bit array (zig-zag) from {@link Long}[] — the `bigint`-free
|
|
309
|
+
* path. Zig-zag `(n << 1) ^ (n >> 63)` is computed on the lo/hi pair.
|
|
310
|
+
*/
|
|
311
|
+
writeSignedArrayLong(id: number, values: readonly Long[]): void;
|
|
312
|
+
/** Write an array of IEEE-754 32-bit floats. */
|
|
313
|
+
writeFp32Array(id: number, values: ArrayLike<number>): void;
|
|
314
|
+
/** Write an array of IEEE-754 64-bit doubles. */
|
|
315
|
+
writeFp64Array(id: number, values: ArrayLike<number>): void;
|
|
316
|
+
/** Open a nested sequence (a fresh id scope). */
|
|
317
|
+
writeSequenceBegin(id: number): void;
|
|
318
|
+
/** Close the current sequence. */
|
|
319
|
+
writeSequenceEnd(): void;
|
|
320
|
+
/** Ensure exactly `value`'s varint size, then write it (bigint path). */
|
|
321
|
+
private putVarint;
|
|
322
|
+
/** Ensure exactly `value`'s varint size, then write it (number fast path). */
|
|
323
|
+
private putVarintNum;
|
|
324
|
+
private header;
|
|
325
|
+
private fixlenHead;
|
|
326
|
+
private arrayHead;
|
|
327
|
+
/** Copy `data` out, flushing/growing as needed (large payloads stay chunked). */
|
|
328
|
+
private writeRaw;
|
|
329
|
+
/** Ensure `n` contiguous bytes are free at `pos`; returns `pos` for chaining. */
|
|
330
|
+
private ensure;
|
|
331
|
+
/** Ensure *some* room (up to `want`); returns how many bytes are available. */
|
|
332
|
+
private ensureSome;
|
|
333
|
+
private growTo;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Opt-in decode resource limits (corelib-ts#38).
|
|
338
|
+
*
|
|
339
|
+
* SofaBuffers' `count` (arrays) and `maxlen` (strings / blobs) schema bounds are
|
|
340
|
+
* optional; when a schema omits them the decoder accepts whatever count / length
|
|
341
|
+
* the received message claims, with no upper bound. That leaves a receiver no way
|
|
342
|
+
* to cap memory against a hostile, oversized field. {@link DecodeLimits} is that
|
|
343
|
+
* cap: an optional options object accepted by every decode entry point
|
|
344
|
+
* ({@link decode}, the {@link IStream} constructor, and the {@link Cursor}
|
|
345
|
+
* constructor).
|
|
346
|
+
*
|
|
347
|
+
* The limits are a **receiver-side policy**, not part of the wire format or the
|
|
348
|
+
* message schema. The normative source of the values is the sofabgen config
|
|
349
|
+
* (see sofa-buffers/generator#102): the generator bakes them into generated code
|
|
350
|
+
* as constants and passes them here at decoder construction. This corelib only
|
|
351
|
+
* provides the mechanism — **an omitted limit means no cap (today's behavior);
|
|
352
|
+
* there is no corelib-side default.**
|
|
353
|
+
*
|
|
354
|
+
* Enforcement happens at header time — where the count / length is first decoded,
|
|
355
|
+
* before any array is sized or any payload is accepted or streamed — so a claimed
|
|
356
|
+
* oversize is rejected even if the payload never arrives. Exceeding a limit is
|
|
357
|
+
* never clamped or truncated: it throws a {@link SofabError} with code
|
|
358
|
+
* {@link SofabErrorCode.LimitExceeded}, which is deliberately distinct from
|
|
359
|
+
* `InvalidMsg` (policy, not malformation).
|
|
360
|
+
*/
|
|
361
|
+
interface DecodeLimits {
|
|
362
|
+
/**
|
|
363
|
+
* Reject a dynamic (`u*` / `i*`, `fp32` / `fp64`) array whose element `count`
|
|
364
|
+
* exceeds this, before the array is materialized. Omit for no cap.
|
|
365
|
+
*/
|
|
366
|
+
maxArrayCount?: number;
|
|
367
|
+
/**
|
|
368
|
+
* Reject a UTF-8 string whose declared byte length exceeds this, before the
|
|
369
|
+
* payload is decoded or streamed. Omit for no cap.
|
|
370
|
+
*/
|
|
371
|
+
maxStringLen?: number;
|
|
372
|
+
/**
|
|
373
|
+
* Reject a blob whose declared byte length exceeds this, before the payload is
|
|
374
|
+
* accepted or streamed. Omit for no cap.
|
|
375
|
+
*/
|
|
376
|
+
maxBlobLen?: number;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* The SofaBuffers decoder.
|
|
381
|
+
*
|
|
382
|
+
* `IStream` is a push parser: feed it bytes with {@link IStream.feed} and it
|
|
383
|
+
* drives a {@link Visitor}, calling one method per decoded field. It is a
|
|
384
|
+
* resumable state machine, so the chunks you feed can be any size — a whole
|
|
385
|
+
* message, a network packet, or a single byte — and a field that straddles a
|
|
386
|
+
* chunk boundary is picked up seamlessly on the next call.
|
|
387
|
+
*
|
|
388
|
+
* Nesting is hierarchical: {@link Visitor.sequenceBegin} may return a child
|
|
389
|
+
* visitor, and the decoder routes the nested fields to it until the matching
|
|
390
|
+
* end. Generated message classes use this directly — a class implements
|
|
391
|
+
* `Visitor`, and a nested-message field returns the child instance.
|
|
392
|
+
*
|
|
393
|
+
* There is no finish / finalize step (MESSAGE_SPEC §7): {@link IStream.feed}
|
|
394
|
+
* throws only for a *malformed* message ({@link SofabErrorCode.InvalidMsg}); a
|
|
395
|
+
* message that merely ends inside a field is reported — never thrown — by
|
|
396
|
+
* {@link IStream.end}, which returns {@link DecodeStatus.Incomplete} rather than
|
|
397
|
+
* {@link DecodeStatus.Complete}. The caller owns end-of-input and decides
|
|
398
|
+
* whether a trailing `Incomplete` is a truncation error.
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Receives decoded fields from an {@link IStream}. Every method is optional and
|
|
403
|
+
* defaults to a no-op, so a visitor implements only the fields it cares about
|
|
404
|
+
* and silently skips the rest.
|
|
405
|
+
*
|
|
406
|
+
* String and blob payloads arrive as one or more `chunk`s, each tagged with the
|
|
407
|
+
* field's `total` length and the `offset` of the chunk within the field, so a
|
|
408
|
+
* large payload never has to be held in one piece. Array elements arrive one at
|
|
409
|
+
* a time between {@link Visitor.arrayBegin} and {@link Visitor.arrayEnd}.
|
|
410
|
+
*/
|
|
411
|
+
interface Visitor {
|
|
412
|
+
/**
|
|
413
|
+
* An unsigned integer field. Number-first: `value` is a `number` when it fits
|
|
414
|
+
* exactly (`≤ 2^53-1`, covering ids, u8..u32 and small u64s) and a `bigint`
|
|
415
|
+
* only beyond that, so the common case avoids a per-value bigint allocation.
|
|
416
|
+
*/
|
|
417
|
+
unsigned?(id: number, value: number | bigint): void;
|
|
418
|
+
/** A signed integer field. Number-first like {@link unsigned} (`|value| ≤ 2^53-1` ⇒ `number`). */
|
|
419
|
+
signed?(id: number, value: number | bigint): void;
|
|
420
|
+
/** An IEEE-754 32-bit float field. */
|
|
421
|
+
fp32?(id: number, value: number): void;
|
|
422
|
+
/** An IEEE-754 64-bit double field. */
|
|
423
|
+
fp64?(id: number, value: number): void;
|
|
424
|
+
/** A chunk of a UTF-8 string field. */
|
|
425
|
+
string?(id: number, total: number, offset: number, chunk: Uint8Array): void;
|
|
426
|
+
/** A chunk of a blob field. */
|
|
427
|
+
blob?(id: number, total: number, offset: number, chunk: Uint8Array): void;
|
|
428
|
+
/** Start of an array; `count` elements of `kind` follow. */
|
|
429
|
+
arrayBegin?(id: number, kind: ArrayKind, count: number): void;
|
|
430
|
+
/** One unsigned array element. Number-first like {@link unsigned}. */
|
|
431
|
+
arrayUnsigned?(id: number, index: number, value: number | bigint): void;
|
|
432
|
+
/** One signed array element. Number-first like {@link signed}. */
|
|
433
|
+
arraySigned?(id: number, index: number, value: number | bigint): void;
|
|
434
|
+
/** One fp32 array element. */
|
|
435
|
+
arrayFp32?(id: number, index: number, value: number): void;
|
|
436
|
+
/** One fp64 array element. */
|
|
437
|
+
arrayFp64?(id: number, index: number, value: number): void;
|
|
438
|
+
/** End of an array. */
|
|
439
|
+
arrayEnd?(id: number): void;
|
|
440
|
+
/**
|
|
441
|
+
* Start of a nested sequence. Return a {@link Visitor} to route the nested
|
|
442
|
+
* fields to it (its {@link Visitor.sequenceEnd} fires at the matching end);
|
|
443
|
+
* return nothing to keep using the current visitor.
|
|
444
|
+
*/
|
|
445
|
+
sequenceBegin?(id: number): Visitor | void;
|
|
446
|
+
/** End of the nested sequence this visitor was handling. */
|
|
447
|
+
sequenceEnd?(): void;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Push parser for the SofaBuffers wire format. Feed it bytes in chunks of any
|
|
451
|
+
* size with {@link IStream.feed} and it drives a {@link Visitor}, one call per
|
|
452
|
+
* decoded field, resuming cleanly across chunk boundaries. Call
|
|
453
|
+
* {@link IStream.end} after the final chunk to read whether the message finished
|
|
454
|
+
* on a field boundary. When the whole message is already in one buffer, prefer
|
|
455
|
+
* the faster {@link decode}.
|
|
456
|
+
*/
|
|
457
|
+
declare class IStream {
|
|
458
|
+
private readonly state;
|
|
459
|
+
/**
|
|
460
|
+
* @param limits Optional opt-in decode caps ({@link DecodeLimits}). An
|
|
461
|
+
* over-limit array count or string / blob length throws {@link SofabError}
|
|
462
|
+
* (`LIMIT_EXCEEDED`) from {@link feed}, at the offending field's header and
|
|
463
|
+
* before any of its payload is streamed to the visitor. Omit for no caps.
|
|
464
|
+
*/
|
|
465
|
+
constructor(limits?: DecodeLimits);
|
|
466
|
+
/**
|
|
467
|
+
* Feed a chunk of bytes, dispatching decoded fields to `visitor`. Throws
|
|
468
|
+
* {@link SofabError} (`INVALID_MSG`) only if the bytes are *malformed*;
|
|
469
|
+
* running out of bytes mid-field is not an error — it simply suspends until
|
|
470
|
+
* the next chunk (see {@link end}).
|
|
471
|
+
*/
|
|
472
|
+
feed(chunk: Uint8Array, visitor: Visitor): void;
|
|
473
|
+
/**
|
|
474
|
+
* Report whether the stream ended exactly at a field boundary. Call after the
|
|
475
|
+
* final {@link feed}: returns {@link DecodeStatus.Complete} at a clean field
|
|
476
|
+
* boundary, or {@link DecodeStatus.Incomplete} if the last chunk ended inside
|
|
477
|
+
* a field (a partial varint, an unfinished payload / array, or a still-open
|
|
478
|
+
* nested sequence).
|
|
479
|
+
*
|
|
480
|
+
* Per the finish-less spec (MESSAGE_SPEC §7) this is a pure accessor: it never
|
|
481
|
+
* throws and never promotes an incomplete decode to an error — the caller owns
|
|
482
|
+
* end-of-input and decides whether a trailing `Incomplete` is a truncation
|
|
483
|
+
* error. (A *malformed* message has already thrown from {@link feed}.)
|
|
484
|
+
*/
|
|
485
|
+
end(): DecodeStatus;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Decode a complete message held in one contiguous buffer, in a single call.
|
|
489
|
+
*
|
|
490
|
+
* This is the non-streaming convenience — and the fast path: with the whole
|
|
491
|
+
* message in hand it advances one cursor over the buffer instead of running the
|
|
492
|
+
* resumable per-byte state machine, so it is markedly faster than feeding the
|
|
493
|
+
* same bytes through
|
|
494
|
+
* {@link IStream}. Use {@link IStream} when the message arrives in chunks; use
|
|
495
|
+
* this when you already have it whole.
|
|
496
|
+
*
|
|
497
|
+
* The whole buffer *is* the end of input, so the two failure outcomes both
|
|
498
|
+
* throw a {@link SofabError} the caller tells apart by `code` (MESSAGE_SPEC §7):
|
|
499
|
+
* malformed input throws `INVALID_MSG`, while input that ends inside a field —
|
|
500
|
+
* truncation or an unclosed sequence — throws `INCOMPLETE`. A complete message
|
|
501
|
+
* returns normally.
|
|
502
|
+
*
|
|
503
|
+
* Pass `limits` ({@link DecodeLimits}) to cap array counts and string / blob
|
|
504
|
+
* lengths; an over-limit field throws `LIMIT_EXCEEDED` at its header, before it
|
|
505
|
+
* is materialized. Omit for no caps (the default).
|
|
506
|
+
*/
|
|
507
|
+
declare function decode(bytes: Uint8Array, visitor: Visitor, limits?: DecodeLimits): void;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* The pull / cursor decoder: a monomorphic companion to {@link "./fast"}.
|
|
511
|
+
*
|
|
512
|
+
* {@link "./fast"}'s {@link decodeContiguous} is a *push* decoder — it drives the
|
|
513
|
+
* buffer and calls a {@link Visitor} method per field. That is the right shape
|
|
514
|
+
* for streaming and skip-subtree callers, but the visitor call sites go
|
|
515
|
+
* megamorphic once a single decode routes through several differently-shaped
|
|
516
|
+
* visitor objects (one per nested message type), which a JIT cannot inline.
|
|
517
|
+
*
|
|
518
|
+
* {@link Cursor} inverts control: it keeps one read cursor over the contiguous
|
|
519
|
+
* {@link Uint8Array} and exposes *pull* primitives — {@link Cursor.readHeader}
|
|
520
|
+
* plus a typed `read*` per wire type — so **generated code drives the loop** with
|
|
521
|
+
* a single `switch (cursor.id)` that reads straight into its own fields. Every
|
|
522
|
+
* call site is then monomorphic (the generated per-type decoder is the only
|
|
523
|
+
* caller), which is what lets V8 inline the whole decode into a flat loop — the
|
|
524
|
+
* same technique protobuf's generated `decode(reader)` uses.
|
|
525
|
+
*
|
|
526
|
+
* It shares {@link "./fast"}'s number-first varint core verbatim: each varint is
|
|
527
|
+
* accumulated into two 32-bit JS *numbers* (`lo`/`hi`) and a `bigint` is
|
|
528
|
+
* materialised only for a 64-bit *value* that does not fit in `2^53-1` (never for
|
|
529
|
+
* ids, lengths or counts). String / blob payloads are returned as a single
|
|
530
|
+
* zero-copy `subarray` view. It reports the same three-valued outcome as the
|
|
531
|
+
* push path (MESSAGE_SPEC §7): malformed input throws a {@link SofabError} with
|
|
532
|
+
* code `INVALID_MSG`, and a read that runs off the end of the buffer mid-field
|
|
533
|
+
* throws `INCOMPLETE`.
|
|
534
|
+
*/
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* A pull decoder over a complete message held in one contiguous buffer.
|
|
538
|
+
*
|
|
539
|
+
* Usage from generated code: loop on {@link readHeader}; for each field switch on
|
|
540
|
+
* {@link id} and call the matching `read*` (which consumes that field's value and
|
|
541
|
+
* advances the cursor); recurse into a child type's decoder on a nested sequence;
|
|
542
|
+
* fall through to {@link skip} for an unknown id. See {@link readHeader}.
|
|
543
|
+
*
|
|
544
|
+
* Pass {@link DecodeLimits} to cap array counts and string / blob lengths; an
|
|
545
|
+
* over-limit field throws {@link SofabError} (`LIMIT_EXCEEDED`) at its header,
|
|
546
|
+
* before it is materialized. Omit for no caps (the default).
|
|
547
|
+
*/
|
|
548
|
+
declare class Cursor {
|
|
549
|
+
/** Field id of the header last accepted by {@link readHeader}. */
|
|
550
|
+
id: number;
|
|
551
|
+
/** Wire type of the header last accepted by {@link readHeader}. */
|
|
552
|
+
wire: number;
|
|
553
|
+
/**
|
|
554
|
+
* Fixlen subtype of the header last accepted by {@link readHeader} — one of
|
|
555
|
+
* {@link FixlenSubtype} — when its {@link wire} is {@link WireType.Fixlen} or
|
|
556
|
+
* {@link WireType.ArrayFixlen}; `-1` otherwise (a non-fixlen field, or a
|
|
557
|
+
* fixlen field whose subtype word is truncated away).
|
|
558
|
+
*
|
|
559
|
+
* The four fixlen subtypes (`fp32`, `fp64`, `string`, `blob`) all share one
|
|
560
|
+
* {@link wire} type, so {@link wire} alone cannot separate them. This is the
|
|
561
|
+
* companion accessor that can: a generated guard reads it right after
|
|
562
|
+
* {@link readHeader} to skip a field whose delivered subtype contradicts the
|
|
563
|
+
* schema (MESSAGE_SPEC §7.3), exactly as it already does on {@link wire} for
|
|
564
|
+
* the other kinds:
|
|
565
|
+
*
|
|
566
|
+
* ```ts
|
|
567
|
+
* case 9: if (c.wire !== WireType.Fixlen || c.fixSub !== FixlenSubtype.Fp64) {
|
|
568
|
+
* c.skip(c.wire); break;
|
|
569
|
+
* } o.somefp64 = c.readFp64(); break;
|
|
570
|
+
* ```
|
|
571
|
+
*
|
|
572
|
+
* It is *peeked* — the subtype word is not consumed — so the matching typed
|
|
573
|
+
* reader (or {@link skip}) still reads and validates it, and a malformed or
|
|
574
|
+
* truncated word surfaces `INVALID` / `INCOMPLETE` there as before.
|
|
575
|
+
*/
|
|
576
|
+
fixSub: number;
|
|
577
|
+
private readonly buf;
|
|
578
|
+
private readonly view;
|
|
579
|
+
private readonly n;
|
|
580
|
+
private p;
|
|
581
|
+
private readonly maxArrayCount;
|
|
582
|
+
private readonly maxStringLen;
|
|
583
|
+
private readonly maxBlobLen;
|
|
584
|
+
private lo;
|
|
585
|
+
private hi;
|
|
586
|
+
private depth;
|
|
587
|
+
constructor(buf: Uint8Array, limits?: DecodeLimits);
|
|
588
|
+
/**
|
|
589
|
+
* Advance to the next field header. Returns `true` and sets {@link id} /
|
|
590
|
+
* {@link wire} when a field follows; returns `false` — consuming the marker —
|
|
591
|
+
* at the end of the buffer *or* at the sequence-end that closes the sequence
|
|
592
|
+
* this decoder is reading. So a generated per-type decoder loops uniformly:
|
|
593
|
+
*
|
|
594
|
+
* ```ts
|
|
595
|
+
* while (c.readHeader()) {
|
|
596
|
+
* switch (c.id) {
|
|
597
|
+
* case 4: this.u32 = Number(c.readUnsigned()); break;
|
|
598
|
+
* case 10: this.child = Child.decodeFrom(c); break; // nested sequence
|
|
599
|
+
* default: c.skip(c.wire); break; // unknown field
|
|
600
|
+
* }
|
|
601
|
+
* }
|
|
602
|
+
* ```
|
|
603
|
+
*
|
|
604
|
+
* At the root the loop ends at end-of-buffer; inside a nested sequence it ends
|
|
605
|
+
* at the matching {@link WireType.SequenceEnd} (which is consumed). A field
|
|
606
|
+
* whose id is out of range throws {@link SofabError} (`INVALID_MSG`).
|
|
607
|
+
*/
|
|
608
|
+
readHeader(): boolean;
|
|
609
|
+
/** Read an unsigned scalar (wire {@link WireType.Unsigned}), number-first. */
|
|
610
|
+
readUnsigned(): number | bigint;
|
|
611
|
+
/** Read a signed scalar (wire {@link WireType.Signed}), zig-zag, number-first. */
|
|
612
|
+
readSigned(): number | bigint;
|
|
613
|
+
/** Read a 32-bit float scalar (wire {@link WireType.Fixlen}, subtype fp32). */
|
|
614
|
+
readFp32(): number;
|
|
615
|
+
/** Read a 64-bit float scalar (wire {@link WireType.Fixlen}, subtype fp64). */
|
|
616
|
+
readFp64(): number;
|
|
617
|
+
/** Read a UTF-8 string scalar (wire {@link WireType.Fixlen}, subtype string). */
|
|
618
|
+
readString(): string;
|
|
619
|
+
/**
|
|
620
|
+
* Read a blob scalar (wire {@link WireType.Fixlen}, subtype blob) as a
|
|
621
|
+
* zero-copy {@link Uint8Array} view into the source buffer.
|
|
622
|
+
*/
|
|
623
|
+
readBlob(): Uint8Array;
|
|
624
|
+
/** Read an unsigned array (wire {@link WireType.ArrayUnsigned}), number-first per element. */
|
|
625
|
+
readUnsignedArray(): (number | bigint)[];
|
|
626
|
+
/** Read a signed array (wire {@link WireType.ArraySigned}), zig-zag, number-first per element. */
|
|
627
|
+
readSignedArray(): (number | bigint)[];
|
|
628
|
+
/**
|
|
629
|
+
* Read an unsigned 64-bit array into {@link Long}[] — the `bigint`-free path.
|
|
630
|
+
* Each element keeps the raw lo/hi halves; call {@link Long.toBigInt} to
|
|
631
|
+
* materialise only the values the caller actually needs.
|
|
632
|
+
*/
|
|
633
|
+
readUnsignedArrayLong(): Long[];
|
|
634
|
+
/** Read a signed 64-bit array (zig-zag) into {@link Long}[] — the `bigint`-free path. */
|
|
635
|
+
readSignedArrayLong(): Long[];
|
|
636
|
+
/** Read an fp32 array (wire {@link WireType.ArrayFixlen}, element subtype fp32). */
|
|
637
|
+
readFp32Array(): number[];
|
|
638
|
+
/** Read an fp64 array (wire {@link WireType.ArrayFixlen}, element subtype fp64). */
|
|
639
|
+
readFp64Array(): number[];
|
|
640
|
+
/**
|
|
641
|
+
* Consume the value of the field whose header {@link readHeader} just accepted,
|
|
642
|
+
* discarding it — for a `default:` branch that keeps the cursor in sync on an
|
|
643
|
+
* unknown id. Pass {@link wire}. A {@link WireType.SequenceStart} skips the
|
|
644
|
+
* whole nested sequence.
|
|
645
|
+
*/
|
|
646
|
+
skip(wire: number): void;
|
|
647
|
+
private skipValue;
|
|
648
|
+
private skipSequence;
|
|
649
|
+
/**
|
|
650
|
+
* Peek the delivered fixlen subtype of the field {@link readHeader} just
|
|
651
|
+
* accepted, **without advancing the cursor** — the readers / {@link skip}
|
|
652
|
+
* still re-read and validate the word. Returns one of {@link FixlenSubtype}
|
|
653
|
+
* (0..3), a reserved value (4..7), or `-1` when the wire is not a fixlen kind
|
|
654
|
+
* or the subtype word is truncated away.
|
|
655
|
+
*
|
|
656
|
+
* The subtype is the low 3 bits of the fixlen sub-header word, and the low
|
|
657
|
+
* bits of a LEB128 word live entirely in its **first** byte — so this only
|
|
658
|
+
* reads one byte, it never decodes a varint.
|
|
659
|
+
*/
|
|
660
|
+
private peekFixSub;
|
|
661
|
+
/** Read and validate an array count word (0..ARRAY_MAX; §4.7/§4.8). */
|
|
662
|
+
private arrayCount;
|
|
663
|
+
/** Read a scalar fixlen sub-header, asserting subtype and exact byte length (floats). */
|
|
664
|
+
private fixlenHeader;
|
|
665
|
+
/** Read a scalar fixlen sub-header for a string/blob, asserting subtype; returns byte length. */
|
|
666
|
+
private fixlenLen;
|
|
667
|
+
/** Read an array fixlen element header (count + element type); returns the count. */
|
|
668
|
+
private arrayFixlenHeader;
|
|
669
|
+
/** Hand back a zero-copy view of the next `len` bytes, advancing the cursor. */
|
|
670
|
+
private take;
|
|
671
|
+
private rawFp32;
|
|
672
|
+
private rawFp64;
|
|
673
|
+
/**
|
|
674
|
+
* The last varint's full value as a `bigint` (64-bit fidelity). Only ever
|
|
675
|
+
* called from {@link unsignedValue} / {@link signedValue} on the `hi` overflow
|
|
676
|
+
* path (`this.hi >>> 0 > 0x1fffff`), so `hi` is always non-zero here.
|
|
677
|
+
*/
|
|
678
|
+
private big;
|
|
679
|
+
/**
|
|
680
|
+
* The last varint as an unsigned value, number-first: a `number` when it fits
|
|
681
|
+
* exactly (`≤ 2^53-1`), a `bigint` only beyond that.
|
|
682
|
+
*/
|
|
683
|
+
private unsignedValue;
|
|
684
|
+
/** The last zig-zag varint as a signed value, number-first. */
|
|
685
|
+
private signedValue;
|
|
686
|
+
/** The last varint's value as a JS number — exact for ids/lengths/counts. */
|
|
687
|
+
private num;
|
|
688
|
+
/** The last varint with its low 3 tag bits stripped (`value >> 3`). */
|
|
689
|
+
private upper;
|
|
690
|
+
/**
|
|
691
|
+
* Decode one LEB128 varint at the cursor into {@link lo} / {@link hi} (each an
|
|
692
|
+
* unsigned 32-bit half), advancing {@link p}. Throws on truncation or a value
|
|
693
|
+
* spilling past 64 bits (>10 bytes). Unrolled, number-only — no `bigint`.
|
|
694
|
+
*/
|
|
695
|
+
private readVarint;
|
|
696
|
+
private set;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* The acceleration seam.
|
|
701
|
+
*
|
|
702
|
+
* The encoder's bulk array paths run through a {@link Kernel} — a small set of
|
|
703
|
+
* self-contained, buffer-oriented transforms over a region the caller has
|
|
704
|
+
* already sized. The default {@link "./js"} kernel is pure TypeScript and works
|
|
705
|
+
* everywhere; a C++ (N-API) or WebAssembly build can implement the same
|
|
706
|
+
* interface and be swapped in with {@link setKernel} for a speed-up, with **no
|
|
707
|
+
* change to the public API**. The boundary is deliberately *bulk* (a whole
|
|
708
|
+
* array per call, into guaranteed capacity) so the cost of crossing into native
|
|
709
|
+
* code is amortised — never one call per element.
|
|
710
|
+
*/
|
|
711
|
+
/**
|
|
712
|
+
* Bulk, capacity-guaranteed transforms used on the encoder's fast path.
|
|
713
|
+
*
|
|
714
|
+
* Every method writes into `out` starting at `pos`, assuming the caller has
|
|
715
|
+
* already ensured enough room, and returns the position just past the last byte
|
|
716
|
+
* written. Headers, counts, flushing and validation stay in the stream classes;
|
|
717
|
+
* a kernel only moves bytes.
|
|
718
|
+
*/
|
|
719
|
+
interface Kernel {
|
|
720
|
+
/** A short identifier, surfaced in diagnostics and the parity tests. */
|
|
721
|
+
readonly name: string;
|
|
722
|
+
/** Encode each element as an unsigned varint. */
|
|
723
|
+
encodeUnsignedVarints(values: ArrayLike<number | bigint>, out: Uint8Array, pos: number): number;
|
|
724
|
+
/** Zig-zag then varint-encode each element. */
|
|
725
|
+
encodeSignedVarints(values: ArrayLike<number | bigint>, out: Uint8Array, pos: number): number;
|
|
726
|
+
/** Pack each element as a little-endian fp32 (4 bytes). */
|
|
727
|
+
packFp32Array(values: ArrayLike<number>, out: Uint8Array, pos: number): number;
|
|
728
|
+
/** Pack each element as a little-endian fp64 (8 bytes). */
|
|
729
|
+
packFp64Array(values: ArrayLike<number>, out: Uint8Array, pos: number): number;
|
|
730
|
+
}
|
|
731
|
+
/** Install `kernel` as the active acceleration backend. */
|
|
732
|
+
declare function setKernel(kernel: Kernel): void;
|
|
733
|
+
/** The currently active kernel (the JS kernel until something replaces it). */
|
|
734
|
+
declare function getKernel(): Kernel;
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* The default, pure-TypeScript {@link Kernel}.
|
|
738
|
+
*
|
|
739
|
+
* Always available — no native dependency, no WebAssembly — so SofaBuffers runs
|
|
740
|
+
* unchanged in Node.js, browsers, Electron and bundled builds. Importing this
|
|
741
|
+
* module registers it as the default kernel (idempotently); a native or WASM
|
|
742
|
+
* kernel can later override it via {@link setKernel}.
|
|
743
|
+
*/
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* The default, pure-TypeScript {@link Kernel} — active until {@link setKernel}
|
|
747
|
+
* installs another. It implements the bulk encoder transforms with no native or
|
|
748
|
+
* WebAssembly dependency, so it works in every environment.
|
|
749
|
+
*/
|
|
750
|
+
declare const jsKernel: Kernel;
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Attempt to load and install the native kernel.
|
|
754
|
+
*
|
|
755
|
+
* @returns `true` if a valid native kernel was installed, `false` if it could
|
|
756
|
+
* not be loaded (not on Node, addon not installed, or wrong shape). Never
|
|
757
|
+
* throws for a missing addon — the JS kernel remains the fallback.
|
|
758
|
+
*/
|
|
759
|
+
declare function loadNativeKernel(): Promise<boolean>;
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Optional WebAssembly acceleration loader.
|
|
763
|
+
*
|
|
764
|
+
* Unlike the native addon, a WASM kernel runs in the browser too. This module
|
|
765
|
+
* does nothing at import time; call {@link loadWasmKernel} with the compiled
|
|
766
|
+
* module's bytes (or a streaming source) to instantiate it and install it as
|
|
767
|
+
* the active {@link Kernel}. A real WASM build is shipped separately; this
|
|
768
|
+
* loader is the stable entry point the rest of the library is wired through.
|
|
769
|
+
*/
|
|
770
|
+
|
|
771
|
+
/** A factory the WASM glue exposes: given the instance exports, build a Kernel. */
|
|
772
|
+
type WasmKernelFactory = (exports: WebAssembly.Exports) => Kernel;
|
|
773
|
+
/**
|
|
774
|
+
* Instantiate a WASM module and install the kernel it produces.
|
|
775
|
+
*
|
|
776
|
+
* @param source compiled-module bytes, a `Response`/stream, or a ready module.
|
|
777
|
+
* @param factory wraps the instance exports into a {@link Kernel}.
|
|
778
|
+
* @returns `true` once installed; throws only if instantiation itself fails.
|
|
779
|
+
*/
|
|
780
|
+
declare function loadWasmKernel(source: BufferSource | Response | PromiseLike<Response> | WebAssembly.Module, factory: WasmKernelFactory, imports?: WebAssembly.Imports): Promise<boolean>;
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* The complete public surface of the library, re-exported by {@link "./index"}
|
|
784
|
+
* both as flat named exports and, aggregated, under the `sofab` namespace.
|
|
785
|
+
*/
|
|
786
|
+
|
|
787
|
+
declare const _public_API_VERSION: typeof API_VERSION;
|
|
788
|
+
declare const _public_ARRAY_MAX: typeof ARRAY_MAX;
|
|
789
|
+
type _public_ArrayKind = ArrayKind;
|
|
790
|
+
type _public_Cursor = Cursor;
|
|
791
|
+
declare const _public_Cursor: typeof Cursor;
|
|
792
|
+
type _public_DecodeLimits = DecodeLimits;
|
|
793
|
+
type _public_DecodeStatus = DecodeStatus;
|
|
794
|
+
declare const _public_FIXLEN_MAX: typeof FIXLEN_MAX;
|
|
795
|
+
type _public_FixlenSubtype = FixlenSubtype;
|
|
796
|
+
type _public_FlushSink = FlushSink;
|
|
797
|
+
declare const _public_I64_MAX: typeof I64_MAX;
|
|
798
|
+
declare const _public_I64_MIN: typeof I64_MIN;
|
|
799
|
+
declare const _public_ID_MAX: typeof ID_MAX;
|
|
800
|
+
type _public_IStream = IStream;
|
|
801
|
+
declare const _public_IStream: typeof IStream;
|
|
802
|
+
type _public_Kernel = Kernel;
|
|
803
|
+
type _public_Long = Long;
|
|
804
|
+
declare const _public_Long: typeof Long;
|
|
805
|
+
declare const _public_MAX_DEPTH: typeof MAX_DEPTH;
|
|
806
|
+
type _public_OStream = OStream;
|
|
807
|
+
declare const _public_OStream: typeof OStream;
|
|
808
|
+
type _public_SofabError = SofabError;
|
|
809
|
+
declare const _public_SofabError: typeof SofabError;
|
|
810
|
+
type _public_SofabErrorCode = SofabErrorCode;
|
|
811
|
+
declare const _public_U64_MAX: typeof U64_MAX;
|
|
812
|
+
type _public_Visitor = Visitor;
|
|
813
|
+
type _public_WasmKernelFactory = WasmKernelFactory;
|
|
814
|
+
type _public_WireType = WireType;
|
|
815
|
+
declare const _public_decode: typeof decode;
|
|
816
|
+
declare const _public_getKernel: typeof getKernel;
|
|
817
|
+
declare const _public_jsKernel: typeof jsKernel;
|
|
818
|
+
declare const _public_loadNativeKernel: typeof loadNativeKernel;
|
|
819
|
+
declare const _public_loadWasmKernel: typeof loadWasmKernel;
|
|
820
|
+
declare const _public_setKernel: typeof setKernel;
|
|
821
|
+
declare namespace _public {
|
|
822
|
+
export { _public_API_VERSION as API_VERSION, _public_ARRAY_MAX as ARRAY_MAX, type _public_ArrayKind as ArrayKind, _public_Cursor as Cursor, type _public_DecodeLimits as DecodeLimits, type _public_DecodeStatus as DecodeStatus, _public_FIXLEN_MAX as FIXLEN_MAX, type _public_FixlenSubtype as FixlenSubtype, type _public_FlushSink as FlushSink, _public_I64_MAX as I64_MAX, _public_I64_MIN as I64_MIN, _public_ID_MAX as ID_MAX, _public_IStream as IStream, type _public_Kernel as Kernel, _public_Long as Long, _public_MAX_DEPTH as MAX_DEPTH, _public_OStream as OStream, _public_SofabError as SofabError, type _public_SofabErrorCode as SofabErrorCode, _public_U64_MAX as U64_MAX, type _public_Visitor as Visitor, type _public_WasmKernelFactory as WasmKernelFactory, type _public_WireType as WireType, _public_decode as decode, _public_getKernel as getKernel, _public_jsKernel as jsKernel, _public_loadNativeKernel as loadNativeKernel, _public_loadWasmKernel as loadWasmKernel, _public_setKernel as setKernel };
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
export { API_VERSION, ARRAY_MAX, ArrayKind, Cursor, type DecodeLimits, DecodeStatus, FIXLEN_MAX, FixlenSubtype, type FlushSink, I64_MAX, I64_MIN, ID_MAX, IStream, type Kernel, Long, MAX_DEPTH, OStream, SofabError, SofabErrorCode, U64_MAX, type Visitor, type WasmKernelFactory, WireType, decode, getKernel, jsKernel, loadNativeKernel, loadWasmKernel, setKernel, _public as sofab };
|