@dusted/anqst 1.5.0 → 1.6.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.
Files changed (84) hide show
  1. package/README.md +45 -4
  2. package/dist/src/app.js +78 -24
  3. package/dist/src/base93.js +0 -72
  4. package/dist/src/boundary-codec-analysis.js +468 -0
  5. package/dist/src/boundary-codec-leaves.js +602 -0
  6. package/dist/src/boundary-codec-model.js +77 -0
  7. package/dist/src/boundary-codec-plan.js +522 -0
  8. package/dist/src/boundary-codec-render.js +1738 -0
  9. package/dist/src/boundary-codecs.js +174 -0
  10. package/dist/src/emit.js +1960 -207
  11. package/dist/src/layout.js +9 -3
  12. package/dist/src/program.js +1 -1
  13. package/dist/src/project.js +3 -3
  14. package/package.json +2 -2
  15. package/spec/AnQst-Spec-DSL.d.ts +22 -24
  16. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/decoder.js +0 -35
  17. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/encoder.js +0 -36
  18. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/decoder.js +0 -26
  19. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/encoder.js +0 -38
  20. package/dist/src/codecgenerators/basecodecemitters/binary-blob/decoder.js +0 -28
  21. package/dist/src/codecgenerators/basecodecemitters/binary-blob/encoder.js +0 -34
  22. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/decoder.js +0 -29
  23. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/encoder.js +0 -36
  24. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/decoder.js +0 -46
  25. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/encoder.js +0 -49
  26. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/decoder.js +0 -46
  27. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/encoder.js +0 -47
  28. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/decoder.js +0 -46
  29. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/encoder.js +0 -49
  30. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/decoder.js +0 -50
  31. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/encoder.js +0 -52
  32. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/decoder.js +0 -38
  33. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/encoder.js +0 -44
  34. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/decoder.js +0 -33
  35. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/encoder.js +0 -34
  36. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/decoder.js +0 -46
  37. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/encoder.js +0 -49
  38. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/decoder.js +0 -46
  39. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/encoder.js +0 -49
  40. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/decoder.js +0 -28
  41. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/encoder.js +0 -34
  42. package/dist/src/codecgenerators/basecodecemitters/boolean/decoder.js +0 -34
  43. package/dist/src/codecgenerators/basecodecemitters/boolean/encoder.js +0 -40
  44. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/decoder.js +0 -43
  45. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/encoder.js +0 -45
  46. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/decoder.js +0 -44
  47. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/encoder.js +0 -46
  48. package/dist/src/codecgenerators/basecodecemitters/integer-int16/decoder.js +0 -32
  49. package/dist/src/codecgenerators/basecodecemitters/integer-int16/encoder.js +0 -43
  50. package/dist/src/codecgenerators/basecodecemitters/integer-int32/decoder.js +0 -26
  51. package/dist/src/codecgenerators/basecodecemitters/integer-int32/encoder.js +0 -37
  52. package/dist/src/codecgenerators/basecodecemitters/integer-int8/decoder.js +0 -26
  53. package/dist/src/codecgenerators/basecodecemitters/integer-int8/encoder.js +0 -37
  54. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/decoder.js +0 -36
  55. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/encoder.js +0 -36
  56. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/decoder.js +0 -25
  57. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/encoder.js +0 -36
  58. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/decoder.js +0 -36
  59. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/encoder.js +0 -36
  60. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/decoder.js +0 -26
  61. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/encoder.js +0 -38
  62. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/decoder.js +0 -27
  63. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/encoder.js +0 -39
  64. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/decoder.js +0 -26
  65. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/encoder.js +0 -38
  66. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/decoder.js +0 -30
  67. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/encoder.js +0 -42
  68. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/decoder.js +0 -31
  69. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/encoder.js +0 -43
  70. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/decoder.js +0 -30
  71. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/encoder.js +0 -40
  72. package/dist/src/codecgenerators/basecodecemitters/number/decoder.js +0 -26
  73. package/dist/src/codecgenerators/basecodecemitters/number/encoder.js +0 -38
  74. package/dist/src/codecgenerators/basecodecemitters/shared/comments.js +0 -13
  75. package/dist/src/codecgenerators/basecodecemitters/shared/contracts.js +0 -2
  76. package/dist/src/codecgenerators/basecodecemitters/shared/fixedwidth.js +0 -53
  77. package/dist/src/codecgenerators/basecodecemitters/shared/index.js +0 -21
  78. package/dist/src/codecgenerators/basecodecemitters/shared/positionalBase93.js +0 -48
  79. package/dist/src/codecgenerators/basecodecemitters/shared/rawbytes.js +0 -30
  80. package/dist/src/codecgenerators/basecodecemitters/string/decoder.js +0 -43
  81. package/dist/src/codecgenerators/basecodecemitters/string/encoder.js +0 -43
  82. package/dist/src/codecgenerators/basecodecemitters/stringArray/decoder.js +0 -80
  83. package/dist/src/codecgenerators/basecodecemitters/stringArray/encoder.js +0 -57
  84. package/dist/src/structured-top-level-codecs.js +0 -1305
@@ -0,0 +1,522 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildBoundaryCodecPlan = buildBoundaryCodecPlan;
4
+ const boundary_codec_model_1 = require("./boundary-codec-model");
5
+ class BoundaryPlanBuilder {
6
+ constructor(codecId, analysis) {
7
+ this.codecId = codecId;
8
+ this.analysis = analysis;
9
+ this.nextIdValue = 0;
10
+ this.blobEntries = [];
11
+ this.itemEntries = [];
12
+ this.usedScalarLeafKinds = new Set();
13
+ this.usedBinaryLeafKinds = new Set();
14
+ this.namedNodes = new Map();
15
+ this.namedShapes = new Map();
16
+ this.tsScalarEncodeHelpers = new Set();
17
+ this.tsScalarDecodeHelpers = new Set();
18
+ this.tsBinaryEncodeHelpers = new Set();
19
+ this.tsBinaryDecodeHelpers = new Set();
20
+ this.tsFiniteDomainEncodeHelpers = new Set();
21
+ this.tsFiniteDomainDecodeHelpers = new Set();
22
+ this.cppScalarEncodeHelpers = new Set();
23
+ this.cppScalarDecodeHelpers = new Set();
24
+ this.cppBinaryEncodeHelpers = new Set();
25
+ this.cppBinaryDecodeHelpers = new Set();
26
+ this.cppFiniteDomainEncodeHelpers = new Set();
27
+ this.cppFiniteDomainDecodeHelpers = new Set();
28
+ this.usesArrayCounts = false;
29
+ this.usesOptionalPresence = false;
30
+ this.usesFiniteDomainCodes = false;
31
+ this.nextItemOrder = 0;
32
+ }
33
+ build() {
34
+ const root = this.buildNode(this.analysis.root, { isRoot: true }).node;
35
+ const requirements = this.buildRequirements();
36
+ return {
37
+ codecId: this.codecId,
38
+ typeText: this.analysis.typeText,
39
+ tsTypeText: this.analysis.tsTypeText,
40
+ decodePolicy: "trusted-only",
41
+ analysis: this.analysis,
42
+ root,
43
+ blobEntries: this.blobEntries,
44
+ itemEntries: this.itemEntries,
45
+ requirements
46
+ };
47
+ }
48
+ buildRequirements() {
49
+ const hasBlob = this.blobEntries.length > 0;
50
+ const itemKinds = [...new Set(this.itemEntries.map((entry) => entry.itemKind))];
51
+ const sortedScalarKinds = (values) => [...values].sort();
52
+ const sortedBinaryKinds = (values) => [...values].sort();
53
+ const sortedStrings = (values) => [...values].sort();
54
+ const helperRequirements = (scalarEncodeKinds, scalarDecodeKinds, binaryEncodeKinds, binaryDecodeKinds, finiteDomainEncodeHelpers, finiteDomainDecodeHelpers) => ({
55
+ scalarEncodeKinds: sortedScalarKinds(scalarEncodeKinds),
56
+ scalarDecodeKinds: sortedScalarKinds(scalarDecodeKinds),
57
+ binaryEncodeKinds: sortedBinaryKinds(binaryEncodeKinds),
58
+ binaryDecodeKinds: sortedBinaryKinds(binaryDecodeKinds),
59
+ finiteDomainEncodeHelpers: sortedStrings(finiteDomainEncodeHelpers),
60
+ finiteDomainDecodeHelpers: sortedStrings(finiteDomainDecodeHelpers)
61
+ });
62
+ return {
63
+ hasBlob,
64
+ hasItems: this.itemEntries.length > 0,
65
+ itemKinds,
66
+ itemCountHeaderKinds: [],
67
+ usesArrayCounts: this.usesArrayCounts,
68
+ usesOptionalPresence: this.usesOptionalPresence,
69
+ usesFiniteDomainCodes: this.usesFiniteDomainCodes,
70
+ usedScalarLeafKinds: [...this.usedScalarLeafKinds].sort(),
71
+ usedBinaryLeafKinds: [...this.usedBinaryLeafKinds].sort(),
72
+ tsHelperRequirements: helperRequirements(this.tsScalarEncodeHelpers, this.tsScalarDecodeHelpers, this.tsBinaryEncodeHelpers, this.tsBinaryDecodeHelpers, this.tsFiniteDomainEncodeHelpers, this.tsFiniteDomainDecodeHelpers),
73
+ cppHelperRequirements: helperRequirements(this.cppScalarEncodeHelpers, this.cppScalarDecodeHelpers, this.cppBinaryEncodeHelpers, this.cppBinaryDecodeHelpers, this.cppFiniteDomainEncodeHelpers, this.cppFiniteDomainDecodeHelpers)
74
+ };
75
+ }
76
+ nextId(prefix) {
77
+ this.nextIdValue += 1;
78
+ return `${this.codecId}_${prefix}_${this.nextIdValue}`;
79
+ }
80
+ addBlobEntry(role, path, widthBytes, leafKind, logicalKind) {
81
+ const entryId = this.nextId(role === "leaf" ? "blob" : role.replace(/-/g, "_"));
82
+ this.blobEntries.push({
83
+ entryId,
84
+ role,
85
+ path,
86
+ widthBytes,
87
+ leafKind,
88
+ logicalKind
89
+ });
90
+ this.usedScalarLeafKinds.add(leafKind);
91
+ return entryId;
92
+ }
93
+ addItemEntry(itemKind, path, logicalKind) {
94
+ const entryId = this.nextId(itemKind);
95
+ this.nextItemOrder += 1;
96
+ const entry = { entryId, itemKind, path, logicalKind, order: this.nextItemOrder };
97
+ this.itemEntries.push(entry);
98
+ return entryId;
99
+ }
100
+ static emptyShape() {
101
+ return {
102
+ fixedBlobWidthBytes: 0,
103
+ fixedItemCount: 0,
104
+ hasItems: false,
105
+ itemKinds: new Set(),
106
+ hasVariableExtent: false
107
+ };
108
+ }
109
+ static recursiveNamedShape() {
110
+ return {
111
+ fixedBlobWidthBytes: null,
112
+ fixedItemCount: null,
113
+ hasItems: false,
114
+ itemKinds: new Set(),
115
+ hasVariableExtent: true
116
+ };
117
+ }
118
+ mergeShapes(left, right) {
119
+ const fixedBlobWidthBytes = left.fixedBlobWidthBytes !== null &&
120
+ right.fixedBlobWidthBytes !== null &&
121
+ !left.hasVariableExtent &&
122
+ !right.hasVariableExtent
123
+ ? left.fixedBlobWidthBytes + right.fixedBlobWidthBytes
124
+ : null;
125
+ const fixedItemCount = left.fixedItemCount !== null &&
126
+ right.fixedItemCount !== null &&
127
+ !left.hasVariableExtent &&
128
+ !right.hasVariableExtent
129
+ ? left.fixedItemCount + right.fixedItemCount
130
+ : null;
131
+ return {
132
+ fixedBlobWidthBytes,
133
+ fixedItemCount,
134
+ hasItems: left.hasItems || right.hasItems,
135
+ itemKinds: new Set([...left.itemKinds, ...right.itemKinds]),
136
+ hasVariableExtent: left.hasVariableExtent || right.hasVariableExtent
137
+ };
138
+ }
139
+ chooseLeafPacking(node, isRoot) {
140
+ void isRoot;
141
+ if (node.leaf.region === "blob")
142
+ return "byte-packed";
143
+ if (node.leaf.region === "binary")
144
+ return "binary-packed";
145
+ if (node.leaf.region === "dynamic")
146
+ return "dynamic";
147
+ return "text-packed";
148
+ }
149
+ loweringSelection(mode, reason, helperNameHint) {
150
+ return { mode, reason, helperNameHint };
151
+ }
152
+ defaultInlineLeafLowering() {
153
+ return {
154
+ tsEncode: this.loweringSelection("inline", "trivial-op"),
155
+ tsDecode: this.loweringSelection("inline", "trivial-op"),
156
+ cppEncode: this.loweringSelection("inline", "trivial-op"),
157
+ cppDecode: this.loweringSelection("inline", "trivial-op")
158
+ };
159
+ }
160
+ finiteDomainHelperHint(node) {
161
+ const joined = node.path.join("_");
162
+ return (0, boundary_codec_model_1.sanitizeIdentifier)(`finite_domain_${joined.length > 0 ? joined : node.typeIdentityKey}`);
163
+ }
164
+ chooseLeafLowering(node, selectedPacking, _context) {
165
+ if (node.leaf.region === "binary" && selectedPacking === "binary-packed") {
166
+ const helperNameHint = (0, boundary_codec_model_1.sanitizeIdentifier)(`binary_${String(node.leaf.key)}`);
167
+ return {
168
+ tsEncode: this.loweringSelection("helper-call", "complex-op", helperNameHint),
169
+ tsDecode: this.loweringSelection("helper-call", "complex-op", helperNameHint),
170
+ cppEncode: this.loweringSelection("helper-call", "complex-op", helperNameHint),
171
+ cppDecode: this.loweringSelection("helper-call", "complex-op", helperNameHint)
172
+ };
173
+ }
174
+ return this.defaultInlineLeafLowering();
175
+ }
176
+ chooseFiniteDomainLowering(node, representation, _context) {
177
+ if (representation.kind === "coded-scalar" && node.domain.variants.length > 64) {
178
+ const helperNameHint = this.finiteDomainHelperHint(node);
179
+ return {
180
+ tsEncode: this.loweringSelection("helper-call", "code-size", helperNameHint),
181
+ tsDecode: this.loweringSelection("helper-call", "code-size", helperNameHint),
182
+ cppEncode: this.loweringSelection("helper-call", "code-size", helperNameHint),
183
+ cppDecode: this.loweringSelection("helper-call", "code-size", helperNameHint)
184
+ };
185
+ }
186
+ return this.defaultInlineLeafLowering();
187
+ }
188
+ registerLeafLoweringRequirements(node, lowering) {
189
+ const scalarLeaf = node.leaf.key;
190
+ const binaryLeaf = node.leaf.key;
191
+ if (node.leaf.region === "blob") {
192
+ if (lowering.tsEncode.mode === "helper-call")
193
+ this.tsScalarEncodeHelpers.add(scalarLeaf);
194
+ if (lowering.tsDecode.mode === "helper-call")
195
+ this.tsScalarDecodeHelpers.add(scalarLeaf);
196
+ if (lowering.cppEncode.mode === "helper-call")
197
+ this.cppScalarEncodeHelpers.add(scalarLeaf);
198
+ if (lowering.cppDecode.mode === "helper-call")
199
+ this.cppScalarDecodeHelpers.add(scalarLeaf);
200
+ return;
201
+ }
202
+ if (node.leaf.region === "binary") {
203
+ if (lowering.tsEncode.mode === "helper-call")
204
+ this.tsBinaryEncodeHelpers.add(binaryLeaf);
205
+ if (lowering.tsDecode.mode === "helper-call")
206
+ this.tsBinaryDecodeHelpers.add(binaryLeaf);
207
+ if (lowering.cppEncode.mode === "helper-call")
208
+ this.cppBinaryEncodeHelpers.add(binaryLeaf);
209
+ if (lowering.cppDecode.mode === "helper-call")
210
+ this.cppBinaryDecodeHelpers.add(binaryLeaf);
211
+ }
212
+ }
213
+ registerFiniteDomainLoweringRequirements(representation, lowering) {
214
+ if (representation.kind === "coded-scalar") {
215
+ if (lowering.tsEncode.mode === "helper-call")
216
+ this.tsScalarEncodeHelpers.add(representation.scalarKind);
217
+ if (lowering.tsDecode.mode === "helper-call")
218
+ this.tsScalarDecodeHelpers.add(representation.scalarKind);
219
+ if (lowering.cppEncode.mode === "helper-call")
220
+ this.cppScalarEncodeHelpers.add(representation.scalarKind);
221
+ if (lowering.cppDecode.mode === "helper-call")
222
+ this.cppScalarDecodeHelpers.add(representation.scalarKind);
223
+ }
224
+ if (lowering.tsEncode.mode === "helper-call" && lowering.tsEncode.helperNameHint) {
225
+ this.tsFiniteDomainEncodeHelpers.add(lowering.tsEncode.helperNameHint);
226
+ }
227
+ if (lowering.tsDecode.mode === "helper-call" && lowering.tsDecode.helperNameHint) {
228
+ this.tsFiniteDomainDecodeHelpers.add(lowering.tsDecode.helperNameHint);
229
+ }
230
+ if (lowering.cppEncode.mode === "helper-call" && lowering.cppEncode.helperNameHint) {
231
+ this.cppFiniteDomainEncodeHelpers.add(lowering.cppEncode.helperNameHint);
232
+ }
233
+ if (lowering.cppDecode.mode === "helper-call" && lowering.cppDecode.helperNameHint) {
234
+ this.cppFiniteDomainDecodeHelpers.add(lowering.cppDecode.helperNameHint);
235
+ }
236
+ }
237
+ chooseFiniteDomainScalar(variantCount) {
238
+ if (variantCount <= 0xff)
239
+ return "uint8";
240
+ if (variantCount <= 0xffff)
241
+ return "uint16";
242
+ return "uint32";
243
+ }
244
+ chooseRootArrayExtentStrategy(nodeShape, isRoot) {
245
+ if (!isRoot || nodeShape.hasVariableExtent)
246
+ return "explicit-count";
247
+ if (nodeShape.fixedBlobWidthBytes !== null && nodeShape.fixedBlobWidthBytes > 0)
248
+ return "blob-tail";
249
+ if (nodeShape.fixedItemCount !== null && nodeShape.fixedItemCount > 0)
250
+ return "item-tail";
251
+ return "explicit-count";
252
+ }
253
+ buildNode(node, context) {
254
+ switch (node.nodeKind) {
255
+ case "leaf":
256
+ return this.buildLeafNode(node, context);
257
+ case "finite-domain":
258
+ return this.buildFiniteDomainNode(node, context);
259
+ case "array":
260
+ return this.buildArrayNode(node, context);
261
+ case "struct":
262
+ return this.buildStructNode(node, context);
263
+ case "named":
264
+ return this.buildNamedNode(node);
265
+ }
266
+ }
267
+ buildNamedNode(node) {
268
+ const existing = this.namedNodes.get(node.name);
269
+ if (existing) {
270
+ return {
271
+ node: existing,
272
+ shape: this.namedShapes.get(node.name) ?? BoundaryPlanBuilder.recursiveNamedShape()
273
+ };
274
+ }
275
+ const placeholder = {
276
+ nodeKind: "named",
277
+ typeText: node.typeText,
278
+ path: node.path,
279
+ typeIdentityKey: node.typeIdentityKey,
280
+ cppNameHintParts: node.cppNameHintParts,
281
+ name: node.name,
282
+ target: null
283
+ };
284
+ this.namedNodes.set(node.name, placeholder);
285
+ const targetResult = this.buildNode(node.target, { isRoot: false });
286
+ placeholder.target = targetResult.node;
287
+ this.namedShapes.set(node.name, targetResult.shape);
288
+ return {
289
+ node: placeholder,
290
+ shape: targetResult.shape
291
+ };
292
+ }
293
+ buildLeafNode(node, context) {
294
+ const selectedPacking = this.chooseLeafPacking(node, context.isRoot);
295
+ const lowering = this.chooseLeafLowering(node, selectedPacking, context);
296
+ this.registerLeafLoweringRequirements(node, lowering);
297
+ if (node.leaf.region === "blob" && selectedPacking === "byte-packed") {
298
+ const widthBytes = node.leaf.fixedByteWidth ?? 0;
299
+ const blobEntryId = this.addBlobEntry("leaf", node.path, widthBytes, node.leaf.key, node.leaf.logicalKind);
300
+ return {
301
+ node: {
302
+ nodeKind: "leaf",
303
+ typeText: node.typeText,
304
+ path: node.path,
305
+ typeIdentityKey: node.typeIdentityKey,
306
+ cppNameHintParts: node.cppNameHintParts,
307
+ leaf: node.leaf,
308
+ selectedPacking,
309
+ lowering,
310
+ blobEntryId
311
+ },
312
+ shape: {
313
+ fixedBlobWidthBytes: widthBytes,
314
+ fixedItemCount: 0,
315
+ hasItems: false,
316
+ itemKinds: new Set(),
317
+ hasVariableExtent: false
318
+ }
319
+ };
320
+ }
321
+ if (node.leaf.region === "binary") {
322
+ this.usedBinaryLeafKinds.add(node.leaf.key);
323
+ return {
324
+ node: {
325
+ nodeKind: "leaf",
326
+ typeText: node.typeText,
327
+ path: node.path,
328
+ typeIdentityKey: node.typeIdentityKey,
329
+ cppNameHintParts: node.cppNameHintParts,
330
+ leaf: node.leaf,
331
+ selectedPacking,
332
+ lowering,
333
+ itemEntryId: this.addItemEntry("binary", node.path, node.leaf.logicalKind)
334
+ },
335
+ shape: {
336
+ fixedBlobWidthBytes: 0,
337
+ fixedItemCount: 1,
338
+ hasItems: true,
339
+ itemKinds: new Set(["binary"]),
340
+ hasVariableExtent: false
341
+ }
342
+ };
343
+ }
344
+ const itemKind = node.leaf.region === "dynamic" ? "dynamic" : "string";
345
+ return {
346
+ node: {
347
+ nodeKind: "leaf",
348
+ typeText: node.typeText,
349
+ path: node.path,
350
+ typeIdentityKey: node.typeIdentityKey,
351
+ cppNameHintParts: node.cppNameHintParts,
352
+ leaf: node.leaf,
353
+ selectedPacking,
354
+ lowering,
355
+ itemEntryId: this.addItemEntry(itemKind, node.path, node.leaf.logicalKind)
356
+ },
357
+ shape: {
358
+ fixedBlobWidthBytes: 0,
359
+ fixedItemCount: 1,
360
+ hasItems: true,
361
+ itemKinds: new Set([itemKind]),
362
+ hasVariableExtent: false
363
+ }
364
+ };
365
+ }
366
+ buildFiniteDomainNode(node, context) {
367
+ if (context.isRoot && node.domain.primitive === "boolean") {
368
+ const representation = { kind: "identity-text" };
369
+ const lowering = this.chooseFiniteDomainLowering(node, representation, context);
370
+ this.registerFiniteDomainLoweringRequirements(representation, lowering);
371
+ return {
372
+ node: {
373
+ nodeKind: "finite-domain",
374
+ typeText: node.typeText,
375
+ path: node.path,
376
+ typeIdentityKey: node.typeIdentityKey,
377
+ cppNameHintParts: node.cppNameHintParts,
378
+ domain: node.domain,
379
+ representation,
380
+ lowering,
381
+ itemEntryId: this.addItemEntry("string", node.path, `finite-domain-${node.domain.primitive}`)
382
+ },
383
+ shape: {
384
+ fixedBlobWidthBytes: 0,
385
+ fixedItemCount: 1,
386
+ hasItems: true,
387
+ itemKinds: new Set(["string"]),
388
+ hasVariableExtent: false
389
+ }
390
+ };
391
+ }
392
+ const scalarKind = this.chooseFiniteDomainScalar(node.domain.variants.length);
393
+ this.usesFiniteDomainCodes = true;
394
+ const widthBytes = scalarKind === "uint8" ? 1 : scalarKind === "uint16" ? 2 : 4;
395
+ const representation = { kind: "coded-scalar", scalarKind };
396
+ const lowering = this.chooseFiniteDomainLowering(node, representation, context);
397
+ this.registerFiniteDomainLoweringRequirements(representation, lowering);
398
+ const blobEntryId = this.addBlobEntry("finite-domain-code", node.path, widthBytes, scalarKind, `finite-domain-${node.domain.primitive}`);
399
+ return {
400
+ node: {
401
+ nodeKind: "finite-domain",
402
+ typeText: node.typeText,
403
+ path: node.path,
404
+ typeIdentityKey: node.typeIdentityKey,
405
+ cppNameHintParts: node.cppNameHintParts,
406
+ domain: node.domain,
407
+ representation,
408
+ lowering,
409
+ blobEntryId
410
+ },
411
+ shape: {
412
+ fixedBlobWidthBytes: widthBytes,
413
+ fixedItemCount: 0,
414
+ hasItems: false,
415
+ itemKinds: new Set(),
416
+ hasVariableExtent: false
417
+ }
418
+ };
419
+ }
420
+ buildArrayNode(node, context) {
421
+ const elementResult = this.buildNode(node.element, { isRoot: false });
422
+ const extentStrategy = this.chooseRootArrayExtentStrategy(elementResult.shape, context.isRoot);
423
+ const countEntryId = extentStrategy === "explicit-count"
424
+ ? (() => {
425
+ this.usesArrayCounts = true;
426
+ return this.addBlobEntry("array-count", node.path, 4, "uint32", "array-count");
427
+ })()
428
+ : undefined;
429
+ return {
430
+ node: {
431
+ nodeKind: "array",
432
+ typeText: node.typeText,
433
+ path: node.path,
434
+ typeIdentityKey: node.typeIdentityKey,
435
+ cppNameHintParts: node.cppNameHintParts,
436
+ extentStrategy,
437
+ countEntryId,
438
+ elementBlobWidthBytes: elementResult.shape.fixedBlobWidthBytes ?? undefined,
439
+ elementItemCount: elementResult.shape.fixedItemCount ?? undefined,
440
+ element: elementResult.node
441
+ },
442
+ shape: {
443
+ fixedBlobWidthBytes: null,
444
+ fixedItemCount: null,
445
+ hasItems: elementResult.shape.hasItems,
446
+ itemKinds: new Set(elementResult.shape.itemKinds),
447
+ hasVariableExtent: true
448
+ }
449
+ };
450
+ }
451
+ shouldTailOptimizeStruct(node) {
452
+ if (node.fields.length < 2)
453
+ return false;
454
+ if (this.analysis.root !== node)
455
+ return false;
456
+ let candidates = 0;
457
+ for (const field of node.fields) {
458
+ if (field.optional || field.node.nodeKind !== "array")
459
+ continue;
460
+ const elementNode = field.node.element;
461
+ if (elementNode.nodeKind !== "leaf" && elementNode.nodeKind !== "finite-domain")
462
+ continue;
463
+ candidates += 1;
464
+ }
465
+ return candidates === 1;
466
+ }
467
+ buildStructNode(node, context) {
468
+ const ordering = this.shouldTailOptimizeStruct(node) ? "tail-optimized" : "source-order";
469
+ const orderedFields = ordering === "tail-optimized"
470
+ ? [...node.fields].sort((left, right) => {
471
+ const leftIsArray = left.node.nodeKind === "array" ? 1 : 0;
472
+ const rightIsArray = right.node.nodeKind === "array" ? 1 : 0;
473
+ return leftIsArray - rightIsArray;
474
+ })
475
+ : node.fields;
476
+ const builtFields = [];
477
+ let shape = BoundaryPlanBuilder.emptyShape();
478
+ for (const field of orderedFields) {
479
+ const presenceEntryId = field.optional
480
+ ? (() => {
481
+ this.usesOptionalPresence = true;
482
+ return this.addBlobEntry("optional-presence", field.path, 1, "uint8", "optional-presence");
483
+ })()
484
+ : undefined;
485
+ const fieldResult = this.buildNode(field.node, { isRoot: false });
486
+ builtFields.push({
487
+ name: field.name,
488
+ optional: field.optional,
489
+ typeText: field.typeText,
490
+ path: field.path,
491
+ presenceStrategy: field.optional ? "byte-flag" : undefined,
492
+ presenceEntryId,
493
+ node: fieldResult.node
494
+ });
495
+ const fieldShape = field.optional
496
+ ? {
497
+ fixedBlobWidthBytes: null,
498
+ fixedItemCount: null,
499
+ hasItems: fieldResult.shape.hasItems,
500
+ itemKinds: new Set(fieldResult.shape.itemKinds),
501
+ hasVariableExtent: true
502
+ }
503
+ : fieldResult.shape;
504
+ shape = this.mergeShapes(shape, fieldShape);
505
+ }
506
+ return {
507
+ node: {
508
+ nodeKind: "struct",
509
+ typeText: node.typeText,
510
+ path: node.path,
511
+ typeIdentityKey: node.typeIdentityKey,
512
+ cppNameHintParts: node.cppNameHintParts,
513
+ ordering,
514
+ fields: builtFields
515
+ },
516
+ shape
517
+ };
518
+ }
519
+ }
520
+ function buildBoundaryCodecPlan(codecId, analysis) {
521
+ return new BoundaryPlanBuilder(codecId, analysis).build();
522
+ }