@elaraai/east 1.0.44 → 1.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/containers/sortedmap.d.ts +22 -0
- package/dist/src/containers/sortedmap.d.ts.map +1 -1
- package/dist/src/containers/sortedmap.js +31 -2
- package/dist/src/containers/sortedmap.js.map +1 -1
- package/dist/src/containers/sortedset.d.ts +22 -0
- package/dist/src/containers/sortedset.d.ts.map +1 -1
- package/dist/src/containers/sortedset.js +31 -2
- package/dist/src/containers/sortedset.js.map +1 -1
- package/dist/src/expr/blob.d.ts +6 -1
- package/dist/src/expr/blob.d.ts.map +1 -1
- package/dist/src/expr/blob.js +6 -1
- package/dist/src/expr/blob.js.map +1 -1
- package/dist/src/expr/libs/blob.d.ts +10 -1
- package/dist/src/expr/libs/blob.d.ts.map +1 -1
- package/dist/src/expr/libs/blob.js +10 -1
- package/dist/src/expr/libs/blob.js.map +1 -1
- package/dist/src/fuzz.d.ts.map +1 -1
- package/dist/src/fuzz.js +11 -2
- package/dist/src/fuzz.js.map +1 -1
- package/dist/src/serialization/beast2/index.d.ts +76 -22
- package/dist/src/serialization/beast2/index.d.ts.map +1 -1
- package/dist/src/serialization/beast2/index.js +87 -720
- package/dist/src/serialization/beast2/index.js.map +1 -1
- package/dist/src/serialization/beast2/index.spec.js +147 -4
- package/dist/src/serialization/beast2/index.spec.js.map +1 -1
- package/dist/src/serialization/beast2/shared.d.ts +79 -0
- package/dist/src/serialization/beast2/shared.d.ts.map +1 -0
- package/dist/src/serialization/beast2/shared.js +143 -0
- package/dist/src/serialization/beast2/shared.js.map +1 -0
- package/dist/src/serialization/beast2/v4/container.d.ts +67 -0
- package/dist/src/serialization/beast2/v4/container.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v4/container.js +727 -0
- package/dist/src/serialization/beast2/v4/container.js.map +1 -0
- package/dist/src/serialization/beast2/{sourcemap-table.d.ts → v4/sourcemap-table.d.ts} +2 -2
- package/dist/src/serialization/beast2/v4/sourcemap-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/{sourcemap-table.js → v4/sourcemap-table.js} +2 -2
- package/dist/src/serialization/beast2/v4/sourcemap-table.js.map +1 -0
- package/dist/src/serialization/beast2/{string-table.d.ts → v4/string-table.d.ts} +1 -1
- package/dist/src/serialization/beast2/v4/string-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/{string-table.js → v4/string-table.js} +1 -1
- package/dist/src/serialization/beast2/v4/string-table.js.map +1 -0
- package/dist/src/serialization/beast2/{type-table.d.ts → v4/type-table.d.ts} +6 -4
- package/dist/src/serialization/beast2/v4/type-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/{type-table.js → v4/type-table.js} +103 -10
- package/dist/src/serialization/beast2/v4/type-table.js.map +1 -0
- package/dist/src/serialization/beast2/{value-table.d.ts → v4/value-table.d.ts} +2 -2
- package/dist/src/serialization/beast2/v4/value-table.d.ts.map +1 -0
- package/dist/src/serialization/beast2/{value-table.js → v4/value-table.js} +3 -3
- package/dist/src/serialization/beast2/v4/value-table.js.map +1 -0
- package/dist/src/serialization/beast2/v5/codec.d.ts +213 -0
- package/dist/src/serialization/beast2/v5/codec.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/codec.js +954 -0
- package/dist/src/serialization/beast2/v5/codec.js.map +1 -0
- package/dist/src/serialization/beast2/v5/deflate.d.ts +16 -0
- package/dist/src/serialization/beast2/v5/deflate.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/deflate.js +172 -0
- package/dist/src/serialization/beast2/v5/deflate.js.map +1 -0
- package/dist/src/serialization/beast2/v5/frames.d.ts +150 -0
- package/dist/src/serialization/beast2/v5/frames.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/frames.js +284 -0
- package/dist/src/serialization/beast2/v5/frames.js.map +1 -0
- package/dist/src/serialization/beast2/v5/index.spec.d.ts +6 -0
- package/dist/src/serialization/beast2/v5/index.spec.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/index.spec.js +484 -0
- package/dist/src/serialization/beast2/v5/index.spec.js.map +1 -0
- package/dist/src/serialization/beast2/v5/stream.d.ts +177 -0
- package/dist/src/serialization/beast2/v5/stream.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/stream.js +447 -0
- package/dist/src/serialization/beast2/v5/stream.js.map +1 -0
- package/dist/src/serialization/beast2/v5/type-section.d.ts +81 -0
- package/dist/src/serialization/beast2/v5/type-section.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/type-section.js +211 -0
- package/dist/src/serialization/beast2/v5/type-section.js.map +1 -0
- package/dist/src/serialization/beast2/version.d.ts +40 -0
- package/dist/src/serialization/beast2/version.d.ts.map +1 -0
- package/dist/src/serialization/beast2/version.js +23 -0
- package/dist/src/serialization/beast2/version.js.map +1 -0
- package/dist/src/serialization/index.d.ts +1 -1
- package/dist/src/serialization/index.d.ts.map +1 -1
- package/dist/src/serialization/index.js +1 -1
- package/dist/src/serialization/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/serialization/beast2/sourcemap-table.d.ts.map +0 -1
- package/dist/src/serialization/beast2/sourcemap-table.js.map +0 -1
- package/dist/src/serialization/beast2/string-table.d.ts.map +0 -1
- package/dist/src/serialization/beast2/string-table.js.map +0 -1
- package/dist/src/serialization/beast2/type-table.d.ts.map +0 -1
- package/dist/src/serialization/beast2/type-table.js.map +0 -1
- package/dist/src/serialization/beast2/value-table.d.ts.map +0 -1
- package/dist/src/serialization/beast2/value-table.js.map +0 -1
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Beast2 v4 container codec — the globally-sectioned format.
|
|
7
|
+
*
|
|
8
|
+
* File layout: magic(8) + type_table_section + string_table_section +
|
|
9
|
+
* source_map_section + value_table_section + value_stream.
|
|
10
|
+
* Type table: flat array of unique types, referenced by varint index.
|
|
11
|
+
* String table: flat array of unique strings, referenced by varint index.
|
|
12
|
+
* Value stream: type-driven positional encoding, no inline type tags.
|
|
13
|
+
* Functions: IR encoded directly — EastTypeType values are regular variants.
|
|
14
|
+
*
|
|
15
|
+
* See v4/SPEC.md for the wire specification. Version-agnostic entry points
|
|
16
|
+
* (magic dispatch across v4/v5) live in `../index.ts`.
|
|
17
|
+
*/
|
|
18
|
+
import { toEastTypeValue } from "../../../type_of_type.js";
|
|
19
|
+
import { isVariant, variant } from "../../../containers/variant.js";
|
|
20
|
+
import { BufferWriter, BufferReader } from "../../binary-utils.js";
|
|
21
|
+
import { ref } from "../../../containers/ref.js";
|
|
22
|
+
import { SortedSet } from "../../../containers/sortedset.js";
|
|
23
|
+
import { SortedMap } from "../../../containers/sortedmap.js";
|
|
24
|
+
import { compareFor } from "../../../comparison.js";
|
|
25
|
+
import { matrix } from "../../../containers/matrix.js";
|
|
26
|
+
import { EAST_IR_SYMBOL, EAST_CAPTURES_SYMBOL, EAST_SOURCE_MAP_SYMBOL } from "../../../compile.js";
|
|
27
|
+
import { InternalError } from "../../../error.js";
|
|
28
|
+
import { TypeTableBuilder, writeTypeTableSection, readTypeTableSection } from "./type-table.js";
|
|
29
|
+
import { writeStringTableSection, readStringTableSection } from "./string-table.js";
|
|
30
|
+
import { writeSourceMapSection, readSourceMapSection } from "./sourcemap-table.js";
|
|
31
|
+
import { buildValueTable, buildIndexMap, isMutableType, TAG_ARRAY, TAG_DICT, TAG_SET, TAG_REF } from "./value-table.js";
|
|
32
|
+
import { buildPlatformContext, describeNoIrValue, finishDecodedFunction, irTypeValue } from "../shared.js";
|
|
33
|
+
// v4: EastTypeType values are encoded as regular variants (no special type-table-index encoding).
|
|
34
|
+
// This ensures uniform encoding across value stream and value table, and simplifies multi-runtime implementations.
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Magic bytes (v4: last byte = 0x04)
|
|
37
|
+
// =============================================================================
|
|
38
|
+
export const MAGIC_BYTES = new Uint8Array([0x89, 0x45, 0x61, 0x73, 0x74, 0x0D, 0x0A, 0x04]);
|
|
39
|
+
function verifyMagic(data) {
|
|
40
|
+
if (data.length < 8) {
|
|
41
|
+
throw new Error(`Data too short for Beast2 format: ${data.length} bytes`);
|
|
42
|
+
}
|
|
43
|
+
for (let i = 0; i < 8; i++) {
|
|
44
|
+
if (data[i] !== MAGIC_BYTES[i]) {
|
|
45
|
+
if (i < 7)
|
|
46
|
+
throw new Error(`Invalid Beast2 magic at offset ${i}: expected 0x${MAGIC_BYTES[i].toString(16)}, got 0x${data[i].toString(16)}`);
|
|
47
|
+
throw new Error(`Unknown Beast2 version: 0x${data[i].toString(16)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Value encoder factory
|
|
53
|
+
// =============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Build a value encoder closure tree for the given type.
|
|
56
|
+
* The tree is built once and reused for every encode call.
|
|
57
|
+
*/
|
|
58
|
+
function buildEncoder(type, typeCtx = new Map()) {
|
|
59
|
+
switch (type.type) {
|
|
60
|
+
case "Never":
|
|
61
|
+
return () => { throw new Error("Cannot encode value of type Never"); };
|
|
62
|
+
case "Null":
|
|
63
|
+
return () => { };
|
|
64
|
+
case "Boolean":
|
|
65
|
+
return (value, writer) => writer.writeUint8(value ? 1 : 0);
|
|
66
|
+
case "Integer":
|
|
67
|
+
return (value, writer) => writer.writeZigzag(value);
|
|
68
|
+
case "Float":
|
|
69
|
+
return (value, writer) => writer.writeFloat64LE(value);
|
|
70
|
+
case "String":
|
|
71
|
+
return (value, writer, ctx) => {
|
|
72
|
+
let idx = ctx.stringTable.get(value);
|
|
73
|
+
if (idx === undefined) {
|
|
74
|
+
idx = ctx.stringTable.size;
|
|
75
|
+
ctx.stringTable.set(value, idx);
|
|
76
|
+
}
|
|
77
|
+
writer.writeVarint(idx);
|
|
78
|
+
};
|
|
79
|
+
case "DateTime":
|
|
80
|
+
return (value, writer) => writer.writeZigzag(BigInt(value.valueOf()));
|
|
81
|
+
case "Blob":
|
|
82
|
+
return (value, writer) => {
|
|
83
|
+
writer.writeVarint(value.length);
|
|
84
|
+
writer.writeBytes(value);
|
|
85
|
+
};
|
|
86
|
+
case "Ref":
|
|
87
|
+
case "Array":
|
|
88
|
+
case "Set":
|
|
89
|
+
case "Dict": {
|
|
90
|
+
// v4: all mutable containers are in the value table, encoded as varint(index)
|
|
91
|
+
return (value, writer, ctx) => {
|
|
92
|
+
const idx = ctx.indexMap.get(value);
|
|
93
|
+
if (idx === undefined)
|
|
94
|
+
throw new InternalError(`Mutable ${type.type} not found in value table during encode`);
|
|
95
|
+
writer.writeVarint(idx);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
case "Struct": {
|
|
99
|
+
const fieldEncoders = [];
|
|
100
|
+
const ret = (value, writer, ctx) => {
|
|
101
|
+
for (const [name, enc] of fieldEncoders)
|
|
102
|
+
enc(value[name], writer, ctx);
|
|
103
|
+
};
|
|
104
|
+
for (const { name, type: fieldType } of type.value) {
|
|
105
|
+
fieldEncoders.push([name, buildEncoder(fieldType, typeCtx)]);
|
|
106
|
+
}
|
|
107
|
+
return ret;
|
|
108
|
+
}
|
|
109
|
+
case "Variant": {
|
|
110
|
+
const caseEncoders = {};
|
|
111
|
+
const caseTags = {};
|
|
112
|
+
const ret = (value, writer, ctx) => {
|
|
113
|
+
writer.writeVarint(caseTags[value.type]);
|
|
114
|
+
caseEncoders[value.type](value.value, writer, ctx);
|
|
115
|
+
};
|
|
116
|
+
for (let i = 0; i < type.value.length; i++) {
|
|
117
|
+
const { name, type: caseType } = type.value[i];
|
|
118
|
+
caseTags[name] = i;
|
|
119
|
+
caseEncoders[name] = buildEncoder(caseType, typeCtx);
|
|
120
|
+
}
|
|
121
|
+
return ret;
|
|
122
|
+
}
|
|
123
|
+
case "Recursive": {
|
|
124
|
+
if (type.value.type === "wrapper") {
|
|
125
|
+
let inner;
|
|
126
|
+
const ret = (value, writer, ctx) => inner(value, writer, ctx);
|
|
127
|
+
typeCtx.set(type.value.value.id, ret);
|
|
128
|
+
inner = buildEncoder(type.value.value.inner, typeCtx);
|
|
129
|
+
return ret;
|
|
130
|
+
}
|
|
131
|
+
const target = typeCtx.get(type.value.value);
|
|
132
|
+
if (!target)
|
|
133
|
+
throw new InternalError("Recursive type context not found during encoder build");
|
|
134
|
+
return target;
|
|
135
|
+
}
|
|
136
|
+
case "Function":
|
|
137
|
+
case "AsyncFunction": {
|
|
138
|
+
// Build IR encoder in the same typeCtx so recursive types resolve correctly
|
|
139
|
+
const fnIrEncoder = buildEncoder(irTypeValue, typeCtx);
|
|
140
|
+
const captureEncoderCache = new Map();
|
|
141
|
+
return (value, writer, ctx) => {
|
|
142
|
+
const ir = value[EAST_IR_SYMBOL];
|
|
143
|
+
if (!ir)
|
|
144
|
+
throw new Error(`Cannot serialize function: no IR attached (${describeNoIrValue(value)})`);
|
|
145
|
+
fnIrEncoder(ir, writer, ctx);
|
|
146
|
+
const captures = value[EAST_CAPTURES_SYMBOL];
|
|
147
|
+
const captureList = ir.value.captures;
|
|
148
|
+
writer.writeVarint(captureList.length);
|
|
149
|
+
for (const captureVar of captureList) {
|
|
150
|
+
const name = captureVar.value.name;
|
|
151
|
+
const captureType = captureVar.value.type;
|
|
152
|
+
if (!captures)
|
|
153
|
+
throw new InternalError("Function has captures but no EAST_CAPTURES_SYMBOL");
|
|
154
|
+
const entry = captures[name];
|
|
155
|
+
if (!entry)
|
|
156
|
+
throw new InternalError(`Capture '${name}' not found`);
|
|
157
|
+
let enc = captureEncoderCache.get(captureType);
|
|
158
|
+
if (!enc) {
|
|
159
|
+
enc = buildEncoder(captureType, typeCtx);
|
|
160
|
+
captureEncoderCache.set(captureType, enc);
|
|
161
|
+
}
|
|
162
|
+
enc(entry.value, writer, ctx);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case "Vector":
|
|
167
|
+
return (value, writer) => {
|
|
168
|
+
writer.writeVarint(value.length);
|
|
169
|
+
writer.writeBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
170
|
+
};
|
|
171
|
+
case "Matrix":
|
|
172
|
+
return (value, writer) => {
|
|
173
|
+
writer.writeVarint(value.rows);
|
|
174
|
+
writer.writeVarint(value.cols);
|
|
175
|
+
writer.writeBytes(new Uint8Array(value.data.buffer, value.data.byteOffset, value.data.byteLength));
|
|
176
|
+
};
|
|
177
|
+
default:
|
|
178
|
+
throw new Error(`Unknown type: ${type.type}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// =============================================================================
|
|
182
|
+
// Value decoder factory
|
|
183
|
+
// =============================================================================
|
|
184
|
+
/**
|
|
185
|
+
* Build a value decoder closure tree for the given type.
|
|
186
|
+
* The tree is built once and reused for every decode call.
|
|
187
|
+
*/
|
|
188
|
+
function buildDecoder(type, typeCtx = new Map()) {
|
|
189
|
+
switch (type.type) {
|
|
190
|
+
case "Never":
|
|
191
|
+
return () => { throw new Error("Cannot decode value of type Never"); };
|
|
192
|
+
case "Null":
|
|
193
|
+
return () => null;
|
|
194
|
+
case "Boolean":
|
|
195
|
+
return (reader) => reader.readBoolean();
|
|
196
|
+
case "Integer":
|
|
197
|
+
return (reader) => reader.readZigzag();
|
|
198
|
+
case "Float":
|
|
199
|
+
return (reader) => reader.readFloat64LE();
|
|
200
|
+
case "String":
|
|
201
|
+
return (reader, ctx) => {
|
|
202
|
+
const idx = reader.readVarint();
|
|
203
|
+
if (idx >= ctx.stringTable.length)
|
|
204
|
+
throw new Error(`String table index ${idx} out of bounds (table has ${ctx.stringTable.length} entries)`);
|
|
205
|
+
return ctx.stringTable[idx];
|
|
206
|
+
};
|
|
207
|
+
case "DateTime":
|
|
208
|
+
return (reader) => new Date(Number(reader.readZigzag()));
|
|
209
|
+
case "Blob":
|
|
210
|
+
return (reader) => reader.readBytes(reader.readVarint());
|
|
211
|
+
case "Ref":
|
|
212
|
+
case "Array":
|
|
213
|
+
case "Set":
|
|
214
|
+
case "Dict": {
|
|
215
|
+
// v4: mutable containers are read as varint(table_index) — the actual
|
|
216
|
+
// content was decoded from the mutable_value_table_section.
|
|
217
|
+
return (reader, ctx) => {
|
|
218
|
+
const idx = reader.readVarint();
|
|
219
|
+
if (idx >= ctx.mutableValues.length)
|
|
220
|
+
throw new Error(`Invalid mutable value table index ${idx} (table has ${ctx.mutableValues.length} entries)`);
|
|
221
|
+
return ctx.mutableValues[idx];
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
case "Struct": {
|
|
225
|
+
const fields = type.value;
|
|
226
|
+
const names = [];
|
|
227
|
+
const decoders = [];
|
|
228
|
+
const ret = (reader, ctx) => {
|
|
229
|
+
const result = {};
|
|
230
|
+
for (let i = 0; i < names.length; i++)
|
|
231
|
+
result[names[i]] = decoders[i](reader, ctx);
|
|
232
|
+
return result;
|
|
233
|
+
};
|
|
234
|
+
for (const { name, type: fieldType } of fields) {
|
|
235
|
+
names.push(name);
|
|
236
|
+
decoders.push(buildDecoder(fieldType, typeCtx));
|
|
237
|
+
}
|
|
238
|
+
return ret;
|
|
239
|
+
}
|
|
240
|
+
case "Variant": {
|
|
241
|
+
const caseDecoders = [];
|
|
242
|
+
const ret = (reader, ctx) => {
|
|
243
|
+
const tagIndex = reader.readVarint();
|
|
244
|
+
if (tagIndex >= caseDecoders.length)
|
|
245
|
+
throw new Error(`Invalid variant tag ${tagIndex}`);
|
|
246
|
+
const [caseName, caseDec] = caseDecoders[tagIndex];
|
|
247
|
+
return variant(caseName, caseDec(reader, ctx));
|
|
248
|
+
};
|
|
249
|
+
for (const { name, type: caseType } of type.value) {
|
|
250
|
+
caseDecoders.push([name, buildDecoder(caseType, typeCtx)]);
|
|
251
|
+
}
|
|
252
|
+
return ret;
|
|
253
|
+
}
|
|
254
|
+
case "Recursive": {
|
|
255
|
+
if (type.value.type === "wrapper") {
|
|
256
|
+
let inner;
|
|
257
|
+
const ret = (reader, ctx) => inner(reader, ctx);
|
|
258
|
+
typeCtx.set(type.value.value.id, ret);
|
|
259
|
+
inner = buildDecoder(type.value.value.inner, typeCtx);
|
|
260
|
+
return ret;
|
|
261
|
+
}
|
|
262
|
+
const target = typeCtx.get(type.value.value);
|
|
263
|
+
if (!target)
|
|
264
|
+
throw new InternalError("Recursive type context not found during decoder build");
|
|
265
|
+
return target;
|
|
266
|
+
}
|
|
267
|
+
case "Function":
|
|
268
|
+
case "AsyncFunction": {
|
|
269
|
+
const isAsync = type.type === "AsyncFunction";
|
|
270
|
+
const fnType = type;
|
|
271
|
+
const fnIrDecoder = buildDecoder(irTypeValue, typeCtx);
|
|
272
|
+
const captureDecoderCache = new Map();
|
|
273
|
+
return (reader, ctx) => {
|
|
274
|
+
const ir = fnIrDecoder(reader, ctx);
|
|
275
|
+
if (ir.type !== (isAsync ? "AsyncFunction" : "Function")) {
|
|
276
|
+
throw new Error(`Expected ${fnType.type} IR, got ${ir.type}`);
|
|
277
|
+
}
|
|
278
|
+
// Decode captures
|
|
279
|
+
const captureCount = reader.readVarint();
|
|
280
|
+
if (captureCount !== ir.value.captures.length) {
|
|
281
|
+
throw new Error(`Capture count mismatch: IR has ${ir.value.captures.length}, data has ${captureCount}`);
|
|
282
|
+
}
|
|
283
|
+
const captureContext = {};
|
|
284
|
+
const typeContext = {};
|
|
285
|
+
for (const captureVar of ir.value.captures) {
|
|
286
|
+
const name = captureVar.value.name;
|
|
287
|
+
const captureType = captureVar.value.type;
|
|
288
|
+
// Get or build cached decoder for this capture type
|
|
289
|
+
let dec = captureDecoderCache.get(captureType);
|
|
290
|
+
if (!dec) {
|
|
291
|
+
dec = buildDecoder(captureType, typeCtx);
|
|
292
|
+
captureDecoderCache.set(captureType, dec);
|
|
293
|
+
}
|
|
294
|
+
const captureValue = dec(reader, ctx);
|
|
295
|
+
captureContext[name] = captureVar.value.mutable
|
|
296
|
+
? variant("boxed", captureValue)
|
|
297
|
+
: variant("value", captureValue);
|
|
298
|
+
typeContext[name] = captureType;
|
|
299
|
+
}
|
|
300
|
+
return finishDecodedFunction(ir, isAsync, captureContext, typeContext, ctx, ctx.sourceMap);
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
case "Vector": {
|
|
304
|
+
const elemType = type.value.type;
|
|
305
|
+
const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
|
|
306
|
+
return (reader) => {
|
|
307
|
+
const len = reader.readVarint();
|
|
308
|
+
const raw = new Uint8Array(reader.readBytesView(len * bpe));
|
|
309
|
+
if (elemType === "Float")
|
|
310
|
+
return new Float64Array(raw.buffer, 0, len);
|
|
311
|
+
if (elemType === "Integer")
|
|
312
|
+
return new BigInt64Array(raw.buffer, 0, len);
|
|
313
|
+
return new Uint8ClampedArray(raw.buffer, 0, len);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
case "Matrix": {
|
|
317
|
+
const elemType = type.value.type;
|
|
318
|
+
const bpe = elemType === "Float" ? 8 : elemType === "Integer" ? 8 : 1;
|
|
319
|
+
return (reader) => {
|
|
320
|
+
const rows = reader.readVarint();
|
|
321
|
+
const cols = reader.readVarint();
|
|
322
|
+
const raw = new Uint8Array(reader.readBytesView(rows * cols * bpe));
|
|
323
|
+
if (elemType === "Float")
|
|
324
|
+
return matrix(new Float64Array(raw.buffer, 0, rows * cols), rows, cols);
|
|
325
|
+
if (elemType === "Integer")
|
|
326
|
+
return matrix(new BigInt64Array(raw.buffer, 0, rows * cols), rows, cols);
|
|
327
|
+
return matrix(new Uint8ClampedArray(raw.buffer, 0, rows * cols), rows, cols);
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
default:
|
|
331
|
+
throw new Error(`Unknown type: ${type.type}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// =============================================================================
|
|
335
|
+
// Mutable value table section (v4)
|
|
336
|
+
// =============================================================================
|
|
337
|
+
function writeValueTableEntry(entry, writer, ctx, encTypeCtx) {
|
|
338
|
+
const type = entry.type;
|
|
339
|
+
const value = entry.value;
|
|
340
|
+
// Write kind tag
|
|
341
|
+
writer.writeUint8(entry.kind);
|
|
342
|
+
// Element encoder — handles both inline (non-mutable) and table-ref (mutable) cases
|
|
343
|
+
const encodeElem = (elemType, val, w, c) => {
|
|
344
|
+
if (isMutableType(elemType)) {
|
|
345
|
+
const idx = c.indexMap.get(val);
|
|
346
|
+
if (idx === undefined)
|
|
347
|
+
throw new InternalError("Nested mutable value not in value table");
|
|
348
|
+
w.writeVarint(idx);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
buildEncoder(elemType, encTypeCtx)(val, w, c);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
switch (type.type) {
|
|
355
|
+
case "Array": {
|
|
356
|
+
if (!ctx.typeTable.has(type.value))
|
|
357
|
+
ctx.typeTable.addETV(type.value);
|
|
358
|
+
writer.writeVarint(ctx.typeTable.indexOf(type.value));
|
|
359
|
+
writer.writeVarint(value.length);
|
|
360
|
+
for (const item of value)
|
|
361
|
+
encodeElem(type.value, item, writer, ctx);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case "Set": {
|
|
365
|
+
if (!ctx.typeTable.has(type.value))
|
|
366
|
+
ctx.typeTable.addETV(type.value);
|
|
367
|
+
writer.writeVarint(ctx.typeTable.indexOf(type.value));
|
|
368
|
+
writer.writeVarint(value.size);
|
|
369
|
+
for (const key of value)
|
|
370
|
+
encodeElem(type.value, key, writer, ctx);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "Dict": {
|
|
374
|
+
if (!ctx.typeTable.has(type.value.key))
|
|
375
|
+
ctx.typeTable.addETV(type.value.key);
|
|
376
|
+
if (!ctx.typeTable.has(type.value.value))
|
|
377
|
+
ctx.typeTable.addETV(type.value.value);
|
|
378
|
+
writer.writeVarint(ctx.typeTable.indexOf(type.value.key));
|
|
379
|
+
writer.writeVarint(ctx.typeTable.indexOf(type.value.value));
|
|
380
|
+
writer.writeVarint(value.size);
|
|
381
|
+
for (const [k, v] of value) {
|
|
382
|
+
encodeElem(type.value.key, k, writer, ctx);
|
|
383
|
+
encodeElem(type.value.value, v, writer, ctx);
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case "Ref": {
|
|
388
|
+
if (!ctx.typeTable.has(type.value))
|
|
389
|
+
ctx.typeTable.addETV(type.value);
|
|
390
|
+
writer.writeVarint(ctx.typeTable.indexOf(type.value));
|
|
391
|
+
encodeElem(type.value, value.value, writer, ctx);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/** Built decoder trees, keyed on the decoded type table itself.
|
|
397
|
+
*
|
|
398
|
+
* These trees were rebuilt on EVERY decode, which for a large recursive
|
|
399
|
+
* schema is the dominant cost of decoding at all: a UIComponentType blob
|
|
400
|
+
* (1127 type-table entries, 5 recursive wrappers) spent ~200 ms per decode
|
|
401
|
+
* with 86% of it in buildDecoder plus the GC churn from the closures it
|
|
402
|
+
* allocates — while east-c, whose decoder is a switch over types and
|
|
403
|
+
* allocates nothing, decoded the same blob in 0.04 ms.
|
|
404
|
+
*
|
|
405
|
+
* Decoders take their DecodeContext as a parameter and close over nothing
|
|
406
|
+
* per-decode, so they are safe to share. The key is the type-table array,
|
|
407
|
+
* which the section skip-cache (#417) returns as one frozen instance per
|
|
408
|
+
* distinct schema — so repeat decodes of a schema hit this cache, and a
|
|
409
|
+
* genuinely new schema simply builds once. */
|
|
410
|
+
const tableDecoderCache = new WeakMap();
|
|
411
|
+
function decodersForTypeTable(typeTable) {
|
|
412
|
+
const cached = tableDecoderCache.get(typeTable);
|
|
413
|
+
if (cached)
|
|
414
|
+
return cached;
|
|
415
|
+
const decTypeCtx = new Map();
|
|
416
|
+
for (const t of typeTable) {
|
|
417
|
+
if (t.type === "Recursive" && t.value?.type === "wrapper") {
|
|
418
|
+
buildDecoder(t, decTypeCtx);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const entry = { decTypeCtx, decoderCache: new Map() };
|
|
422
|
+
tableDecoderCache.set(typeTable, entry);
|
|
423
|
+
return entry;
|
|
424
|
+
}
|
|
425
|
+
function readMutableValueTableSection(reader, ctx) {
|
|
426
|
+
const sectionByteLength = reader.readVarint();
|
|
427
|
+
const sectionEnd = reader.offset + sectionByteLength;
|
|
428
|
+
const entryCount = reader.readVarint();
|
|
429
|
+
if (entryCount === 0) {
|
|
430
|
+
if (reader.offset !== sectionEnd)
|
|
431
|
+
throw new Error(`Value table section size mismatch`);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
// Two-pass decode: pre-allocate all containers, then fill elements.
|
|
435
|
+
// Each entry is prefixed with varint(byte_length), enabling trivial skip in pass 1.
|
|
436
|
+
// Pass 1: read entry headers, pre-allocate empty containers, skip element data by byte length
|
|
437
|
+
const entryOffsets = new Array(entryCount);
|
|
438
|
+
const entryLengths = new Array(entryCount);
|
|
439
|
+
for (let i = 0; i < entryCount; i++) {
|
|
440
|
+
const entryByteLength = reader.readVarint();
|
|
441
|
+
const entryStart = reader.offset;
|
|
442
|
+
const kindTag = reader.readUint8();
|
|
443
|
+
switch (kindTag) {
|
|
444
|
+
case TAG_ARRAY: {
|
|
445
|
+
reader.readVarint(); // elem type idx (skip)
|
|
446
|
+
const count = reader.readVarint();
|
|
447
|
+
ctx.mutableValues.push(new Array(count));
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case TAG_SET: {
|
|
451
|
+
// Sets and Dicts are ORDERED by East's total order — a decoded one
|
|
452
|
+
// must be a SortedSet/SortedMap, not a plain Set/Map. A plain
|
|
453
|
+
// container preserves wire order instead, which (a) violates the
|
|
454
|
+
// ordering invariant every East-created collection holds (see
|
|
455
|
+
// compile.ts) and (b) made TS re-encode a value to different bytes
|
|
456
|
+
// than east-c, whose btrees always sort. That broke cross-runtime
|
|
457
|
+
// byte parity, and with it content-addressing.
|
|
458
|
+
const setElemIdx = reader.readVarint();
|
|
459
|
+
reader.readVarint(); // count (skip — SortedSet doesn't pre-allocate)
|
|
460
|
+
ctx.mutableValues.push(new SortedSet(undefined, compareFor(ctx.typeTable[setElemIdx])));
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case TAG_DICT: {
|
|
464
|
+
const dictKeyIdx = reader.readVarint();
|
|
465
|
+
reader.readVarint(); // val type idx (skip)
|
|
466
|
+
reader.readVarint(); // count (skip)
|
|
467
|
+
ctx.mutableValues.push(new SortedMap(undefined, compareFor(ctx.typeTable[dictKeyIdx])));
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
case TAG_REF: {
|
|
471
|
+
reader.readVarint(); // inner type idx (skip)
|
|
472
|
+
ctx.mutableValues.push(ref(undefined));
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
default:
|
|
476
|
+
throw new Error(`Unknown value table kind tag 0x${kindTag.toString(16)}`);
|
|
477
|
+
}
|
|
478
|
+
entryOffsets[i] = entryStart;
|
|
479
|
+
entryLengths[i] = entryByteLength;
|
|
480
|
+
// Skip to next entry using byte length
|
|
481
|
+
reader.offset = entryStart + entryByteLength;
|
|
482
|
+
}
|
|
483
|
+
if (reader.offset !== sectionEnd) {
|
|
484
|
+
throw new Error(`Value table section size mismatch: expected ${sectionEnd}, got ${reader.offset}`);
|
|
485
|
+
}
|
|
486
|
+
// Decoder trees for the decoded type table, shared across decodes of the
|
|
487
|
+
// same schema (see decodersForTypeTable).
|
|
488
|
+
const { decTypeCtx, decoderCache } = decodersForTypeTable(ctx.typeTable);
|
|
489
|
+
const getCachedDecoder = (typeIdx) => {
|
|
490
|
+
let dec = decoderCache.get(typeIdx);
|
|
491
|
+
if (!dec) {
|
|
492
|
+
dec = buildDecoder(ctx.typeTable[typeIdx], decTypeCtx);
|
|
493
|
+
decoderCache.set(typeIdx, dec);
|
|
494
|
+
}
|
|
495
|
+
return dec;
|
|
496
|
+
};
|
|
497
|
+
// Pass 2: fill elements in REVERSE order — children (higher indices) before parents.
|
|
498
|
+
// The walker produces parents-before-children, so reversing ensures a parent's
|
|
499
|
+
// element decoders (e.g., Function compilers) see fully-populated child entries.
|
|
500
|
+
for (let i = entryCount - 1; i >= 0; i--) {
|
|
501
|
+
const r = new BufferReader(reader.buffer, entryOffsets[i]);
|
|
502
|
+
const kindTag = r.readUint8();
|
|
503
|
+
switch (kindTag) {
|
|
504
|
+
case TAG_ARRAY: {
|
|
505
|
+
const elemTypeIdx = r.readVarint();
|
|
506
|
+
const elemType = ctx.typeTable[elemTypeIdx];
|
|
507
|
+
const count = r.readVarint();
|
|
508
|
+
const arr = ctx.mutableValues[i];
|
|
509
|
+
const isMut = isMutableType(elemType);
|
|
510
|
+
const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
|
|
511
|
+
for (let j = 0; j < count; j++) {
|
|
512
|
+
arr[j] = isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx);
|
|
513
|
+
}
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case TAG_SET: {
|
|
517
|
+
const elemTypeIdx = r.readVarint();
|
|
518
|
+
const elemType = ctx.typeTable[elemTypeIdx];
|
|
519
|
+
const count = r.readVarint();
|
|
520
|
+
const set = ctx.mutableValues[i];
|
|
521
|
+
const isMut = isMutableType(elemType);
|
|
522
|
+
const dec = isMut ? null : getCachedDecoder(elemTypeIdx);
|
|
523
|
+
for (let j = 0; j < count; j++) {
|
|
524
|
+
set.add(isMut ? ctx.mutableValues[r.readVarint()] : dec(r, ctx));
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case TAG_DICT: {
|
|
529
|
+
const keyTypeIdx = r.readVarint();
|
|
530
|
+
const valTypeIdx = r.readVarint();
|
|
531
|
+
const keyType = ctx.typeTable[keyTypeIdx];
|
|
532
|
+
const valType = ctx.typeTable[valTypeIdx];
|
|
533
|
+
const count = r.readVarint();
|
|
534
|
+
const map = ctx.mutableValues[i];
|
|
535
|
+
const keyMut = isMutableType(keyType);
|
|
536
|
+
const valMut = isMutableType(valType);
|
|
537
|
+
const keyDec = keyMut ? null : getCachedDecoder(keyTypeIdx);
|
|
538
|
+
const valDec = valMut ? null : getCachedDecoder(valTypeIdx);
|
|
539
|
+
for (let j = 0; j < count; j++) {
|
|
540
|
+
const k = keyMut ? ctx.mutableValues[r.readVarint()] : keyDec(r, ctx);
|
|
541
|
+
const v = valMut ? ctx.mutableValues[r.readVarint()] : valDec(r, ctx);
|
|
542
|
+
map.set(k, v);
|
|
543
|
+
}
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case TAG_REF: {
|
|
547
|
+
const innerTypeIdx = r.readVarint();
|
|
548
|
+
const innerType = ctx.typeTable[innerTypeIdx];
|
|
549
|
+
const rv = ctx.mutableValues[i];
|
|
550
|
+
if (isMutableType(innerType)) {
|
|
551
|
+
rv.value = ctx.mutableValues[r.readVarint()];
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
rv.value = getCachedDecoder(innerTypeIdx)(r, ctx);
|
|
555
|
+
}
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const baseTypeTableCache = new WeakMap();
|
|
562
|
+
function baseTypeTableFor(type, eastType, typeValue) {
|
|
563
|
+
const cached = baseTypeTableCache.get(type);
|
|
564
|
+
if (cached)
|
|
565
|
+
return cached;
|
|
566
|
+
const builder = new TypeTableBuilder();
|
|
567
|
+
const rootIdx = eastType ? builder.add(eastType) : builder.add(typeValue);
|
|
568
|
+
const sw = new BufferWriter();
|
|
569
|
+
writeTypeTableSection(rootIdx, builder.entries, sw);
|
|
570
|
+
const entry = { builder, rootIdx, sectionBytes: sw.toUint8Array() };
|
|
571
|
+
baseTypeTableCache.set(type, entry);
|
|
572
|
+
return entry;
|
|
573
|
+
}
|
|
574
|
+
export function encodeBeast2V4For(type, options) {
|
|
575
|
+
const eastType = isVariant(type) ? undefined : type;
|
|
576
|
+
const typeValue = isVariant(type) ? type : toEastTypeValue(type);
|
|
577
|
+
// Build encoder tree once at setup — save the typeCtx for reuse by value table entries
|
|
578
|
+
const setupTypeCtx = new Map();
|
|
579
|
+
const valueEncoder = buildEncoder(typeValue, setupTypeCtx);
|
|
580
|
+
// Root-closure type table (stable across encode calls AND closures — #417).
|
|
581
|
+
const base = baseTypeTableFor(type, eastType, typeValue);
|
|
582
|
+
const baseRootIdx = base.rootIdx;
|
|
583
|
+
const baseEntryCount = base.builder.entries.length;
|
|
584
|
+
return (value) => {
|
|
585
|
+
const builder = base.builder.clone();
|
|
586
|
+
const stringTable = new Map();
|
|
587
|
+
// Build the mutable value table by walking the value graph.
|
|
588
|
+
// The walk also discovers the source map from any function in the value graph.
|
|
589
|
+
const vtResult = buildValueTable(value, typeValue, builder);
|
|
590
|
+
const vtEntries = vtResult.entries;
|
|
591
|
+
const indexMap = buildIndexMap(vtEntries);
|
|
592
|
+
// Source map: explicit option > root value > discovered during walk > null
|
|
593
|
+
const sourceMap = options?.sourceMap
|
|
594
|
+
?? value?.[EAST_SOURCE_MAP_SYMBOL]
|
|
595
|
+
?? vtResult.sourceMap
|
|
596
|
+
?? null;
|
|
597
|
+
const ctx = { indexMap, typeTable: builder, stringTable };
|
|
598
|
+
// Encode value table entries FIRST — this discovers types and strings
|
|
599
|
+
// from inside mutable containers (which are varint refs in the value stream)
|
|
600
|
+
const vtWriter = new BufferWriter();
|
|
601
|
+
vtWriter.writeVarint(vtEntries.length);
|
|
602
|
+
for (const entry of vtEntries) {
|
|
603
|
+
// Buffer each entry, then write varint(byte_length) + entry bytes
|
|
604
|
+
const entryWriter = new BufferWriter();
|
|
605
|
+
writeValueTableEntry(entry, entryWriter, ctx, setupTypeCtx);
|
|
606
|
+
const entryBytes = entryWriter.toUint8Array();
|
|
607
|
+
vtWriter.writeVarint(entryBytes.length);
|
|
608
|
+
vtWriter.writeBytes(entryBytes);
|
|
609
|
+
}
|
|
610
|
+
// Encode the value stream (mutable containers are written as varint indices)
|
|
611
|
+
const valueWriter = new BufferWriter();
|
|
612
|
+
valueEncoder(value, valueWriter, ctx);
|
|
613
|
+
// Source map filenames must be added to stringTable before writing string table section
|
|
614
|
+
const smWriter = new BufferWriter();
|
|
615
|
+
writeSourceMapSection(sourceMap, stringTable, smWriter);
|
|
616
|
+
// Write the final blob: magic + type table + string table + source map + value table + value data
|
|
617
|
+
const writer = new BufferWriter();
|
|
618
|
+
writer.writeBytes(MAGIC_BYTES);
|
|
619
|
+
if (builder.entries.length === baseEntryCount) {
|
|
620
|
+
// The value walk added no types — the section is the cached root
|
|
621
|
+
// closure, byte for byte (#417).
|
|
622
|
+
writer.writeBytes(base.sectionBytes);
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
writeTypeTableSection(baseRootIdx, builder.entries, writer);
|
|
626
|
+
}
|
|
627
|
+
writeStringTableSection(stringTable, writer);
|
|
628
|
+
writer.writeBytes(smWriter.toUint8Array());
|
|
629
|
+
// Write value table section with byte-length prefix
|
|
630
|
+
const vtBytes = vtWriter.toUint8Array();
|
|
631
|
+
writer.writeVarint(vtBytes.length);
|
|
632
|
+
writer.writeBytes(vtBytes);
|
|
633
|
+
writer.writeBytes(valueWriter.toUint8Array());
|
|
634
|
+
return writer.toUint8Array();
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
export function decodeBeast2V4For(type, options) {
|
|
638
|
+
if (!isVariant(type))
|
|
639
|
+
type = toEastTypeValue(type);
|
|
640
|
+
const typeValue = type;
|
|
641
|
+
// Pre-build value decoder once (reused on every decode call)
|
|
642
|
+
const valueDecoder = buildDecoder(typeValue);
|
|
643
|
+
const platformCtx = buildPlatformContext(options);
|
|
644
|
+
return (data) => {
|
|
645
|
+
verifyMagic(data);
|
|
646
|
+
const reader = new BufferReader(data, MAGIC_BYTES.length);
|
|
647
|
+
const { typeTable } = readTypeTableSection(reader);
|
|
648
|
+
const stringTable = readStringTableSection(reader);
|
|
649
|
+
const sourceMap = readSourceMapSection(reader, stringTable);
|
|
650
|
+
const ctx = {
|
|
651
|
+
mutableValues: [],
|
|
652
|
+
typeTable,
|
|
653
|
+
stringTable,
|
|
654
|
+
sourceMap,
|
|
655
|
+
...platformCtx,
|
|
656
|
+
};
|
|
657
|
+
readMutableValueTableSection(reader, ctx);
|
|
658
|
+
const value = valueDecoder(reader, ctx);
|
|
659
|
+
if (reader.offset !== data.length) {
|
|
660
|
+
throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
|
|
661
|
+
}
|
|
662
|
+
return value;
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
// =============================================================================
|
|
666
|
+
// Public API — IR + source map (v4 container)
|
|
667
|
+
// =============================================================================
|
|
668
|
+
/**
|
|
669
|
+
* Decodes a v4 IRType blob and returns both the IR value and the decoded
|
|
670
|
+
* source map. Backs the version-agnostic `decodeEastIR` / `decodeAsyncEastIR`.
|
|
671
|
+
*
|
|
672
|
+
* @param data - a v4 beast2 blob whose root is a Function/AsyncFunction IR
|
|
673
|
+
* @returns the decoded IR value and the blob's source map (or `null`)
|
|
674
|
+
*/
|
|
675
|
+
export function decodeIRWithSourceMapV4(data) {
|
|
676
|
+
verifyMagic(data);
|
|
677
|
+
const reader = new BufferReader(data, MAGIC_BYTES.length);
|
|
678
|
+
const { typeTable } = readTypeTableSection(reader);
|
|
679
|
+
const stringTable = readStringTableSection(reader);
|
|
680
|
+
const sourceMap = readSourceMapSection(reader, stringTable);
|
|
681
|
+
const valueDecoder = buildDecoder(irTypeValue);
|
|
682
|
+
const ctx = {
|
|
683
|
+
mutableValues: [],
|
|
684
|
+
typeTable,
|
|
685
|
+
stringTable,
|
|
686
|
+
sourceMap,
|
|
687
|
+
...buildPlatformContext(),
|
|
688
|
+
};
|
|
689
|
+
readMutableValueTableSection(reader, ctx);
|
|
690
|
+
const ir = valueDecoder(reader, ctx);
|
|
691
|
+
if (reader.offset !== data.length) {
|
|
692
|
+
throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
|
|
693
|
+
}
|
|
694
|
+
return { ir, sourceMap };
|
|
695
|
+
}
|
|
696
|
+
// =============================================================================
|
|
697
|
+
// Public API — Decode (v4 container, self-describing)
|
|
698
|
+
// =============================================================================
|
|
699
|
+
/**
|
|
700
|
+
* Decodes a self-describing v4 blob using its embedded root type.
|
|
701
|
+
*
|
|
702
|
+
* @param data - a v4 beast2 blob
|
|
703
|
+
* @param options - decode options (platform functions for decoded functions)
|
|
704
|
+
* @returns the blob's root type and decoded value
|
|
705
|
+
*/
|
|
706
|
+
export function decodeBeast2V4(data, options) {
|
|
707
|
+
verifyMagic(data);
|
|
708
|
+
const reader = new BufferReader(data, MAGIC_BYTES.length);
|
|
709
|
+
const { rootType, typeTable } = readTypeTableSection(reader);
|
|
710
|
+
const stringTable = readStringTableSection(reader);
|
|
711
|
+
const sourceMap = readSourceMapSection(reader, stringTable);
|
|
712
|
+
const valueDecoder = buildDecoder(rootType);
|
|
713
|
+
const ctx = {
|
|
714
|
+
mutableValues: [],
|
|
715
|
+
typeTable,
|
|
716
|
+
stringTable,
|
|
717
|
+
sourceMap,
|
|
718
|
+
...buildPlatformContext(options),
|
|
719
|
+
};
|
|
720
|
+
readMutableValueTableSection(reader, ctx);
|
|
721
|
+
const value = valueDecoder(reader, ctx);
|
|
722
|
+
if (reader.offset !== data.length) {
|
|
723
|
+
throw new Error(`${data.length - reader.offset} trailing bytes at offset ${reader.offset}`);
|
|
724
|
+
}
|
|
725
|
+
return { type: rootType, value };
|
|
726
|
+
}
|
|
727
|
+
//# sourceMappingURL=container.js.map
|