@dusted/anqst 1.5.0 → 1.5.1

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 (80) hide show
  1. package/dist/src/app.js +31 -9
  2. package/dist/src/base93.js +0 -72
  3. package/dist/src/boundary-codec-analysis.js +468 -0
  4. package/dist/src/boundary-codec-leaves.js +602 -0
  5. package/dist/src/boundary-codec-model.js +77 -0
  6. package/dist/src/boundary-codec-plan.js +522 -0
  7. package/dist/src/boundary-codec-render.js +1738 -0
  8. package/dist/src/boundary-codecs.js +174 -0
  9. package/dist/src/emit.js +580 -90
  10. package/dist/src/program.js +1 -1
  11. package/package.json +2 -2
  12. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/decoder.js +0 -35
  13. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/encoder.js +0 -36
  14. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/decoder.js +0 -26
  15. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/encoder.js +0 -38
  16. package/dist/src/codecgenerators/basecodecemitters/binary-blob/decoder.js +0 -28
  17. package/dist/src/codecgenerators/basecodecemitters/binary-blob/encoder.js +0 -34
  18. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/decoder.js +0 -29
  19. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/encoder.js +0 -36
  20. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/decoder.js +0 -46
  21. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/encoder.js +0 -49
  22. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/decoder.js +0 -46
  23. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/encoder.js +0 -47
  24. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/decoder.js +0 -46
  25. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/encoder.js +0 -49
  26. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/decoder.js +0 -50
  27. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/encoder.js +0 -52
  28. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/decoder.js +0 -38
  29. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/encoder.js +0 -44
  30. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/decoder.js +0 -33
  31. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/encoder.js +0 -34
  32. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/decoder.js +0 -46
  33. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/encoder.js +0 -49
  34. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/decoder.js +0 -46
  35. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/encoder.js +0 -49
  36. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/decoder.js +0 -28
  37. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/encoder.js +0 -34
  38. package/dist/src/codecgenerators/basecodecemitters/boolean/decoder.js +0 -34
  39. package/dist/src/codecgenerators/basecodecemitters/boolean/encoder.js +0 -40
  40. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/decoder.js +0 -43
  41. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/encoder.js +0 -45
  42. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/decoder.js +0 -44
  43. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/encoder.js +0 -46
  44. package/dist/src/codecgenerators/basecodecemitters/integer-int16/decoder.js +0 -32
  45. package/dist/src/codecgenerators/basecodecemitters/integer-int16/encoder.js +0 -43
  46. package/dist/src/codecgenerators/basecodecemitters/integer-int32/decoder.js +0 -26
  47. package/dist/src/codecgenerators/basecodecemitters/integer-int32/encoder.js +0 -37
  48. package/dist/src/codecgenerators/basecodecemitters/integer-int8/decoder.js +0 -26
  49. package/dist/src/codecgenerators/basecodecemitters/integer-int8/encoder.js +0 -37
  50. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/decoder.js +0 -36
  51. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/encoder.js +0 -36
  52. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/decoder.js +0 -25
  53. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/encoder.js +0 -36
  54. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/decoder.js +0 -36
  55. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/encoder.js +0 -36
  56. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/decoder.js +0 -26
  57. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/encoder.js +0 -38
  58. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/decoder.js +0 -27
  59. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/encoder.js +0 -39
  60. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/decoder.js +0 -26
  61. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/encoder.js +0 -38
  62. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/decoder.js +0 -30
  63. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/encoder.js +0 -42
  64. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/decoder.js +0 -31
  65. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/encoder.js +0 -43
  66. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/decoder.js +0 -30
  67. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/encoder.js +0 -40
  68. package/dist/src/codecgenerators/basecodecemitters/number/decoder.js +0 -26
  69. package/dist/src/codecgenerators/basecodecemitters/number/encoder.js +0 -38
  70. package/dist/src/codecgenerators/basecodecemitters/shared/comments.js +0 -13
  71. package/dist/src/codecgenerators/basecodecemitters/shared/contracts.js +0 -2
  72. package/dist/src/codecgenerators/basecodecemitters/shared/fixedwidth.js +0 -53
  73. package/dist/src/codecgenerators/basecodecemitters/shared/index.js +0 -21
  74. package/dist/src/codecgenerators/basecodecemitters/shared/positionalBase93.js +0 -48
  75. package/dist/src/codecgenerators/basecodecemitters/shared/rawbytes.js +0 -30
  76. package/dist/src/codecgenerators/basecodecemitters/string/decoder.js +0 -43
  77. package/dist/src/codecgenerators/basecodecemitters/string/encoder.js +0 -43
  78. package/dist/src/codecgenerators/basecodecemitters/stringArray/decoder.js +0 -80
  79. package/dist/src/codecgenerators/basecodecemitters/stringArray/encoder.js +0 -57
  80. package/dist/src/structured-top-level-codecs.js +0 -1305
@@ -0,0 +1,1738 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderTsBoundaryCodecHelpers = renderTsBoundaryCodecHelpers;
4
+ exports.renderCppBoundaryCodecHelpers = renderCppBoundaryCodecHelpers;
5
+ const base93_1 = require("./base93");
6
+ const boundary_codec_model_1 = require("./boundary-codec-model");
7
+ function indent(level) {
8
+ return " ".repeat(level);
9
+ }
10
+ class TsEmitterContext {
11
+ constructor() {
12
+ this.nextId = 0;
13
+ }
14
+ next(prefix) {
15
+ this.nextId += 1;
16
+ return `__${prefix}${this.nextId}`;
17
+ }
18
+ }
19
+ class CppEmitterContext {
20
+ constructor() {
21
+ this.nextId = 0;
22
+ }
23
+ next(prefix) {
24
+ this.nextId += 1;
25
+ return `${prefix}${this.nextId}`;
26
+ }
27
+ }
28
+ function tsNamedHelperStem(node, scope = "") {
29
+ const scopePrefix = scope ? `${(0, boundary_codec_model_1.sanitizeIdentifier)(scope)}_` : "";
30
+ return `__anqstNamed_${scopePrefix}${(0, boundary_codec_model_1.sanitizeIdentifier)(node.name)}`;
31
+ }
32
+ function tsNamedEncodeHelperName(node, scope = "") {
33
+ return `${tsNamedHelperStem(node, scope)}_encode`;
34
+ }
35
+ function tsNamedDecodeHelperName(node, scope = "") {
36
+ return `${tsNamedHelperStem(node, scope)}_decode`;
37
+ }
38
+ function cppNamedHelperStem(node, scope = "") {
39
+ const scopePrefix = scope ? `${(0, boundary_codec_model_1.sanitizeIdentifier)(scope)}_` : "";
40
+ return `anqstNamed_${scopePrefix}${(0, boundary_codec_model_1.sanitizeIdentifier)(node.name)}`;
41
+ }
42
+ function cppNamedEncodeHelperName(node, scope = "") {
43
+ return `${cppNamedHelperStem(node, scope)}_encode`;
44
+ }
45
+ function cppNamedDecodeHelperName(node, scope = "") {
46
+ return `${cppNamedHelperStem(node, scope)}_decode`;
47
+ }
48
+ function collectNamedPlanNodes(node, out = new Map()) {
49
+ switch (node.nodeKind) {
50
+ case "named":
51
+ if (out.has(node.name)) {
52
+ return out;
53
+ }
54
+ out.set(node.name, node);
55
+ collectNamedPlanNodes(node.target, out);
56
+ return out;
57
+ case "array":
58
+ collectNamedPlanNodes(node.element, out);
59
+ return out;
60
+ case "struct":
61
+ for (const field of node.fields) {
62
+ collectNamedPlanNodes(field.node, out);
63
+ }
64
+ return out;
65
+ default:
66
+ return out;
67
+ }
68
+ }
69
+ function collectFiniteDomainPlanNodes(node, out = [], visitedNamed = new Set()) {
70
+ switch (node.nodeKind) {
71
+ case "finite-domain":
72
+ out.push(node);
73
+ return out;
74
+ case "array":
75
+ collectFiniteDomainPlanNodes(node.element, out, visitedNamed);
76
+ return out;
77
+ case "struct":
78
+ for (const field of node.fields) {
79
+ collectFiniteDomainPlanNodes(field.node, out, visitedNamed);
80
+ }
81
+ return out;
82
+ case "named":
83
+ if (visitedNamed.has(node.name))
84
+ return out;
85
+ visitedNamed.add(node.name);
86
+ collectFiniteDomainPlanNodes(node.target, out, visitedNamed);
87
+ return out;
88
+ default:
89
+ return out;
90
+ }
91
+ }
92
+ function tsInlineScalarNeedsScratch(kind) {
93
+ return kind === "number" || kind === "qint64" || kind === "quint64";
94
+ }
95
+ function planNeedsTsInlineScalarScratch(node, visitedNamed = new Set()) {
96
+ switch (node.nodeKind) {
97
+ case "leaf":
98
+ return (node.blobEntryId !== null
99
+ && node.lowering.tsEncode.mode === "inline"
100
+ && tsInlineScalarNeedsScratch(node.leaf.key));
101
+ case "finite-domain":
102
+ return (node.representation.kind === "coded-scalar"
103
+ && node.lowering.tsEncode.mode === "inline"
104
+ && tsInlineScalarNeedsScratch(node.representation.scalarKind));
105
+ case "array":
106
+ return planNeedsTsInlineScalarScratch(node.element, visitedNamed);
107
+ case "struct":
108
+ return node.fields.some((field) => planNeedsTsInlineScalarScratch(field.node, visitedNamed));
109
+ case "named":
110
+ if (visitedNamed.has(node.name))
111
+ return false;
112
+ visitedNamed.add(node.name);
113
+ return planNeedsTsInlineScalarScratch(node.target, visitedNamed);
114
+ }
115
+ }
116
+ function tsScalarWriteHelper(leaf) {
117
+ switch (leaf) {
118
+ case "boolean": return "__anqstPushBool";
119
+ case "number": return "__anqstPushFloat64";
120
+ case "qint64": return "__anqstPushBigInt64";
121
+ case "quint64": return "__anqstPushBigUint64";
122
+ case "qint32": return "__anqstPushInt32";
123
+ case "quint32": return "__anqstPushUint32";
124
+ case "qint16": return "__anqstPushInt16";
125
+ case "quint16": return "__anqstPushUint16";
126
+ case "qint8": return "__anqstPushInt8";
127
+ case "quint8": return "__anqstPushUint8";
128
+ case "int32": return "__anqstPushInt32";
129
+ case "uint32": return "__anqstPushUint32";
130
+ case "int16": return "__anqstPushInt16";
131
+ case "uint16": return "__anqstPushUint16";
132
+ case "int8": return "__anqstPushInt8";
133
+ case "uint8": return "__anqstPushUint8";
134
+ }
135
+ }
136
+ function tsScalarReadHelper(leaf) {
137
+ switch (leaf) {
138
+ case "boolean": return "__anqstReadBool";
139
+ case "number": return "__anqstReadFloat64";
140
+ case "qint64": return "__anqstReadBigInt64";
141
+ case "quint64": return "__anqstReadBigUint64";
142
+ case "qint32": return "__anqstReadInt32";
143
+ case "quint32": return "__anqstReadUint32";
144
+ case "qint16": return "__anqstReadInt16";
145
+ case "quint16": return "__anqstReadUint16";
146
+ case "qint8": return "__anqstReadInt8";
147
+ case "quint8": return "__anqstReadUint8";
148
+ case "int32": return "__anqstReadInt32";
149
+ case "uint32": return "__anqstReadUint32";
150
+ case "int16": return "__anqstReadInt16";
151
+ case "uint16": return "__anqstReadUint16";
152
+ case "int8": return "__anqstReadInt8";
153
+ case "uint8": return "__anqstReadUint8";
154
+ }
155
+ }
156
+ function cppScalarWriteHelper(leaf) {
157
+ switch (leaf) {
158
+ case "boolean": return "anqstPushBool";
159
+ case "number": return "anqstPushFloat64";
160
+ case "qint64": return "anqstPushQint64";
161
+ case "quint64": return "anqstPushQuint64";
162
+ case "qint32": return "anqstPushQint32";
163
+ case "quint32": return "anqstPushQuint32";
164
+ case "qint16": return "anqstPushQint16";
165
+ case "quint16": return "anqstPushQuint16";
166
+ case "qint8": return "anqstPushInt8";
167
+ case "quint8": return "anqstPushUint8";
168
+ case "int32": return "anqstPushInt32";
169
+ case "uint32": return "anqstPushUint32";
170
+ case "int16": return "anqstPushInt16";
171
+ case "uint16": return "anqstPushUint16";
172
+ case "int8": return "anqstPushInt8";
173
+ case "uint8": return "anqstPushUint8";
174
+ }
175
+ }
176
+ function cppScalarReadHelper(leaf) {
177
+ switch (leaf) {
178
+ case "boolean": return "anqstReadBool";
179
+ case "number": return "anqstReadFloat64";
180
+ case "qint64": return "anqstReadQint64";
181
+ case "quint64": return "anqstReadQuint64";
182
+ case "qint32": return "anqstReadQint32";
183
+ case "quint32": return "anqstReadQuint32";
184
+ case "qint16": return "anqstReadQint16";
185
+ case "quint16": return "anqstReadQuint16";
186
+ case "qint8": return "anqstReadInt8";
187
+ case "quint8": return "anqstReadUint8";
188
+ case "int32": return "anqstReadInt32";
189
+ case "uint32": return "anqstReadUint32";
190
+ case "int16": return "anqstReadInt16";
191
+ case "uint16": return "anqstReadUint16";
192
+ case "int8": return "anqstReadInt8";
193
+ case "uint8": return "anqstReadUint8";
194
+ }
195
+ }
196
+ function emitCppInlineScalarWrite(leaf, valueExpr, lines, ctx, level) {
197
+ const pad = indent(level).replace(/ /g, " ");
198
+ switch (leaf) {
199
+ case "boolean":
200
+ lines.push(`${pad}bytes.push_back(${valueExpr} ? 1u : 0u);`);
201
+ return;
202
+ case "qint8":
203
+ case "int8":
204
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>(static_cast<std::int8_t>(${valueExpr})));`);
205
+ return;
206
+ case "quint8":
207
+ case "uint8":
208
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>(${valueExpr}));`);
209
+ return;
210
+ case "qint16":
211
+ case "int16":
212
+ case "quint16":
213
+ case "uint16": {
214
+ const valueVar = ctx.next("u16");
215
+ lines.push(`${pad}const std::uint16_t ${valueVar} = static_cast<std::uint16_t>(${valueExpr});`);
216
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>(${valueVar} & 0xffu));`);
217
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>((${valueVar} >> 8) & 0xffu));`);
218
+ return;
219
+ }
220
+ case "qint32":
221
+ case "int32":
222
+ case "quint32":
223
+ case "uint32": {
224
+ const valueVar = ctx.next("u32");
225
+ lines.push(`${pad}const std::uint32_t ${valueVar} = static_cast<std::uint32_t>(${valueExpr});`);
226
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>(${valueVar} & 0xffu));`);
227
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>((${valueVar} >> 8) & 0xffu));`);
228
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>((${valueVar} >> 16) & 0xffu));`);
229
+ lines.push(`${pad}bytes.push_back(static_cast<std::uint8_t>((${valueVar} >> 24) & 0xffu));`);
230
+ return;
231
+ }
232
+ case "qint64":
233
+ case "quint64": {
234
+ const valueVar = ctx.next("u64");
235
+ lines.push(`${pad}const std::uint64_t ${valueVar} = static_cast<std::uint64_t>(${valueExpr});`);
236
+ lines.push(`${pad}for (int shift = 0; shift < 64; shift += 8) bytes.push_back(static_cast<std::uint8_t>((${valueVar} >> shift) & 0xffu));`);
237
+ return;
238
+ }
239
+ case "number": {
240
+ const bitsVar = ctx.next("bits");
241
+ const valueVar = ctx.next("float");
242
+ lines.push(`${pad}const double ${valueVar} = static_cast<double>(${valueExpr});`);
243
+ lines.push(`${pad}std::uint64_t ${bitsVar} = 0;`);
244
+ lines.push(`${pad}std::memcpy(&${bitsVar}, &${valueVar}, sizeof(${bitsVar}));`);
245
+ lines.push(`${pad}for (int shift = 0; shift < 64; shift += 8) bytes.push_back(static_cast<std::uint8_t>((${bitsVar} >> shift) & 0xffu));`);
246
+ return;
247
+ }
248
+ }
249
+ }
250
+ function emitCppInlineScalarRead(leaf, lines, ctx, level) {
251
+ const pad = indent(level).replace(/ /g, " ");
252
+ const readByte = "(blob[dataOffset++])";
253
+ switch (leaf) {
254
+ case "boolean":
255
+ return `((${readByte} & 1u) != 0u)`;
256
+ case "qint8":
257
+ return `static_cast<qint8>(static_cast<std::int8_t>(${readByte}))`;
258
+ case "quint8":
259
+ return `static_cast<quint8>(${readByte})`;
260
+ case "int8":
261
+ return `static_cast<std::int8_t>(${readByte})`;
262
+ case "uint8":
263
+ return `static_cast<std::uint8_t>(${readByte})`;
264
+ case "qint16":
265
+ case "quint16":
266
+ case "int16":
267
+ case "uint16": {
268
+ const valueVar = ctx.next("u16");
269
+ lines.push(`${pad}const std::uint16_t ${valueVar} = static_cast<std::uint16_t>(blob[dataOffset]) | (static_cast<std::uint16_t>(blob[dataOffset + 1]) << 8);`);
270
+ lines.push(`${pad}dataOffset += 2;`);
271
+ if (leaf === "qint16")
272
+ return `static_cast<qint16>(static_cast<std::int16_t>(${valueVar}))`;
273
+ if (leaf === "quint16")
274
+ return `static_cast<quint16>(${valueVar})`;
275
+ if (leaf === "int16")
276
+ return `static_cast<std::int16_t>(${valueVar})`;
277
+ return valueVar;
278
+ }
279
+ case "qint32":
280
+ case "quint32":
281
+ case "int32":
282
+ case "uint32": {
283
+ const valueVar = ctx.next("u32");
284
+ lines.push(`${pad}const std::uint32_t ${valueVar} =`);
285
+ lines.push(`${pad} static_cast<std::uint32_t>(blob[dataOffset])`);
286
+ lines.push(`${pad} | (static_cast<std::uint32_t>(blob[dataOffset + 1]) << 8)`);
287
+ lines.push(`${pad} | (static_cast<std::uint32_t>(blob[dataOffset + 2]) << 16)`);
288
+ lines.push(`${pad} | (static_cast<std::uint32_t>(blob[dataOffset + 3]) << 24);`);
289
+ lines.push(`${pad}dataOffset += 4;`);
290
+ if (leaf === "qint32")
291
+ return `static_cast<qint32>(static_cast<std::int32_t>(${valueVar}))`;
292
+ if (leaf === "quint32")
293
+ return `static_cast<quint32>(${valueVar})`;
294
+ if (leaf === "int32")
295
+ return `static_cast<std::int32_t>(${valueVar})`;
296
+ return valueVar;
297
+ }
298
+ case "qint64":
299
+ case "quint64":
300
+ case "number": {
301
+ const valueVar = ctx.next("u64");
302
+ lines.push(`${pad}std::uint64_t ${valueVar} = 0;`);
303
+ lines.push(`${pad}for (int shift = 0; shift < 64; shift += 8) {`);
304
+ lines.push(`${pad} ${valueVar} |= (static_cast<std::uint64_t>(blob[dataOffset++]) << shift);`);
305
+ lines.push(`${pad}}`);
306
+ if (leaf === "qint64")
307
+ return `static_cast<qint64>(static_cast<std::int64_t>(${valueVar}))`;
308
+ if (leaf === "quint64")
309
+ return `static_cast<quint64>(${valueVar})`;
310
+ const floatVar = ctx.next("float");
311
+ lines.push(`${pad}double ${floatVar} = 0.0;`);
312
+ lines.push(`${pad}std::memcpy(&${floatVar}, &${valueVar}, sizeof(${floatVar}));`);
313
+ return floatVar;
314
+ }
315
+ }
316
+ }
317
+ function cppInlineBinaryEncodeExpr(valueExpr) {
318
+ return `anqstBase93Encode(std::vector<std::uint8_t>(${valueExpr}.begin(), ${valueExpr}.end()))`;
319
+ }
320
+ function cppInlineBinaryDecodeExpr(encodedExpr) {
321
+ return `([&]() { const auto __bytes = anqstBase93Decode(${encodedExpr}); return QByteArray(reinterpret_cast<const char*>(__bytes.data()), static_cast<int>(__bytes.size())); })()`;
322
+ }
323
+ function binaryEncodeHelperName(binary) {
324
+ return `__anqstEncodeBinary_${binary}`;
325
+ }
326
+ function binaryDecodeHelperName(binary) {
327
+ return `__anqstDecodeBinary_${binary}`;
328
+ }
329
+ function emitTsInlineScalarWrite(leaf, valueExpr, lines, level, ctx) {
330
+ const pad = indent(level);
331
+ switch (leaf) {
332
+ case "boolean":
333
+ lines.push(`${pad}__bytes.push(${valueExpr} ? 1 : 0);`);
334
+ return;
335
+ case "qint8":
336
+ case "quint8":
337
+ case "int8":
338
+ case "uint8":
339
+ lines.push(`${pad}__bytes.push(((${valueExpr}) as number) & 0xff);`);
340
+ return;
341
+ case "qint16":
342
+ case "quint16":
343
+ case "int16":
344
+ case "uint16": {
345
+ const valueVar = ctx.next("u16");
346
+ lines.push(`${pad}const ${valueVar} = ((${valueExpr}) as number) & 0xffff;`);
347
+ lines.push(`${pad}__bytes.push(${valueVar} & 0xff, (${valueVar} >>> 8) & 0xff);`);
348
+ return;
349
+ }
350
+ case "qint32":
351
+ case "quint32":
352
+ case "int32":
353
+ case "uint32": {
354
+ const valueVar = ctx.next("u32");
355
+ lines.push(`${pad}const ${valueVar} = ((${valueExpr}) as number) >>> 0;`);
356
+ lines.push(`${pad}__bytes.push(${valueVar} & 0xff, (${valueVar} >>> 8) & 0xff, (${valueVar} >>> 16) & 0xff, (${valueVar} >>> 24) & 0xff);`);
357
+ return;
358
+ }
359
+ case "number":
360
+ lines.push(`${pad}__anqstScalarScratchView.setFloat64(0, ${valueExpr}, true);`);
361
+ lines.push(`${pad}__bytes.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!);`);
362
+ return;
363
+ case "qint64":
364
+ lines.push(`${pad}__anqstScalarScratchView.setBigInt64(0, ${valueExpr}, true);`);
365
+ lines.push(`${pad}__bytes.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!);`);
366
+ return;
367
+ case "quint64":
368
+ lines.push(`${pad}__anqstScalarScratchView.setBigUint64(0, ${valueExpr}, true);`);
369
+ lines.push(`${pad}__bytes.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!);`);
370
+ return;
371
+ }
372
+ }
373
+ function tsInlineScalarReadExpr(leaf) {
374
+ switch (leaf) {
375
+ case "boolean":
376
+ return `((__blob[__dataCursor.offset++]! & 1) === 1)`;
377
+ case "qint8":
378
+ case "int8":
379
+ return `__blobView.getInt8(__dataCursor.offset++)`;
380
+ case "quint8":
381
+ case "uint8":
382
+ return `__blob[__dataCursor.offset++]!`;
383
+ case "qint16":
384
+ case "int16":
385
+ return `(__dataCursor.offset += 2, __blobView.getInt16(__dataCursor.offset - 2, true))`;
386
+ case "quint16":
387
+ case "uint16":
388
+ return `(__dataCursor.offset += 2, __blobView.getUint16(__dataCursor.offset - 2, true))`;
389
+ case "qint32":
390
+ case "int32":
391
+ return `(__dataCursor.offset += 4, __blobView.getInt32(__dataCursor.offset - 4, true))`;
392
+ case "quint32":
393
+ case "uint32":
394
+ return `(__dataCursor.offset += 4, __blobView.getUint32(__dataCursor.offset - 4, true))`;
395
+ case "number":
396
+ return `(__dataCursor.offset += 8, __blobView.getFloat64(__dataCursor.offset - 8, true))`;
397
+ case "qint64":
398
+ return `(__dataCursor.offset += 8, __blobView.getBigInt64(__dataCursor.offset - 8, true))`;
399
+ case "quint64":
400
+ return `(__dataCursor.offset += 8, __blobView.getBigUint64(__dataCursor.offset - 8, true))`;
401
+ }
402
+ }
403
+ function tsInlineBinaryEncodeExpr(binary, valueExpr) {
404
+ switch (binary) {
405
+ case "ArrayBuffer":
406
+ return `__anqstBase93Encode(new Uint8Array(${valueExpr}))`;
407
+ case "Uint8Array":
408
+ case "Int8Array":
409
+ case "Uint16Array":
410
+ case "Int16Array":
411
+ case "Uint32Array":
412
+ case "Int32Array":
413
+ case "Float32Array":
414
+ case "Float64Array":
415
+ return `__anqstBase93Encode(new Uint8Array(${valueExpr}.buffer, ${valueExpr}.byteOffset, ${valueExpr}.byteLength))`;
416
+ }
417
+ }
418
+ function tsInlineBinaryDecodeExpr(binary, encodedExpr) {
419
+ if (binary === "ArrayBuffer") {
420
+ return `(() => { const __bytes = __anqstBase93Decode(${encodedExpr}); if (__bytes.byteOffset === 0 && __bytes.byteLength === __bytes.buffer.byteLength) return __bytes.buffer as ArrayBuffer; return __bytes.buffer.slice(__bytes.byteOffset, __bytes.byteOffset + __bytes.byteLength); })()`;
421
+ }
422
+ const typedArrayInfo = {
423
+ Uint8Array: { ctor: "Uint8Array", bytesPerElement: 1 },
424
+ Int8Array: { ctor: "Int8Array", bytesPerElement: 1 },
425
+ Uint16Array: { ctor: "Uint16Array", bytesPerElement: 2 },
426
+ Int16Array: { ctor: "Int16Array", bytesPerElement: 2 },
427
+ Uint32Array: { ctor: "Uint32Array", bytesPerElement: 4 },
428
+ Int32Array: { ctor: "Int32Array", bytesPerElement: 4 },
429
+ Float32Array: { ctor: "Float32Array", bytesPerElement: 4 },
430
+ Float64Array: { ctor: "Float64Array", bytesPerElement: 8 }
431
+ };
432
+ const info = typedArrayInfo[binary];
433
+ return `(() => { const __bytes = __anqstBase93Decode(${encodedExpr}); if ((__bytes.byteOffset % ${info.bytesPerElement}) === 0) return new ${info.ctor}(__bytes.buffer, __bytes.byteOffset, __bytes.byteLength / ${info.bytesPerElement}); const __copy = __bytes.slice(); return new ${info.ctor}(__copy.buffer, 0, __copy.byteLength / ${info.bytesPerElement}); })()`;
434
+ }
435
+ function emitTsEncodeScratchDeclarations(_ctx, _level) {
436
+ return [];
437
+ }
438
+ function tsCast(expression, typeText) {
439
+ return `(${expression}) as ${(0, boundary_codec_model_1.stripAnQstType)(typeText)}`;
440
+ }
441
+ function tsFiniteDomainTextValueExpr(domain, valueExpr) {
442
+ switch (domain.primitive) {
443
+ case "string":
444
+ return valueExpr;
445
+ case "number":
446
+ return `String(${valueExpr})`;
447
+ case "boolean":
448
+ return `${valueExpr} ? "1" : "0"`;
449
+ }
450
+ }
451
+ function emitTsFiniteDomainCodeAssignment(domain, valueExpr, targetVar, lines, level) {
452
+ const pad = indent(level);
453
+ lines.push(`${pad}switch (${valueExpr}) {`);
454
+ for (const variant of domain.variants) {
455
+ lines.push(`${pad} case ${variant.tsLiteralText}: ${targetVar} = ${variant.code}; break;`);
456
+ }
457
+ lines.push(`${pad} default: throw new Error("AnQst finite-domain encode received an unsupported value.");`);
458
+ lines.push(`${pad}}`);
459
+ }
460
+ function emitTsFiniteDomainDecodeFromCode(node, readExpr, lines, ctx, level) {
461
+ const pad = indent(level);
462
+ const valueVar = ctx.next("value");
463
+ const firstVariant = node.domain.variants[0];
464
+ lines.push(`${pad}let ${valueVar}: ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)} = ${tsCast(firstVariant.tsLiteralText, node.typeText)};`);
465
+ lines.push(`${pad}switch (${readExpr}) {`);
466
+ for (const variant of node.domain.variants) {
467
+ lines.push(`${pad} case ${variant.code}: ${valueVar} = ${tsCast(variant.tsLiteralText, node.typeText)}; break;`);
468
+ }
469
+ lines.push(`${pad}}`);
470
+ return valueVar;
471
+ }
472
+ function emitTsFiniteDomainDecodeFromText(node, textExpr, lines, ctx, level) {
473
+ const pad = indent(level);
474
+ const valueVar = ctx.next("value");
475
+ const firstVariant = node.domain.variants[0];
476
+ lines.push(`${pad}let ${valueVar}: ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)} = ${tsCast(firstVariant.tsLiteralText, node.typeText)};`);
477
+ lines.push(`${pad}switch (${textExpr}) {`);
478
+ for (const variant of node.domain.variants) {
479
+ const encodedText = node.domain.primitive === "boolean" ? (variant.value ? '"1"' : '"0"') : JSON.stringify(String(variant.value));
480
+ lines.push(`${pad} case ${encodedText}: ${valueVar} = ${tsCast(variant.tsLiteralText, node.typeText)}; break;`);
481
+ }
482
+ lines.push(`${pad}}`);
483
+ return valueVar;
484
+ }
485
+ function emitTsEncodeLeaf(node, valueExpr, lines, ctx, level) {
486
+ const pad = indent(level);
487
+ if (node.blobEntryId) {
488
+ const leafKind = node.leaf.key;
489
+ if (node.lowering.tsEncode.mode === "helper-call") {
490
+ lines.push(`${pad}${tsScalarWriteHelper(leafKind)}(__bytes, ${valueExpr});`);
491
+ }
492
+ else {
493
+ emitTsInlineScalarWrite(leafKind, valueExpr, lines, level, ctx);
494
+ }
495
+ return;
496
+ }
497
+ if (node.leaf.region === "string") {
498
+ if (node.selectedPacking === "text-packed" && node.leaf.key === "boolean") {
499
+ lines.push(`${pad}__items.push(${valueExpr} ? "1" : "0");`);
500
+ return;
501
+ }
502
+ lines.push(`${pad}__items.push(${valueExpr});`);
503
+ return;
504
+ }
505
+ if (node.leaf.region === "binary") {
506
+ const binaryKind = node.leaf.key;
507
+ if (node.lowering.tsEncode.mode === "helper-call") {
508
+ lines.push(`${pad}__items.push(${binaryEncodeHelperName(binaryKind)}(${valueExpr}));`);
509
+ }
510
+ else {
511
+ lines.push(`${pad}__items.push(${tsInlineBinaryEncodeExpr(binaryKind, valueExpr)});`);
512
+ }
513
+ return;
514
+ }
515
+ lines.push(`${pad}__items.push(${valueExpr});`);
516
+ }
517
+ function emitTsEncodeNode(node, valueExpr, lines, ctx, level, scope = "") {
518
+ const pad = indent(level);
519
+ switch (node.nodeKind) {
520
+ case "leaf":
521
+ emitTsEncodeLeaf(node, valueExpr, lines, ctx, level);
522
+ return;
523
+ case "named":
524
+ lines.push(`${pad}${tsNamedEncodeHelperName(node, scope)}(${valueExpr}, __bytes, __items);`);
525
+ return;
526
+ case "finite-domain":
527
+ if (node.representation.kind === "coded-scalar") {
528
+ const codeVar = ctx.next("code");
529
+ if (node.lowering.tsEncode.mode === "helper-call") {
530
+ if (node.lowering.tsEncode.helperNameHint) {
531
+ lines.push(`${pad}const ${codeVar} = __anqstFiniteDomainEncodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.tsEncode.helperNameHint)}(${valueExpr});`);
532
+ }
533
+ else {
534
+ lines.push(`${pad}let ${codeVar} = 0;`);
535
+ emitTsFiniteDomainCodeAssignment(node.domain, valueExpr, codeVar, lines, level);
536
+ }
537
+ lines.push(`${pad}${tsScalarWriteHelper(node.representation.scalarKind)}(__bytes, ${codeVar});`);
538
+ }
539
+ else {
540
+ lines.push(`${pad}let ${codeVar} = 0;`);
541
+ emitTsFiniteDomainCodeAssignment(node.domain, valueExpr, codeVar, lines, level);
542
+ emitTsInlineScalarWrite(node.representation.scalarKind, codeVar, lines, level, ctx);
543
+ }
544
+ }
545
+ else {
546
+ if (node.lowering.tsEncode.mode === "helper-call" && node.lowering.tsEncode.helperNameHint) {
547
+ lines.push(`${pad}__items.push(__anqstFiniteDomainEncodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.tsEncode.helperNameHint)}(${valueExpr}));`);
548
+ }
549
+ else {
550
+ lines.push(`${pad}__items.push(${tsFiniteDomainTextValueExpr(node.domain, valueExpr)});`);
551
+ }
552
+ }
553
+ return;
554
+ case "array": {
555
+ if (node.extentStrategy === "explicit-count") {
556
+ emitTsInlineScalarWrite("uint32", `${valueExpr}.length >>> 0`, lines, level, ctx);
557
+ }
558
+ const itemVar = ctx.next("item");
559
+ lines.push(`${pad}for (const ${itemVar} of ${valueExpr}) {`);
560
+ emitTsEncodeNode(node.element, itemVar, lines, ctx, level + 1, scope);
561
+ lines.push(`${pad}}`);
562
+ return;
563
+ }
564
+ case "struct":
565
+ for (const field of node.fields) {
566
+ const fieldExpr = `${valueExpr}.${field.name}`;
567
+ if (field.optional) {
568
+ const presentVar = ctx.next("present");
569
+ lines.push(`${pad}const ${presentVar} = ${fieldExpr} !== undefined;`);
570
+ emitTsInlineScalarWrite("uint8", `${presentVar} ? 1 : 0`, lines, level, ctx);
571
+ lines.push(`${pad}if (${presentVar}) {`);
572
+ emitTsEncodeNode(field.node, `${fieldExpr}!`, lines, ctx, level + 1, scope);
573
+ lines.push(`${pad}}`);
574
+ }
575
+ else {
576
+ emitTsEncodeNode(field.node, fieldExpr, lines, ctx, level, scope);
577
+ }
578
+ }
579
+ }
580
+ }
581
+ function emitTsDecodeLeaf(node) {
582
+ if (node.blobEntryId) {
583
+ const leafKind = node.leaf.key;
584
+ if (node.lowering.tsDecode.mode === "helper-call") {
585
+ return tsCast(`${tsScalarReadHelper(leafKind)}(__blob, __blobView, __dataCursor)`, node.typeText);
586
+ }
587
+ return tsCast(tsInlineScalarReadExpr(leafKind), node.typeText);
588
+ }
589
+ if (node.leaf.region === "string") {
590
+ if (node.selectedPacking === "text-packed" && node.leaf.key === "boolean") {
591
+ return tsCast(`String(__items[__itemIndex.value++]!) === "1"`, node.typeText);
592
+ }
593
+ return tsCast(`String(__items[__itemIndex.value++]!)`, node.typeText);
594
+ }
595
+ if (node.leaf.region === "binary") {
596
+ const binaryKind = node.leaf.key;
597
+ if (node.lowering.tsDecode.mode === "helper-call") {
598
+ return tsCast(`${binaryDecodeHelperName(binaryKind)}(String(__items[__itemIndex.value++]!))`, node.typeText);
599
+ }
600
+ return tsCast(tsInlineBinaryDecodeExpr(binaryKind, "String(__items[__itemIndex.value++]!)"), node.typeText);
601
+ }
602
+ return tsCast(`__items[__itemIndex.value++]!`, node.typeText);
603
+ }
604
+ function emitTsDecodeNode(node, lines, ctx, level, scope = "") {
605
+ const pad = indent(level);
606
+ switch (node.nodeKind) {
607
+ case "leaf":
608
+ return emitTsDecodeLeaf(node);
609
+ case "named":
610
+ return `${tsNamedDecodeHelperName(node, scope)}(__blob, __blobView, __dataCursor, __items, __itemIndex)`;
611
+ case "finite-domain":
612
+ if (node.representation.kind === "coded-scalar") {
613
+ const codeVar = ctx.next("code");
614
+ if (node.lowering.tsDecode.mode === "helper-call") {
615
+ lines.push(`${pad}const ${codeVar} = ${tsScalarReadHelper(node.representation.scalarKind)}(__blob, __blobView, __dataCursor);`);
616
+ if (node.lowering.tsDecode.helperNameHint) {
617
+ return `__anqstFiniteDomainDecodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.tsDecode.helperNameHint)}(${codeVar})`;
618
+ }
619
+ }
620
+ else {
621
+ lines.push(`${pad}const ${codeVar} = ${tsInlineScalarReadExpr(node.representation.scalarKind)};`);
622
+ }
623
+ return emitTsFiniteDomainDecodeFromCode(node, codeVar, lines, ctx, level);
624
+ }
625
+ const textVar = ctx.next("text");
626
+ lines.push(`${pad}const ${textVar} = String(__items[__itemIndex.value++]!);`);
627
+ if (node.lowering.tsDecode.mode === "helper-call" && node.lowering.tsDecode.helperNameHint) {
628
+ return `__anqstFiniteDomainDecodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.tsDecode.helperNameHint)}(${textVar})`;
629
+ }
630
+ return emitTsFiniteDomainDecodeFromText(node, textVar, lines, ctx, level);
631
+ case "array": {
632
+ const arrayVar = ctx.next("array");
633
+ const countVar = ctx.next("count");
634
+ const indexVar = ctx.next("index");
635
+ if (node.extentStrategy === "blob-tail") {
636
+ const elementWidth = node.elementBlobWidthBytes ?? 1;
637
+ const remainingVar = ctx.next("remainingBytes");
638
+ lines.push(`${pad}const ${remainingVar} = __blob.length - __dataCursor.offset;`);
639
+ lines.push(`${pad}const ${countVar} = ${remainingVar} / ${elementWidth};`);
640
+ }
641
+ else if (node.extentStrategy === "item-tail") {
642
+ const elementItemCount = node.elementItemCount ?? 1;
643
+ const remainingItemsVar = ctx.next("remainingItems");
644
+ lines.push(`${pad}const ${remainingItemsVar} = __items.length - __itemIndex.value;`);
645
+ lines.push(`${pad}const ${countVar} = ${remainingItemsVar} / ${elementItemCount};`);
646
+ }
647
+ else {
648
+ lines.push(`${pad}const ${countVar} = ${tsInlineScalarReadExpr("uint32")};`);
649
+ }
650
+ lines.push(`${pad}const ${arrayVar} = new Array(${countVar}) as ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)};`);
651
+ lines.push(`${pad}for (let ${indexVar} = 0; ${indexVar} < ${countVar}; ${indexVar} += 1) {`);
652
+ const elementExpr = emitTsDecodeNode(node.element, lines, ctx, level + 1, scope);
653
+ lines.push(`${indent(level + 1)}${arrayVar}[${indexVar}] = ${elementExpr};`);
654
+ lines.push(`${pad}}`);
655
+ return arrayVar;
656
+ }
657
+ case "struct": {
658
+ const valueVar = ctx.next("value");
659
+ lines.push(`${pad}const ${valueVar} = {} as ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)};`);
660
+ for (const field of node.fields) {
661
+ if (field.optional) {
662
+ const presentVar = ctx.next("present");
663
+ lines.push(`${pad}const ${presentVar} = ${tsInlineScalarReadExpr("uint8")} !== 0;`);
664
+ lines.push(`${pad}if (${presentVar}) {`);
665
+ const fieldExpr = emitTsDecodeNode(field.node, lines, ctx, level + 1, scope);
666
+ lines.push(`${indent(level + 1)}${valueVar}.${field.name} = ${fieldExpr};`);
667
+ lines.push(`${pad}}`);
668
+ }
669
+ else {
670
+ const fieldExpr = emitTsDecodeNode(field.node, lines, ctx, level, scope);
671
+ lines.push(`${pad}${valueVar}.${field.name} = ${fieldExpr};`);
672
+ }
673
+ }
674
+ return valueVar;
675
+ }
676
+ }
677
+ }
678
+ function renderTsFastPathCodec(plan) {
679
+ const encoderName = `encode${plan.codecId}`;
680
+ const decoderName = `decode${plan.codecId}`;
681
+ const trustedOnlyDecode = plan.decodePolicy === "trusted-only";
682
+ if (plan.root.nodeKind === "leaf") {
683
+ if (plan.root.blobEntryId) {
684
+ const encodeCtx = new TsEmitterContext();
685
+ const encodeLines = [];
686
+ emitTsEncodeLeaf(plan.root, "value", encodeLines, encodeCtx, 1);
687
+ const encodeScratchLines = emitTsEncodeScratchDeclarations(encodeCtx, 1);
688
+ return `function ${encoderName}(value: ${plan.tsTypeText}): unknown {
689
+ const __bytes: number[] = [];
690
+ ${encodeScratchLines.length > 0 ? `${encodeScratchLines.join("\n")}\n` : ""}${encodeLines.join("\n")}
691
+ return __anqstBase93Encode(Uint8Array.from(__bytes));
692
+ }
693
+
694
+ function ${decoderName}(wire: unknown): ${plan.tsTypeText} {
695
+ const __blob = __anqstBase93Decode(String(wire ?? ""));
696
+ const __blobView = new DataView(__blob.buffer, __blob.byteOffset, __blob.byteLength);
697
+ const __dataCursor = { offset: 0 };
698
+ const __result = ${emitTsDecodeLeaf(plan.root)};
699
+ ${trustedOnlyDecode ? "" : ' if (__dataCursor.offset !== __blob.length) throw new Error("AnQst wire contained trailing blob bytes.");'}
700
+ return __result;
701
+ }`;
702
+ }
703
+ const directEncode = plan.root.leaf.region === "string" && plan.root.selectedPacking === "text-packed" && plan.root.leaf.key === "boolean"
704
+ ? `value ? "1" : "0"`
705
+ : plan.root.leaf.region === "binary"
706
+ ? plan.root.lowering.tsEncode.mode === "helper-call"
707
+ ? `${binaryEncodeHelperName(plan.root.leaf.key)}(value)`
708
+ : tsInlineBinaryEncodeExpr(plan.root.leaf.key, "value")
709
+ : "value";
710
+ const directDecode = plan.root.leaf.region === "string" && plan.root.selectedPacking === "text-packed" && plan.root.leaf.key === "boolean"
711
+ ? tsCast(`String(wire ?? "") === "1"`, plan.root.typeText)
712
+ : plan.root.leaf.region === "string"
713
+ ? tsCast(`String(wire ?? "")`, plan.root.typeText)
714
+ : plan.root.leaf.region === "binary"
715
+ ? tsCast(plan.root.lowering.tsDecode.mode === "helper-call"
716
+ ? `${binaryDecodeHelperName(plan.root.leaf.key)}(String(wire ?? ""))`
717
+ : tsInlineBinaryDecodeExpr(plan.root.leaf.key, "String(wire ?? \"\")"), plan.root.typeText)
718
+ : tsCast("wire", plan.root.typeText);
719
+ return `function ${encoderName}(value: ${plan.tsTypeText}): unknown {
720
+ return ${directEncode};
721
+ }
722
+
723
+ function ${decoderName}(wire: unknown): ${plan.tsTypeText} {
724
+ return ${directDecode};
725
+ }`;
726
+ }
727
+ if (plan.root.nodeKind === "finite-domain") {
728
+ if (plan.root.representation.kind === "coded-scalar") {
729
+ const encodeCtx = new TsEmitterContext();
730
+ const encodeLines = [];
731
+ if (plan.root.lowering.tsEncode.mode === "helper-call" && plan.root.lowering.tsEncode.helperNameHint) {
732
+ encodeLines.push(` const __code = __anqstFiniteDomainEncodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.tsEncode.helperNameHint)}(value);`);
733
+ }
734
+ else {
735
+ encodeLines.push(` let __code = 0;`);
736
+ emitTsFiniteDomainCodeAssignment(plan.root.domain, "value", "__code", encodeLines, 1);
737
+ }
738
+ encodeLines.push(` const __bytes: number[] = [];`);
739
+ if (plan.root.lowering.tsEncode.mode === "helper-call") {
740
+ encodeLines.push(` ${tsScalarWriteHelper(plan.root.representation.scalarKind)}(__bytes, __code);`);
741
+ }
742
+ else {
743
+ emitTsInlineScalarWrite(plan.root.representation.scalarKind, "__code", encodeLines, 1, encodeCtx);
744
+ }
745
+ const encodeScratch = emitTsEncodeScratchDeclarations(encodeCtx, 1);
746
+ if (encodeScratch.length > 0) {
747
+ encodeLines.splice(encodeLines.indexOf(" const __bytes: number[] = [];"), 0, ...encodeScratch);
748
+ }
749
+ encodeLines.push(` return __anqstBase93Encode(Uint8Array.from(__bytes));`);
750
+ const decodeLines = [
751
+ " const __blob = __anqstBase93Decode(String(wire ?? \"\"));",
752
+ " const __blobView = new DataView(__blob.buffer, __blob.byteOffset, __blob.byteLength);",
753
+ " const __dataCursor = { offset: 0 };",
754
+ " const __code = " + `${plan.root.lowering.tsDecode.mode === "helper-call"
755
+ ? `${tsScalarReadHelper(plan.root.representation.scalarKind)}(__blob, __blobView, __dataCursor)`
756
+ : tsInlineScalarReadExpr(plan.root.representation.scalarKind)};`
757
+ ];
758
+ const decodeBody = [];
759
+ const valueExpr = plan.root.lowering.tsDecode.mode === "helper-call" && plan.root.lowering.tsDecode.helperNameHint
760
+ ? `__anqstFiniteDomainDecodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.tsDecode.helperNameHint)}(__code)`
761
+ : emitTsFiniteDomainDecodeFromCode(plan.root, "__code", decodeBody, new TsEmitterContext(), 1);
762
+ return `function ${encoderName}(value: ${plan.tsTypeText}): unknown {
763
+ ${encodeLines.join("\n")}
764
+ }
765
+
766
+ function ${decoderName}(wire: unknown): ${plan.tsTypeText} {
767
+ ${decodeLines.join("\n")}
768
+ ${decodeBody.join("\n")}
769
+ const __result = ${valueExpr};
770
+ ${trustedOnlyDecode ? "" : ' if (__dataCursor.offset !== __blob.length) throw new Error("AnQst wire contained trailing blob bytes.");'}
771
+ return __result;
772
+ }`;
773
+ }
774
+ const decodeBody = [];
775
+ const valueExpr = plan.root.lowering.tsDecode.mode === "helper-call" && plan.root.lowering.tsDecode.helperNameHint
776
+ ? `__anqstFiniteDomainDecodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.tsDecode.helperNameHint)}(__text)`
777
+ : emitTsFiniteDomainDecodeFromText(plan.root, "__text", decodeBody, new TsEmitterContext(), 1);
778
+ return `function ${encoderName}(value: ${plan.tsTypeText}): unknown {
779
+ return ${plan.root.lowering.tsEncode.mode === "helper-call" && plan.root.lowering.tsEncode.helperNameHint
780
+ ? `__anqstFiniteDomainEncodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.tsEncode.helperNameHint)}(value)`
781
+ : tsFiniteDomainTextValueExpr(plan.root.domain, "value")};
782
+ }
783
+
784
+ function ${decoderName}(wire: unknown): ${plan.tsTypeText} {
785
+ const __text = String(wire ?? "");
786
+ ${decodeBody.join("\n")}
787
+ const __result = ${valueExpr};
788
+ return __result;
789
+ }`;
790
+ }
791
+ return null;
792
+ }
793
+ function renderTsPlanCodec(plan) {
794
+ const fastPath = renderTsFastPathCodec(plan);
795
+ if (fastPath)
796
+ return fastPath;
797
+ const trustedOnlyDecode = plan.decodePolicy === "trusted-only";
798
+ const namedNodes = [...collectNamedPlanNodes(plan.root).values()];
799
+ const encodeCtx = new TsEmitterContext();
800
+ const encodeLines = [];
801
+ emitTsEncodeNode(plan.root, "value", encodeLines, encodeCtx, 1, plan.codecId);
802
+ const encodeScratchLines = emitTsEncodeScratchDeclarations(encodeCtx, 1);
803
+ const decodeCtx = new TsEmitterContext();
804
+ const decodeLines = [];
805
+ const decodeExpr = emitTsDecodeNode(plan.root, decodeLines, decodeCtx, 1, plan.codecId);
806
+ const encoderName = `encode${plan.codecId}`;
807
+ const decoderName = `decode${plan.codecId}`;
808
+ const namedHelpers = namedNodes.map((node) => {
809
+ const helperEncodeCtx = new TsEmitterContext();
810
+ const helperEncodeLines = [];
811
+ emitTsEncodeNode(node.target, "value", helperEncodeLines, helperEncodeCtx, 1, plan.codecId);
812
+ const helperEncodeScratch = emitTsEncodeScratchDeclarations(helperEncodeCtx, 1);
813
+ const helperDecodeLines = [];
814
+ const helperDecodeExpr = emitTsDecodeNode(node.target, helperDecodeLines, new TsEmitterContext(), 1, plan.codecId);
815
+ const tsType = (0, boundary_codec_model_1.stripAnQstType)(node.typeText);
816
+ return `function ${tsNamedEncodeHelperName(node, plan.codecId)}(value: ${tsType}, __bytes: number[], __items: unknown[]): void {
817
+ ${helperEncodeScratch.length > 0 ? `${helperEncodeScratch.join("\n")}\n` : ""}${helperEncodeLines.join("\n")}
818
+ }
819
+
820
+ function ${tsNamedDecodeHelperName(node, plan.codecId)}(
821
+ __blob: Uint8Array,
822
+ __blobView: DataView,
823
+ __dataCursor: { offset: number },
824
+ __items: unknown[],
825
+ __itemIndex: { value: number }
826
+ ): ${tsType} {
827
+ ${helperDecodeLines.join("\n")}
828
+ return ${helperDecodeExpr};
829
+ }`;
830
+ }).join("\n\n");
831
+ return `${namedHelpers ? `${namedHelpers}\n\n` : ""}function ${encoderName}(value: ${plan.tsTypeText}): unknown {
832
+ const __bytes: number[] = [];
833
+ const __items: unknown[] = [];
834
+ ${encodeScratchLines.length > 0 ? `${encodeScratchLines.join("\n")}\n` : ""}${encodeLines.join("\n")}
835
+ return __anqstEncodeWire(__bytes, __items);
836
+ }
837
+
838
+ function ${decoderName}(wire: unknown): ${plan.tsTypeText} {
839
+ const __items = Array.isArray(wire) ? wire : [wire];
840
+ const __blob = ${plan.requirements.hasBlob ? `__anqstBase93Decode(String(__items[0] ?? ""))` : "new Uint8Array()"};
841
+ const __blobView = new DataView(__blob.buffer, __blob.byteOffset, __blob.byteLength);
842
+ const __itemIndex = { value: ${plan.requirements.hasBlob ? 1 : 0} };
843
+ const __dataCursor = { offset: 0 };
844
+ ${decodeLines.join("\n")}
845
+ const __result = ${decodeExpr};
846
+ ${trustedOnlyDecode ? "" : (plan.requirements.hasBlob ? ` if (__dataCursor.offset !== __blob.length) throw new Error("AnQst wire contained trailing blob bytes.");` : "")}
847
+ ${trustedOnlyDecode ? "" : " if (__itemIndex.value !== __items.length) throw new Error(\"AnQst wire contained trailing item payloads.\");"}
848
+ return __result;
849
+ }`;
850
+ }
851
+ function collectTsSupport(catalog) {
852
+ const scalarEncodeKinds = new Set();
853
+ const scalarDecodeKinds = new Set();
854
+ const binaryHelperKinds = new Set();
855
+ const finiteDomainEncodeHelpers = new Map();
856
+ const finiteDomainDecodeHelpers = new Map();
857
+ let needsBase93 = false;
858
+ let needsInlineScalarScratch = false;
859
+ for (const plan of catalog.plans) {
860
+ if (plan.requirements.hasBlob || plan.requirements.usedBinaryLeafKinds.length > 0) {
861
+ needsBase93 = true;
862
+ }
863
+ if (planNeedsTsInlineScalarScratch(plan.root)) {
864
+ needsInlineScalarScratch = true;
865
+ }
866
+ for (const kind of plan.requirements.tsHelperRequirements.scalarEncodeKinds)
867
+ scalarEncodeKinds.add(kind);
868
+ for (const kind of plan.requirements.tsHelperRequirements.scalarDecodeKinds)
869
+ scalarDecodeKinds.add(kind);
870
+ for (const kind of plan.requirements.tsHelperRequirements.binaryEncodeKinds)
871
+ binaryHelperKinds.add(kind);
872
+ for (const kind of plan.requirements.tsHelperRequirements.binaryDecodeKinds)
873
+ binaryHelperKinds.add(kind);
874
+ for (const node of collectFiniteDomainPlanNodes(plan.root)) {
875
+ if (node.lowering.tsEncode.mode === "helper-call" && node.lowering.tsEncode.helperNameHint) {
876
+ finiteDomainEncodeHelpers.set(node.lowering.tsEncode.helperNameHint, node);
877
+ }
878
+ if (node.lowering.tsDecode.mode === "helper-call" && node.lowering.tsDecode.helperNameHint) {
879
+ finiteDomainDecodeHelpers.set(node.lowering.tsDecode.helperNameHint, node);
880
+ }
881
+ }
882
+ }
883
+ const sortByName = (items) => [...items].sort();
884
+ return {
885
+ needsBase93,
886
+ needsInlineScalarScratch,
887
+ scalarEncodeKinds: sortByName(scalarEncodeKinds),
888
+ scalarDecodeKinds: sortByName(scalarDecodeKinds),
889
+ binaryHelperKinds: sortByName(binaryHelperKinds),
890
+ finiteDomainEncodeHelpers: [...finiteDomainEncodeHelpers.entries()]
891
+ .sort(([left], [right]) => left.localeCompare(right))
892
+ .map(([helperName, node]) => ({ helperName, node })),
893
+ finiteDomainDecodeHelpers: [...finiteDomainDecodeHelpers.entries()]
894
+ .sort(([left], [right]) => left.localeCompare(right))
895
+ .map(([helperName, node]) => ({ helperName, node }))
896
+ };
897
+ }
898
+ function renderTsRuntimeSupport(catalog) {
899
+ const support = collectTsSupport(catalog);
900
+ const lines = [];
901
+ if (support.needsBase93) {
902
+ lines.push(`const __anqstBase93Encode: (d: Uint8Array) => string = ${(0, base93_1.emitBase93Encoder)()};`);
903
+ lines.push(`const __anqstBase93Decode: (s: string) => Uint8Array = ${(0, base93_1.emitBase93Decoder)()};`);
904
+ lines.push("");
905
+ }
906
+ lines.push("function __anqstEncodeWire(bytes: number[], items: unknown[]): unknown {");
907
+ lines.push(" if (bytes.length === 0) {");
908
+ lines.push(" if (items.length === 1) return items[0];");
909
+ lines.push(" return items;");
910
+ lines.push(" }");
911
+ lines.push(" const out = new Array<unknown>(items.length + 1);");
912
+ if (support.needsBase93) {
913
+ lines.push(" out[0] = __anqstBase93Encode(Uint8Array.from(bytes));");
914
+ }
915
+ else {
916
+ lines.push(' throw new Error("AnQst boundary planner emitted unexpected blob bytes.");');
917
+ }
918
+ lines.push(" for (let i = 0; i < items.length; i += 1) out[i + 1] = items[i];");
919
+ lines.push(" if (out.length === 1) return out[0];");
920
+ lines.push(" return out;");
921
+ lines.push("}");
922
+ lines.push("");
923
+ const scalarEncodeKinds = new Set(support.scalarEncodeKinds);
924
+ const scalarDecodeKinds = new Set(support.scalarDecodeKinds);
925
+ const needsHelperScratch = scalarEncodeKinds.has("number") || scalarEncodeKinds.has("qint64") || scalarEncodeKinds.has("quint64");
926
+ if (needsHelperScratch || support.needsInlineScalarScratch) {
927
+ lines.push("const __anqstScalarScratchBuffer = new ArrayBuffer(8);");
928
+ lines.push("const __anqstScalarScratchView = new DataView(__anqstScalarScratchBuffer);");
929
+ lines.push("const __anqstScalarScratchBytes = new Uint8Array(__anqstScalarScratchBuffer);");
930
+ lines.push("");
931
+ }
932
+ if (scalarEncodeKinds.has("uint8"))
933
+ lines.push("function __anqstPushUint8(out: number[], value: number): void { out.push(value & 0xff); }");
934
+ if (scalarEncodeKinds.has("int8"))
935
+ lines.push("function __anqstPushInt8(out: number[], value: number): void { out.push((value as number) & 0xff); }");
936
+ if (scalarEncodeKinds.has("boolean"))
937
+ lines.push("function __anqstPushBool(out: number[], value: boolean): void { out.push(value ? 1 : 0); }");
938
+ if (scalarEncodeKinds.has("uint16") || scalarEncodeKinds.has("quint16"))
939
+ lines.push("function __anqstPushUint16(out: number[], value: number): void { const v = value & 0xffff; out.push(v & 0xff, (v >>> 8) & 0xff); }");
940
+ if (scalarEncodeKinds.has("int16") || scalarEncodeKinds.has("qint16"))
941
+ lines.push("function __anqstPushInt16(out: number[], value: number): void { const v = value & 0xffff; out.push(v & 0xff, (v >>> 8) & 0xff); }");
942
+ if (scalarEncodeKinds.has("uint32") || scalarEncodeKinds.has("quint32"))
943
+ lines.push("function __anqstPushUint32(out: number[], value: number): void { const v = value >>> 0; out.push(v & 0xff, (v >>> 8) & 0xff, (v >>> 16) & 0xff, (v >>> 24) & 0xff); }");
944
+ if (scalarEncodeKinds.has("int32") || scalarEncodeKinds.has("qint32"))
945
+ lines.push("function __anqstPushInt32(out: number[], value: number): void { const v = value >>> 0; out.push(v & 0xff, (v >>> 8) & 0xff, (v >>> 16) & 0xff, (v >>> 24) & 0xff); }");
946
+ if (scalarEncodeKinds.has("number"))
947
+ lines.push("function __anqstPushFloat64(out: number[], value: number): void { __anqstScalarScratchView.setFloat64(0, value, true); out.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!); }");
948
+ if (scalarEncodeKinds.has("qint64"))
949
+ lines.push("function __anqstPushBigInt64(out: number[], value: bigint): void { __anqstScalarScratchView.setBigInt64(0, value, true); out.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!); }");
950
+ if (scalarEncodeKinds.has("quint64"))
951
+ lines.push("function __anqstPushBigUint64(out: number[], value: bigint): void { __anqstScalarScratchView.setBigUint64(0, value, true); out.push(__anqstScalarScratchBytes[0]!, __anqstScalarScratchBytes[1]!, __anqstScalarScratchBytes[2]!, __anqstScalarScratchBytes[3]!, __anqstScalarScratchBytes[4]!, __anqstScalarScratchBytes[5]!, __anqstScalarScratchBytes[6]!, __anqstScalarScratchBytes[7]!); }");
952
+ if (scalarEncodeKinds.size > 0)
953
+ lines.push("");
954
+ if (scalarDecodeKinds.has("uint8"))
955
+ lines.push("function __anqstReadUint8(bytes: Uint8Array, _view: DataView, cursor: { offset: number }): number { return bytes[cursor.offset++]!; }");
956
+ if (scalarDecodeKinds.has("int8"))
957
+ lines.push("function __anqstReadInt8(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { return view.getInt8(cursor.offset++); }");
958
+ if (scalarDecodeKinds.has("boolean"))
959
+ lines.push("function __anqstReadBool(bytes: Uint8Array, _view: DataView, cursor: { offset: number }): boolean { return (bytes[cursor.offset++]! & 1) === 1; }");
960
+ if (scalarDecodeKinds.has("uint16") || scalarDecodeKinds.has("quint16"))
961
+ lines.push("function __anqstReadUint16(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { const value = view.getUint16(cursor.offset, true); cursor.offset += 2; return value; }");
962
+ if (scalarDecodeKinds.has("int16") || scalarDecodeKinds.has("qint16"))
963
+ lines.push("function __anqstReadInt16(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { const value = view.getInt16(cursor.offset, true); cursor.offset += 2; return value; }");
964
+ if (scalarDecodeKinds.has("uint32") || scalarDecodeKinds.has("quint32"))
965
+ lines.push("function __anqstReadUint32(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { const value = view.getUint32(cursor.offset, true); cursor.offset += 4; return value; }");
966
+ if (scalarDecodeKinds.has("int32") || scalarDecodeKinds.has("qint32"))
967
+ lines.push("function __anqstReadInt32(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { const value = view.getInt32(cursor.offset, true); cursor.offset += 4; return value; }");
968
+ if (scalarDecodeKinds.has("number"))
969
+ lines.push("function __anqstReadFloat64(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): number { const value = view.getFloat64(cursor.offset, true); cursor.offset += 8; return value; }");
970
+ if (scalarDecodeKinds.has("qint64"))
971
+ lines.push("function __anqstReadBigInt64(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): bigint { const value = view.getBigInt64(cursor.offset, true); cursor.offset += 8; return value; }");
972
+ if (scalarDecodeKinds.has("quint64"))
973
+ lines.push("function __anqstReadBigUint64(_bytes: Uint8Array, view: DataView, cursor: { offset: number }): bigint { const value = view.getBigUint64(cursor.offset, true); cursor.offset += 8; return value; }");
974
+ if (scalarDecodeKinds.size > 0)
975
+ lines.push("");
976
+ for (const { helperName, node } of support.finiteDomainEncodeHelpers) {
977
+ const safeName = (0, boundary_codec_model_1.sanitizeIdentifier)(helperName);
978
+ if (node.representation.kind === "coded-scalar") {
979
+ lines.push(`function __anqstFiniteDomainEncodeCode_${safeName}(value: ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)}): number {`);
980
+ lines.push(" let __code = 0;");
981
+ emitTsFiniteDomainCodeAssignment(node.domain, "value", "__code", lines, 1);
982
+ lines.push(" return __code;");
983
+ lines.push("}");
984
+ lines.push("");
985
+ }
986
+ else {
987
+ lines.push(`function __anqstFiniteDomainEncodeText_${safeName}(value: ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)}): string {`);
988
+ lines.push(` return ${tsFiniteDomainTextValueExpr(node.domain, "value")};`);
989
+ lines.push("}");
990
+ lines.push("");
991
+ }
992
+ }
993
+ for (const { helperName, node } of support.finiteDomainDecodeHelpers) {
994
+ const safeName = (0, boundary_codec_model_1.sanitizeIdentifier)(helperName);
995
+ if (node.representation.kind === "coded-scalar") {
996
+ lines.push(`function __anqstFiniteDomainDecodeCode_${safeName}(code: number): ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)} {`);
997
+ const decodeBody = [];
998
+ const valueExpr = emitTsFiniteDomainDecodeFromCode(node, "code", decodeBody, new TsEmitterContext(), 1);
999
+ lines.push(...decodeBody);
1000
+ lines.push(` return ${valueExpr};`);
1001
+ lines.push("}");
1002
+ lines.push("");
1003
+ }
1004
+ else {
1005
+ lines.push(`function __anqstFiniteDomainDecodeText_${safeName}(text: string): ${(0, boundary_codec_model_1.stripAnQstType)(node.typeText)} {`);
1006
+ const decodeBody = [];
1007
+ const valueExpr = emitTsFiniteDomainDecodeFromText(node, "text", decodeBody, new TsEmitterContext(), 1);
1008
+ lines.push(...decodeBody);
1009
+ lines.push(` return ${valueExpr};`);
1010
+ lines.push("}");
1011
+ lines.push("");
1012
+ }
1013
+ }
1014
+ const typedArrayInfoByKind = {
1015
+ Uint8Array: { ctor: "Uint8Array", bytesPerElement: 1 },
1016
+ Int8Array: { ctor: "Int8Array", bytesPerElement: 1 },
1017
+ Uint16Array: { ctor: "Uint16Array", bytesPerElement: 2 },
1018
+ Int16Array: { ctor: "Int16Array", bytesPerElement: 2 },
1019
+ Uint32Array: { ctor: "Uint32Array", bytesPerElement: 4 },
1020
+ Int32Array: { ctor: "Int32Array", bytesPerElement: 4 },
1021
+ Float32Array: { ctor: "Float32Array", bytesPerElement: 4 },
1022
+ Float64Array: { ctor: "Float64Array", bytesPerElement: 8 }
1023
+ };
1024
+ if (support.binaryHelperKinds.includes("ArrayBuffer")) {
1025
+ lines.push("function __anqstEncodeBinary_ArrayBuffer(value: ArrayBuffer): string { return __anqstBase93Encode(new Uint8Array(value)); }");
1026
+ lines.push("function __anqstDecodeBinary_ArrayBuffer(encoded: string): ArrayBuffer { const bytes = __anqstBase93Decode(encoded); if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) return bytes.buffer as ArrayBuffer; return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); }");
1027
+ }
1028
+ for (const kind of support.binaryHelperKinds.filter((binary) => binary !== "ArrayBuffer")) {
1029
+ const info = typedArrayInfoByKind[kind];
1030
+ const ctor = info.ctor;
1031
+ const bytesPerElement = info.bytesPerElement;
1032
+ lines.push(`function ${binaryEncodeHelperName(kind)}(value: ${ctor}): string { return __anqstBase93Encode(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)); }`);
1033
+ lines.push(`function ${binaryDecodeHelperName(kind)}(encoded: string): ${ctor} { const bytes = __anqstBase93Decode(encoded); if ((bytes.byteOffset % ${bytesPerElement}) === 0) return new ${ctor}(bytes.buffer, bytes.byteOffset, bytes.byteLength / ${bytesPerElement}); const copy = bytes.slice(); return new ${ctor}(copy.buffer, 0, copy.byteLength / ${bytesPerElement}); }`);
1034
+ }
1035
+ return lines.join("\n");
1036
+ }
1037
+ function renderTsBoundaryCodecHelpers(catalog) {
1038
+ if (catalog.plans.length === 0)
1039
+ return "";
1040
+ const runtime = renderTsRuntimeSupport(catalog);
1041
+ const codecs = catalog.plans.map((plan) => renderTsPlanCodec(plan)).join("\n\n");
1042
+ return `${runtime}\n\n${codecs}\n`;
1043
+ }
1044
+ function isNumericCppType(cppType) {
1045
+ return [
1046
+ "double",
1047
+ "qint64",
1048
+ "quint64",
1049
+ "qint32",
1050
+ "quint32",
1051
+ "qint16",
1052
+ "quint16",
1053
+ "qint8",
1054
+ "quint8",
1055
+ "std::int32_t",
1056
+ "std::uint32_t",
1057
+ "std::int16_t",
1058
+ "std::uint16_t",
1059
+ "std::int8_t",
1060
+ "std::uint8_t"
1061
+ ].includes(cppType);
1062
+ }
1063
+ function cppFiniteDomainVariantExpr(cppType, variant) {
1064
+ if (cppType === "QString")
1065
+ return `QStringLiteral(${JSON.stringify(String(variant.value))})`;
1066
+ if (cppType === "bool")
1067
+ return variant.value ? "true" : "false";
1068
+ if (isNumericCppType(cppType))
1069
+ return `static_cast<${cppType}>(${variant.value})`;
1070
+ return `${cppType}::${variant.symbolicName}`;
1071
+ }
1072
+ function cppFiniteDomainTextExpr(domain, cppType, valueExpr, lines, level, ctx) {
1073
+ const pad = indent(level).replace(/ /g, " ");
1074
+ const textVar = ctx.next("text");
1075
+ lines.push(`${pad}QString ${textVar};`);
1076
+ if (domain.primitive === "boolean" && cppType === "bool") {
1077
+ lines.push(`${pad}${textVar} = ${valueExpr} ? QStringLiteral("1") : QStringLiteral("0");`);
1078
+ return textVar;
1079
+ }
1080
+ if (domain.primitive === "string" && cppType === "QString") {
1081
+ lines.push(`${pad}${textVar} = ${valueExpr};`);
1082
+ return textVar;
1083
+ }
1084
+ if (domain.primitive === "number" && isNumericCppType(cppType)) {
1085
+ lines.push(`${pad}${textVar} = QString::number(${valueExpr});`);
1086
+ return textVar;
1087
+ }
1088
+ lines.push(`${pad}switch (${valueExpr}) {`);
1089
+ for (const variant of domain.variants) {
1090
+ const encodedText = domain.primitive === "boolean" ? (variant.value ? '"1"' : '"0"') : JSON.stringify(String(variant.value));
1091
+ lines.push(`${pad}case ${cppFiniteDomainVariantExpr(cppType, variant)}: ${textVar} = QStringLiteral(${encodedText}); break;`);
1092
+ }
1093
+ lines.push(`${pad}default: throw std::runtime_error("AnQst finite-domain encode received an unsupported value.");`);
1094
+ lines.push(`${pad}}`);
1095
+ return textVar;
1096
+ }
1097
+ function cppVariantToValueExpr(cppType, expr) {
1098
+ if (cppType === "QString")
1099
+ return `${expr}.toString()`;
1100
+ if (cppType === "bool")
1101
+ return `${expr}.toBool()`;
1102
+ if (cppType === "QVariantMap")
1103
+ return `${expr}.toMap()`;
1104
+ if (isNumericCppType(cppType))
1105
+ return `static_cast<${cppType}>(${expr}.toDouble())`;
1106
+ return `${expr}.value<${cppType}>()`;
1107
+ }
1108
+ function emitCppFiniteDomainCodeAssignment(domain, cppType, valueExpr, targetVar, lines, level) {
1109
+ const pad = indent(level).replace(/ /g, " ");
1110
+ const enumLike = cppType !== "QString" && cppType !== "bool" && !isNumericCppType(cppType);
1111
+ if (enumLike) {
1112
+ lines.push(`${pad}switch (${valueExpr}) {`);
1113
+ for (const variant of domain.variants) {
1114
+ lines.push(`${pad}case ${cppFiniteDomainVariantExpr(cppType, variant)}: ${targetVar} = ${variant.code}; break;`);
1115
+ }
1116
+ lines.push(`${pad}default: throw std::runtime_error("AnQst finite-domain encode received an unsupported value.");`);
1117
+ lines.push(`${pad}}`);
1118
+ return;
1119
+ }
1120
+ let started = false;
1121
+ for (const variant of domain.variants) {
1122
+ const keyword = started ? "else if" : "if";
1123
+ lines.push(`${pad}${keyword} (${valueExpr} == ${cppFiniteDomainVariantExpr(cppType, variant)}) {`);
1124
+ lines.push(`${pad} ${targetVar} = ${variant.code};`);
1125
+ lines.push(`${pad}}`);
1126
+ started = true;
1127
+ }
1128
+ lines.push(`${pad}else {`);
1129
+ lines.push(`${pad} throw std::runtime_error("AnQst finite-domain encode received an unsupported value.");`);
1130
+ lines.push(`${pad}}`);
1131
+ }
1132
+ function emitCppEncodeNode(node, valueExpr, lines, ctx, level, mapCppType, scope = "") {
1133
+ const pad = indent(level).replace(/ /g, " ");
1134
+ switch (node.nodeKind) {
1135
+ case "leaf":
1136
+ if (node.blobEntryId) {
1137
+ const leafKind = node.leaf.key;
1138
+ if (node.lowering.cppEncode.mode === "helper-call") {
1139
+ lines.push(`${pad}${cppScalarWriteHelper(leafKind)}(bytes, ${valueExpr});`);
1140
+ }
1141
+ else {
1142
+ emitCppInlineScalarWrite(leafKind, valueExpr, lines, ctx, level);
1143
+ }
1144
+ return;
1145
+ }
1146
+ if (node.leaf.region === "string") {
1147
+ if (node.selectedPacking === "text-packed" && node.leaf.key === "boolean") {
1148
+ lines.push(`${pad}items.push_back(${valueExpr} ? QStringLiteral("1") : QStringLiteral("0"));`);
1149
+ }
1150
+ else {
1151
+ lines.push(`${pad}items.push_back(${valueExpr});`);
1152
+ }
1153
+ return;
1154
+ }
1155
+ if (node.leaf.region === "binary") {
1156
+ if (node.lowering.cppEncode.mode === "helper-call") {
1157
+ lines.push(`${pad}items.push_back(anqstEncodeBinary(${valueExpr}));`);
1158
+ }
1159
+ else {
1160
+ lines.push(`${pad}items.push_back(${cppInlineBinaryEncodeExpr(valueExpr)});`);
1161
+ }
1162
+ return;
1163
+ }
1164
+ lines.push(`${pad}items.push_back(QVariant::fromValue(${valueExpr}));`);
1165
+ return;
1166
+ case "named":
1167
+ lines.push(`${pad}${cppNamedEncodeHelperName(node, scope)}(${valueExpr}, bytes, items);`);
1168
+ return;
1169
+ case "finite-domain": {
1170
+ const cppType = mapCppType(node.typeText, node.cppNameHintParts);
1171
+ if (node.representation.kind === "coded-scalar") {
1172
+ const codeVar = ctx.next("code");
1173
+ if (node.lowering.cppEncode.mode === "helper-call" && node.lowering.cppEncode.helperNameHint) {
1174
+ lines.push(`${pad}const std::uint32_t ${codeVar} = anqstFiniteDomainEncodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.cppEncode.helperNameHint)}(${valueExpr});`);
1175
+ lines.push(`${pad}${cppScalarWriteHelper(node.representation.scalarKind)}(bytes, static_cast<std::${node.representation.scalarKind}_t>(${codeVar}));`);
1176
+ }
1177
+ else if (node.lowering.cppEncode.mode === "helper-call") {
1178
+ lines.push(`${pad}std::uint32_t ${codeVar} = 0;`);
1179
+ emitCppFiniteDomainCodeAssignment(node.domain, cppType, valueExpr, codeVar, lines, level);
1180
+ lines.push(`${pad}${cppScalarWriteHelper(node.representation.scalarKind)}(bytes, static_cast<std::${node.representation.scalarKind}_t>(${codeVar}));`);
1181
+ }
1182
+ else {
1183
+ lines.push(`${pad}std::uint32_t ${codeVar} = 0;`);
1184
+ emitCppFiniteDomainCodeAssignment(node.domain, cppType, valueExpr, codeVar, lines, level);
1185
+ emitCppInlineScalarWrite(node.representation.scalarKind, `static_cast<std::${node.representation.scalarKind}_t>(${codeVar})`, lines, ctx, level);
1186
+ }
1187
+ }
1188
+ else {
1189
+ if (node.lowering.cppEncode.mode === "helper-call" && node.lowering.cppEncode.helperNameHint) {
1190
+ lines.push(`${pad}items.push_back(anqstFiniteDomainEncodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.cppEncode.helperNameHint)}(${valueExpr}));`);
1191
+ }
1192
+ else {
1193
+ const textExpr = cppFiniteDomainTextExpr(node.domain, cppType, valueExpr, lines, level, ctx);
1194
+ lines.push(`${pad}items.push_back(${textExpr});`);
1195
+ }
1196
+ }
1197
+ return;
1198
+ }
1199
+ case "array": {
1200
+ if (node.extentStrategy === "explicit-count") {
1201
+ emitCppInlineScalarWrite("uint32", `static_cast<std::uint32_t>(${valueExpr}.size())`, lines, ctx, level);
1202
+ }
1203
+ const itemVar = ctx.next("item");
1204
+ lines.push(`${pad}for (const auto& ${itemVar} : ${valueExpr}) {`);
1205
+ emitCppEncodeNode(node.element, itemVar, lines, ctx, level + 1, mapCppType, scope);
1206
+ lines.push(`${pad}}`);
1207
+ return;
1208
+ }
1209
+ case "struct":
1210
+ for (const field of node.fields) {
1211
+ const fieldExpr = `${valueExpr}.${field.name}`;
1212
+ if (field.optional) {
1213
+ const presentVar = ctx.next("present");
1214
+ lines.push(`${pad}const bool ${presentVar} = ${fieldExpr}.has_value();`);
1215
+ emitCppInlineScalarWrite("uint8", `${presentVar} ? 1u : 0u`, lines, ctx, level);
1216
+ lines.push(`${pad}if (${presentVar}) {`);
1217
+ emitCppEncodeNode(field.node, `${fieldExpr}.value()`, lines, ctx, level + 1, mapCppType, scope);
1218
+ lines.push(`${pad}}`);
1219
+ }
1220
+ else {
1221
+ emitCppEncodeNode(field.node, fieldExpr, lines, ctx, level, mapCppType, scope);
1222
+ }
1223
+ }
1224
+ }
1225
+ }
1226
+ function emitCppDecodeNode(node, lines, ctx, level, mapCppType, scope = "") {
1227
+ const pad = indent(level).replace(/ /g, " ");
1228
+ switch (node.nodeKind) {
1229
+ case "leaf":
1230
+ if (node.blobEntryId) {
1231
+ const leafKind = node.leaf.key;
1232
+ if (node.lowering.cppDecode.mode === "helper-call") {
1233
+ return `${cppScalarReadHelper(leafKind)}(blob, dataOffset)`;
1234
+ }
1235
+ return emitCppInlineScalarRead(leafKind, lines, ctx, level);
1236
+ }
1237
+ if (node.leaf.region === "string") {
1238
+ if (node.selectedPacking === "text-packed" && node.leaf.key === "boolean") {
1239
+ return `items[static_cast<int>(itemIndex++)].toString() == QStringLiteral("1")`;
1240
+ }
1241
+ return `items[static_cast<int>(itemIndex++)].toString()`;
1242
+ }
1243
+ if (node.leaf.region === "binary") {
1244
+ if (node.lowering.cppDecode.mode === "helper-call") {
1245
+ return `anqstDecodeBinary(items[static_cast<int>(itemIndex++)].toString())`;
1246
+ }
1247
+ return cppInlineBinaryDecodeExpr("items[static_cast<int>(itemIndex++)].toString()");
1248
+ }
1249
+ return cppVariantToValueExpr(mapCppType(node.typeText, node.cppNameHintParts), "items[static_cast<int>(itemIndex++)]");
1250
+ case "named":
1251
+ return `${cppNamedDecodeHelperName(node, scope)}(items, blob, itemIndex, dataOffset)`;
1252
+ case "finite-domain": {
1253
+ const cppType = mapCppType(node.typeText, node.cppNameHintParts);
1254
+ const valueVar = ctx.next("value");
1255
+ lines.push(`${pad}${cppType} ${valueVar}{};`);
1256
+ if (node.representation.kind === "coded-scalar") {
1257
+ const codeVar = ctx.next("code");
1258
+ if (node.lowering.cppDecode.mode === "helper-call" && node.lowering.cppDecode.helperNameHint) {
1259
+ lines.push(`${pad}const auto ${codeVar} = ${cppScalarReadHelper(node.representation.scalarKind)}(blob, dataOffset);`);
1260
+ lines.push(`${pad}${valueVar} = anqstFiniteDomainDecodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.cppDecode.helperNameHint)}(${codeVar});`);
1261
+ return valueVar;
1262
+ }
1263
+ if (node.lowering.cppDecode.mode === "helper-call") {
1264
+ lines.push(`${pad}const auto ${codeVar} = ${cppScalarReadHelper(node.representation.scalarKind)}(blob, dataOffset);`);
1265
+ }
1266
+ else {
1267
+ lines.push(`${pad}const auto ${codeVar} = ${emitCppInlineScalarRead(node.representation.scalarKind, lines, ctx, level)};`);
1268
+ }
1269
+ const firstVariant = node.domain.variants[0];
1270
+ lines.push(`${pad}${valueVar} = ${cppFiniteDomainVariantExpr(cppType, firstVariant)};`);
1271
+ lines.push(`${pad}switch (${codeVar}) {`);
1272
+ for (const variant of node.domain.variants) {
1273
+ lines.push(`${pad}case ${variant.code}: ${valueVar} = ${cppFiniteDomainVariantExpr(cppType, variant)}; break;`);
1274
+ }
1275
+ lines.push(`${pad}}`);
1276
+ }
1277
+ else {
1278
+ const textVar = ctx.next("text");
1279
+ lines.push(`${pad}const QString ${textVar} = items[static_cast<int>(itemIndex++)].toString();`);
1280
+ if (node.lowering.cppDecode.mode === "helper-call" && node.lowering.cppDecode.helperNameHint) {
1281
+ lines.push(`${pad}${valueVar} = anqstFiniteDomainDecodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(node.lowering.cppDecode.helperNameHint)}(${textVar});`);
1282
+ return valueVar;
1283
+ }
1284
+ const firstVariant = node.domain.variants[0];
1285
+ lines.push(`${pad}${valueVar} = ${cppFiniteDomainVariantExpr(cppType, firstVariant)};`);
1286
+ let started = false;
1287
+ for (const variant of node.domain.variants) {
1288
+ const keyword = started ? "else if" : "if";
1289
+ const encodedText = node.domain.primitive === "boolean" ? (variant.value ? '"1"' : '"0"') : JSON.stringify(String(variant.value));
1290
+ lines.push(`${pad}${keyword} (${textVar} == QStringLiteral(${encodedText})) {`);
1291
+ lines.push(`${pad} ${valueVar} = ${cppFiniteDomainVariantExpr(cppType, variant)};`);
1292
+ lines.push(`${pad}}`);
1293
+ started = true;
1294
+ }
1295
+ }
1296
+ return valueVar;
1297
+ }
1298
+ case "array": {
1299
+ const arrayType = mapCppType(node.typeText, node.cppNameHintParts);
1300
+ const arrayVar = ctx.next("array");
1301
+ const countVar = ctx.next("count");
1302
+ lines.push(`${pad}${arrayType} ${arrayVar};`);
1303
+ if (node.extentStrategy === "blob-tail") {
1304
+ const elementWidth = node.elementBlobWidthBytes ?? 1;
1305
+ const remainingVar = ctx.next("remaining");
1306
+ lines.push(`${pad}const std::size_t ${remainingVar} = blob.size() - dataOffset;`);
1307
+ lines.push(`${pad}const std::uint32_t ${countVar} = static_cast<std::uint32_t>(${remainingVar} / ${elementWidth});`);
1308
+ }
1309
+ else if (node.extentStrategy === "item-tail") {
1310
+ const elementItemCount = node.elementItemCount ?? 1;
1311
+ const remainingVar = ctx.next("remainingItems");
1312
+ lines.push(`${pad}const std::size_t ${remainingVar} = static_cast<std::size_t>(items.size()) - itemIndex;`);
1313
+ lines.push(`${pad}const std::uint32_t ${countVar} = static_cast<std::uint32_t>(${remainingVar} / ${elementItemCount});`);
1314
+ }
1315
+ else {
1316
+ lines.push(`${pad}const std::uint32_t ${countVar} = ${emitCppInlineScalarRead("uint32", lines, ctx, level)};`);
1317
+ }
1318
+ lines.push(`${pad}${arrayVar}.reserve(static_cast<qsizetype>(${countVar}));`);
1319
+ lines.push(`${pad}for (std::uint32_t i = 0; i < ${countVar}; ++i) {`);
1320
+ const elementExpr = emitCppDecodeNode(node.element, lines, ctx, level + 1, mapCppType, scope);
1321
+ lines.push(`${indent(level + 1).replace(/ /g, " ")}${arrayVar}.push_back(${elementExpr});`);
1322
+ lines.push(`${pad}}`);
1323
+ return arrayVar;
1324
+ }
1325
+ case "struct": {
1326
+ const valueType = mapCppType(node.typeText, node.cppNameHintParts);
1327
+ const valueVar = ctx.next("value");
1328
+ lines.push(`${pad}${valueType} ${valueVar}{};`);
1329
+ for (const field of node.fields) {
1330
+ if (field.optional) {
1331
+ const presentVar = ctx.next("present");
1332
+ lines.push(`${pad}const bool ${presentVar} = ${emitCppInlineScalarRead("uint8", lines, ctx, level)} != 0u;`);
1333
+ lines.push(`${pad}if (${presentVar}) {`);
1334
+ const fieldExpr = emitCppDecodeNode(field.node, lines, ctx, level + 1, mapCppType, scope);
1335
+ lines.push(`${indent(level + 1).replace(/ /g, " ")}${valueVar}.${field.name} = ${fieldExpr};`);
1336
+ lines.push(`${pad}}`);
1337
+ }
1338
+ else {
1339
+ const fieldExpr = emitCppDecodeNode(field.node, lines, ctx, level, mapCppType, scope);
1340
+ lines.push(`${pad}${valueVar}.${field.name} = ${fieldExpr};`);
1341
+ }
1342
+ }
1343
+ return valueVar;
1344
+ }
1345
+ }
1346
+ }
1347
+ function renderCppFastPathCodec(plan, mapCppType) {
1348
+ const encoderName = `encode${plan.codecId}`;
1349
+ const decoderName = `decode${plan.codecId}`;
1350
+ const cppType = mapCppType(plan.typeText, plan.root.cppNameHintParts);
1351
+ const trustedOnlyDecode = plan.decodePolicy === "trusted-only";
1352
+ if (plan.root.nodeKind === "leaf") {
1353
+ if (plan.root.blobEntryId) {
1354
+ const encodeCtx = new CppEmitterContext();
1355
+ const encodeLines = [];
1356
+ if (plan.root.lowering.cppEncode.mode === "helper-call") {
1357
+ encodeLines.push(` ${cppScalarWriteHelper(plan.root.leaf.key)}(bytes, value);`);
1358
+ }
1359
+ else {
1360
+ emitCppInlineScalarWrite(plan.root.leaf.key, "value", encodeLines, encodeCtx, 1);
1361
+ }
1362
+ const decodeBody = [];
1363
+ const decodeExpr = plan.root.lowering.cppDecode.mode === "helper-call"
1364
+ ? `${cppScalarReadHelper(plan.root.leaf.key)}(blob, dataOffset)`
1365
+ : emitCppInlineScalarRead(plan.root.leaf.key, decodeBody, new CppEmitterContext(), 1);
1366
+ return `inline QVariant ${encoderName}(const ${cppType}& value) {
1367
+ std::vector<std::uint8_t> bytes;
1368
+ ${encodeLines.join("\n")}
1369
+ return anqstBase93Encode(bytes);
1370
+ }
1371
+
1372
+ inline ${cppType} ${decoderName}(const QVariant& wire) {
1373
+ const std::vector<std::uint8_t> blob = anqstBase93Decode(wire.toString());
1374
+ std::size_t dataOffset = 0;
1375
+ ${decodeBody.join("\n")}
1376
+ const ${cppType} result = ${decodeExpr};
1377
+ ${trustedOnlyDecode ? "" : ' if (dataOffset != blob.size()) throw std::runtime_error("AnQst wire contained trailing blob bytes.");'}
1378
+ return result;
1379
+ }`;
1380
+ }
1381
+ const directEncode = plan.root.leaf.region === "string" && plan.root.selectedPacking === "text-packed" && plan.root.leaf.key === "boolean"
1382
+ ? `(value ? QStringLiteral("1") : QStringLiteral("0"))`
1383
+ : plan.root.leaf.region === "binary"
1384
+ ? plan.root.lowering.cppEncode.mode === "helper-call"
1385
+ ? `anqstEncodeBinary(value)`
1386
+ : cppInlineBinaryEncodeExpr("value")
1387
+ : plan.root.leaf.region === "dynamic"
1388
+ ? `QVariant::fromValue(value)`
1389
+ : "QVariant::fromValue(value)";
1390
+ const directDecode = plan.root.leaf.region === "string" && plan.root.selectedPacking === "text-packed" && plan.root.leaf.key === "boolean"
1391
+ ? `(wire.toString() == QStringLiteral("1"))`
1392
+ : plan.root.leaf.region === "string"
1393
+ ? "wire.toString()"
1394
+ : plan.root.leaf.region === "binary"
1395
+ ? plan.root.lowering.cppDecode.mode === "helper-call"
1396
+ ? "anqstDecodeBinary(wire.toString())"
1397
+ : cppInlineBinaryDecodeExpr("wire.toString()")
1398
+ : cppVariantToValueExpr(cppType, "wire");
1399
+ return `inline QVariant ${encoderName}(const ${cppType}& value) {
1400
+ return ${directEncode};
1401
+ }
1402
+
1403
+ inline ${cppType} ${decoderName}(const QVariant& wire) {
1404
+ return ${directDecode};
1405
+ }`;
1406
+ }
1407
+ if (plan.root.nodeKind === "finite-domain") {
1408
+ if (plan.root.representation.kind === "coded-scalar") {
1409
+ const encodeLines = [];
1410
+ const encodeCtx = new CppEmitterContext();
1411
+ if (plan.root.lowering.cppEncode.mode === "helper-call" && plan.root.lowering.cppEncode.helperNameHint) {
1412
+ encodeLines.push(` const std::uint32_t code = anqstFiniteDomainEncodeCode_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.cppEncode.helperNameHint)}(value);`);
1413
+ }
1414
+ else {
1415
+ encodeLines.push(" std::uint32_t code = 0;");
1416
+ emitCppFiniteDomainCodeAssignment(plan.root.domain, cppType, "value", "code", encodeLines, 1);
1417
+ }
1418
+ encodeLines.push(` std::vector<std::uint8_t> bytes;`);
1419
+ if (plan.root.lowering.cppEncode.mode === "helper-call") {
1420
+ encodeLines.push(` ${cppScalarWriteHelper(plan.root.representation.scalarKind)}(bytes, static_cast<std::${plan.root.representation.scalarKind}_t>(code));`);
1421
+ }
1422
+ else {
1423
+ emitCppInlineScalarWrite(plan.root.representation.scalarKind, `static_cast<std::${plan.root.representation.scalarKind}_t>(code)`, encodeLines, encodeCtx, 1);
1424
+ }
1425
+ encodeLines.push(` return anqstBase93Encode(bytes);`);
1426
+ const decodeLines = [" const std::vector<std::uint8_t> blob = anqstBase93Decode(wire.toString());", " std::size_t dataOffset = 0;"];
1427
+ const decodeBody = [];
1428
+ const expr = emitCppDecodeNode(plan.root, decodeBody, new CppEmitterContext(), 1, mapCppType);
1429
+ return `inline QVariant ${encoderName}(const ${cppType}& value) {
1430
+ ${encodeLines.join("\n")}
1431
+ }
1432
+
1433
+ inline ${cppType} ${decoderName}(const QVariant& wire) {
1434
+ ${decodeLines.join("\n")}
1435
+ ${decodeBody.join("\n")}
1436
+ const ${cppType} result = ${expr};
1437
+ ${trustedOnlyDecode ? "" : ' if (dataOffset != blob.size()) throw std::runtime_error("AnQst wire contained trailing blob bytes.");'}
1438
+ return result;
1439
+ }`;
1440
+ }
1441
+ const encodeLines = [];
1442
+ const textExpr = plan.root.lowering.cppEncode.mode === "helper-call" && plan.root.lowering.cppEncode.helperNameHint
1443
+ ? `anqstFiniteDomainEncodeText_${(0, boundary_codec_model_1.sanitizeIdentifier)(plan.root.lowering.cppEncode.helperNameHint)}(value)`
1444
+ : cppFiniteDomainTextExpr(plan.root.domain, cppType, "value", encodeLines, 1, new CppEmitterContext());
1445
+ const decodeBody = [];
1446
+ const expr = emitCppDecodeNode(plan.root, decodeBody, new CppEmitterContext(), 1, mapCppType);
1447
+ return `inline QVariant ${encoderName}(const ${cppType}& value) {
1448
+ ${encodeLines.join("\n")}
1449
+ return ${textExpr};
1450
+ }
1451
+
1452
+ inline ${cppType} ${decoderName}(const QVariant& wire) {
1453
+ const QVariantList items{wire};
1454
+ std::size_t itemIndex = 0;
1455
+ std::vector<std::uint8_t> blob;
1456
+ std::size_t dataOffset = 0;
1457
+ ${decodeBody.join("\n")}
1458
+ const ${cppType} result = ${expr};
1459
+ ${trustedOnlyDecode ? "" : ' if (dataOffset != blob.size()) throw std::runtime_error("AnQst wire contained trailing blob bytes.");'}
1460
+ ${trustedOnlyDecode ? "" : ' if (itemIndex != static_cast<std::size_t>(items.size())) throw std::runtime_error("AnQst wire contained trailing item payloads.");'}
1461
+ return result;
1462
+ }`;
1463
+ }
1464
+ return null;
1465
+ }
1466
+ function renderCppPlanCodec(plan, mapCppType) {
1467
+ const fastPath = renderCppFastPathCodec(plan, mapCppType);
1468
+ if (fastPath)
1469
+ return fastPath;
1470
+ const trustedOnlyDecode = plan.decodePolicy === "trusted-only";
1471
+ const cppType = mapCppType(plan.typeText, plan.root.cppNameHintParts);
1472
+ const namedNodes = [...collectNamedPlanNodes(plan.root).values()];
1473
+ const encodeLines = [];
1474
+ emitCppEncodeNode(plan.root, "value", encodeLines, new CppEmitterContext(), 1, mapCppType, plan.codecId);
1475
+ const decodeCtx = new CppEmitterContext();
1476
+ const decodeLines = [];
1477
+ const decodeExpr = emitCppDecodeNode(plan.root, decodeLines, decodeCtx, 1, mapCppType, plan.codecId);
1478
+ const encoderName = `encode${plan.codecId}`;
1479
+ const decoderName = `decode${plan.codecId}`;
1480
+ const namedDeclarations = namedNodes.map((node) => {
1481
+ const helperType = mapCppType(node.typeText, node.cppNameHintParts);
1482
+ return `inline void ${cppNamedEncodeHelperName(node, plan.codecId)}(
1483
+ const ${helperType}& value,
1484
+ std::vector<std::uint8_t>& bytes,
1485
+ QVariantList& items
1486
+ );
1487
+ inline ${helperType} ${cppNamedDecodeHelperName(node, plan.codecId)}(
1488
+ const QVariantList& items,
1489
+ const std::vector<std::uint8_t>& blob,
1490
+ std::size_t& itemIndex,
1491
+ std::size_t& dataOffset
1492
+ );`;
1493
+ }).join("\n");
1494
+ const namedHelpers = namedNodes.map((node) => {
1495
+ const helperType = mapCppType(node.typeText, node.cppNameHintParts);
1496
+ const helperEncodeLines = [];
1497
+ emitCppEncodeNode(node.target, "value", helperEncodeLines, new CppEmitterContext(), 1, mapCppType, plan.codecId);
1498
+ const helperDecodeLines = [];
1499
+ const helperDecodeExpr = emitCppDecodeNode(node.target, helperDecodeLines, new CppEmitterContext(), 1, mapCppType, plan.codecId);
1500
+ return `inline void ${cppNamedEncodeHelperName(node, plan.codecId)}(
1501
+ const ${helperType}& value,
1502
+ std::vector<std::uint8_t>& bytes,
1503
+ QVariantList& items
1504
+ ) {
1505
+ ${helperEncodeLines.join("\n")}
1506
+ }
1507
+
1508
+ inline ${helperType} ${cppNamedDecodeHelperName(node, plan.codecId)}(
1509
+ const QVariantList& items,
1510
+ const std::vector<std::uint8_t>& blob,
1511
+ std::size_t& itemIndex,
1512
+ std::size_t& dataOffset
1513
+ ) {
1514
+ ${helperDecodeLines.join("\n")}
1515
+ return ${helperDecodeExpr};
1516
+ }`;
1517
+ }).join("\n\n");
1518
+ return `${namedDeclarations ? `${namedDeclarations}\n\n` : ""}${namedHelpers ? `${namedHelpers}\n\n` : ""}inline QVariant ${encoderName}(const ${cppType}& value) {
1519
+ std::vector<std::uint8_t> bytes;
1520
+ QVariantList items;
1521
+ ${encodeLines.join("\n")}
1522
+ return anqstFinalizeWire(bytes, items);
1523
+ }
1524
+
1525
+ inline ${cppType} ${decoderName}(const QVariant& wire) {
1526
+ const QVariantList items = anqstNormalizeWireItems(wire);
1527
+ const std::vector<std::uint8_t> blob = ${plan.requirements.hasBlob ? `(items.isEmpty() ? std::vector<std::uint8_t>{} : anqstBase93Decode(items.value(0).toString()))` : "std::vector<std::uint8_t>{}"};
1528
+ std::size_t itemIndex = ${plan.requirements.hasBlob ? 1 : 0};
1529
+ std::size_t dataOffset = 0;
1530
+ ${decodeLines.join("\n")}
1531
+ const ${cppType} result = ${decodeExpr};
1532
+ ${trustedOnlyDecode ? "" : (plan.requirements.hasBlob ? " if (dataOffset != blob.size()) throw std::runtime_error(\"AnQst wire contained trailing blob bytes.\");" : "")}
1533
+ ${trustedOnlyDecode ? "" : ' if (itemIndex != static_cast<std::size_t>(items.size())) throw std::runtime_error("AnQst wire contained trailing item payloads.");'}
1534
+ return result;
1535
+ }`;
1536
+ }
1537
+ function collectCppSupport(catalog) {
1538
+ const scalarEncodeKinds = new Set();
1539
+ const scalarDecodeKinds = new Set();
1540
+ let needsBase93 = false;
1541
+ let needsBinaryHelpers = false;
1542
+ const finiteDomainEncodeHelpers = new Map();
1543
+ const finiteDomainDecodeHelpers = new Map();
1544
+ for (const plan of catalog.plans) {
1545
+ if (plan.requirements.hasBlob || plan.requirements.usedBinaryLeafKinds.length > 0) {
1546
+ needsBase93 = true;
1547
+ }
1548
+ if (plan.requirements.cppHelperRequirements.binaryEncodeKinds.length > 0
1549
+ || plan.requirements.cppHelperRequirements.binaryDecodeKinds.length > 0) {
1550
+ needsBinaryHelpers = true;
1551
+ }
1552
+ for (const kind of plan.requirements.cppHelperRequirements.scalarEncodeKinds)
1553
+ scalarEncodeKinds.add(kind);
1554
+ for (const kind of plan.requirements.cppHelperRequirements.scalarDecodeKinds)
1555
+ scalarDecodeKinds.add(kind);
1556
+ for (const node of collectFiniteDomainPlanNodes(plan.root)) {
1557
+ if (node.lowering.cppEncode.mode === "helper-call" && node.lowering.cppEncode.helperNameHint) {
1558
+ finiteDomainEncodeHelpers.set(node.lowering.cppEncode.helperNameHint, node);
1559
+ }
1560
+ if (node.lowering.cppDecode.mode === "helper-call" && node.lowering.cppDecode.helperNameHint) {
1561
+ finiteDomainDecodeHelpers.set(node.lowering.cppDecode.helperNameHint, node);
1562
+ }
1563
+ }
1564
+ }
1565
+ const sortByName = (values) => [...values].sort();
1566
+ return {
1567
+ needsBase93,
1568
+ scalarEncodeKinds: sortByName(scalarEncodeKinds),
1569
+ scalarDecodeKinds: sortByName(scalarDecodeKinds),
1570
+ needsBinaryHelpers,
1571
+ finiteDomainEncodeHelpers: [...finiteDomainEncodeHelpers.entries()]
1572
+ .sort(([left], [right]) => left.localeCompare(right))
1573
+ .map(([helperName, node]) => ({ helperName, node })),
1574
+ finiteDomainDecodeHelpers: [...finiteDomainDecodeHelpers.entries()]
1575
+ .sort(([left], [right]) => left.localeCompare(right))
1576
+ .map(([helperName, node]) => ({ helperName, node }))
1577
+ };
1578
+ }
1579
+ function renderCppRuntimeSupport(catalog, mapCppType) {
1580
+ const support = collectCppSupport(catalog);
1581
+ const lines = [];
1582
+ lines.push("inline QVariantList anqstNormalizeWireItems(const QVariant& wire) {");
1583
+ lines.push(" return wire.type() == QVariant::List ? wire.toList() : QVariantList{wire};");
1584
+ lines.push("}");
1585
+ lines.push("");
1586
+ lines.push("inline QVariant anqstFinalizeWire(const std::vector<std::uint8_t>& bytes, const QVariantList& items) {");
1587
+ lines.push(" if (bytes.empty()) {");
1588
+ lines.push(" if (items.size() == 1) return items.front();");
1589
+ lines.push(" return items;");
1590
+ lines.push(" }");
1591
+ if (!support.needsBase93) {
1592
+ lines.push(' throw std::runtime_error("AnQst boundary planner emitted unexpected blob bytes.");');
1593
+ lines.push("}");
1594
+ lines.push("");
1595
+ }
1596
+ else {
1597
+ lines.push(" QVariantList out;");
1598
+ lines.push(" out.reserve(static_cast<qsizetype>(items.size() + 1));");
1599
+ lines.push(" out.push_back(anqstBase93Encode(bytes));");
1600
+ lines.push(" for (const auto& item : items) out.push_back(item);");
1601
+ lines.push(" return out;");
1602
+ lines.push("}");
1603
+ lines.push("");
1604
+ }
1605
+ const scalarEncodeKinds = new Set(support.scalarEncodeKinds);
1606
+ const scalarDecodeKinds = new Set(support.scalarDecodeKinds);
1607
+ if (scalarEncodeKinds.has("uint8"))
1608
+ lines.push("inline void anqstPushUint8(std::vector<std::uint8_t>& out, std::uint8_t value) { out.push_back(value); }");
1609
+ if (scalarEncodeKinds.has("int8"))
1610
+ lines.push("inline void anqstPushInt8(std::vector<std::uint8_t>& out, std::int8_t value) { out.push_back(static_cast<std::uint8_t>(value)); }");
1611
+ if (scalarEncodeKinds.has("boolean"))
1612
+ lines.push("inline void anqstPushBool(std::vector<std::uint8_t>& out, bool value) { out.push_back(value ? 1u : 0u); }");
1613
+ if (scalarEncodeKinds.has("uint16") || scalarEncodeKinds.has("quint16"))
1614
+ lines.push("inline void anqstPushUint16(std::vector<std::uint8_t>& out, std::uint16_t value) { out.push_back(static_cast<std::uint8_t>(value & 0xffu)); out.push_back(static_cast<std::uint8_t>((value >> 8) & 0xffu)); }");
1615
+ if (scalarEncodeKinds.has("int16"))
1616
+ lines.push("inline void anqstPushInt16(std::vector<std::uint8_t>& out, std::int16_t value) { anqstPushUint16(out, static_cast<std::uint16_t>(value)); }");
1617
+ if (scalarEncodeKinds.has("quint16"))
1618
+ lines.push("inline void anqstPushQuint16(std::vector<std::uint8_t>& out, quint16 value) { anqstPushUint16(out, static_cast<std::uint16_t>(value)); }");
1619
+ if (scalarEncodeKinds.has("qint16"))
1620
+ lines.push("inline void anqstPushQint16(std::vector<std::uint8_t>& out, qint16 value) { anqstPushInt16(out, static_cast<std::int16_t>(value)); }");
1621
+ if (scalarEncodeKinds.has("uint32") || scalarEncodeKinds.has("quint32"))
1622
+ lines.push("inline void anqstPushUint32(std::vector<std::uint8_t>& out, std::uint32_t value) { out.push_back(static_cast<std::uint8_t>(value & 0xffu)); out.push_back(static_cast<std::uint8_t>((value >> 8) & 0xffu)); out.push_back(static_cast<std::uint8_t>((value >> 16) & 0xffu)); out.push_back(static_cast<std::uint8_t>((value >> 24) & 0xffu)); }");
1623
+ if (scalarEncodeKinds.has("int32"))
1624
+ lines.push("inline void anqstPushInt32(std::vector<std::uint8_t>& out, std::int32_t value) { anqstPushUint32(out, static_cast<std::uint32_t>(value)); }");
1625
+ if (scalarEncodeKinds.has("quint32"))
1626
+ lines.push("inline void anqstPushQuint32(std::vector<std::uint8_t>& out, quint32 value) { anqstPushUint32(out, static_cast<std::uint32_t>(value)); }");
1627
+ if (scalarEncodeKinds.has("qint32"))
1628
+ lines.push("inline void anqstPushQint32(std::vector<std::uint8_t>& out, qint32 value) { anqstPushInt32(out, static_cast<std::int32_t>(value)); }");
1629
+ if (scalarEncodeKinds.has("quint64"))
1630
+ lines.push("inline void anqstPushQuint64(std::vector<std::uint8_t>& out, quint64 value) { for (int shift = 0; shift < 64; shift += 8) out.push_back(static_cast<std::uint8_t>((static_cast<std::uint64_t>(value) >> shift) & 0xffu)); }");
1631
+ if (scalarEncodeKinds.has("qint64"))
1632
+ lines.push("inline void anqstPushQint64(std::vector<std::uint8_t>& out, qint64 value) { anqstPushQuint64(out, static_cast<quint64>(value)); }");
1633
+ if (scalarEncodeKinds.has("number"))
1634
+ lines.push("inline void anqstPushFloat64(std::vector<std::uint8_t>& out, double value) { std::uint64_t bits = 0; std::memcpy(&bits, &value, sizeof(bits)); anqstPushQuint64(out, bits); }");
1635
+ if (scalarEncodeKinds.size > 0)
1636
+ lines.push("");
1637
+ if (scalarDecodeKinds.has("uint8"))
1638
+ lines.push("inline std::uint8_t anqstReadUint8(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return bytes[offset++]; }");
1639
+ if (scalarDecodeKinds.has("int8"))
1640
+ lines.push("inline std::int8_t anqstReadInt8(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<std::int8_t>(bytes[offset++]); }");
1641
+ if (scalarDecodeKinds.has("boolean"))
1642
+ lines.push("inline bool anqstReadBool(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return (bytes[offset++] & 1u) != 0u; }");
1643
+ if (scalarDecodeKinds.has("uint16") || scalarDecodeKinds.has("quint16"))
1644
+ lines.push("inline std::uint16_t anqstReadUint16(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { const std::uint16_t b0 = bytes[offset]; const std::uint16_t b1 = bytes[offset + 1]; offset += 2; return static_cast<std::uint16_t>(b0 | (b1 << 8)); }");
1645
+ if (scalarDecodeKinds.has("int16"))
1646
+ lines.push("inline std::int16_t anqstReadInt16(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<std::int16_t>(anqstReadUint16(bytes, offset)); }");
1647
+ if (scalarDecodeKinds.has("quint16"))
1648
+ lines.push("inline quint16 anqstReadQuint16(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<quint16>(anqstReadUint16(bytes, offset)); }");
1649
+ if (scalarDecodeKinds.has("qint16"))
1650
+ lines.push("inline qint16 anqstReadQint16(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<qint16>(anqstReadInt16(bytes, offset)); }");
1651
+ if (scalarDecodeKinds.has("uint32") || scalarDecodeKinds.has("quint32"))
1652
+ lines.push("inline std::uint32_t anqstReadUint32(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { const std::uint32_t b0 = bytes[offset]; const std::uint32_t b1 = bytes[offset + 1]; const std::uint32_t b2 = bytes[offset + 2]; const std::uint32_t b3 = bytes[offset + 3]; offset += 4; return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); }");
1653
+ if (scalarDecodeKinds.has("int32"))
1654
+ lines.push("inline std::int32_t anqstReadInt32(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<std::int32_t>(anqstReadUint32(bytes, offset)); }");
1655
+ if (scalarDecodeKinds.has("quint32"))
1656
+ lines.push("inline quint32 anqstReadQuint32(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<quint32>(anqstReadUint32(bytes, offset)); }");
1657
+ if (scalarDecodeKinds.has("qint32"))
1658
+ lines.push("inline qint32 anqstReadQint32(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<qint32>(anqstReadInt32(bytes, offset)); }");
1659
+ if (scalarDecodeKinds.has("quint64"))
1660
+ lines.push("inline std::uint64_t anqstReadQuint64(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { std::uint64_t value = 0; for (int shift = 0; shift < 64; shift += 8) value |= (static_cast<std::uint64_t>(bytes[offset++]) << shift); return value; }");
1661
+ if (scalarDecodeKinds.has("qint64"))
1662
+ lines.push("inline std::int64_t anqstReadQint64(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { return static_cast<std::int64_t>(anqstReadQuint64(bytes, offset)); }");
1663
+ if (scalarDecodeKinds.has("number"))
1664
+ lines.push("inline double anqstReadFloat64(const std::vector<std::uint8_t>& bytes, std::size_t& offset) { const std::uint64_t bits = anqstReadQuint64(bytes, offset); double value = 0; std::memcpy(&value, &bits, sizeof(value)); return value; }");
1665
+ if (scalarDecodeKinds.size > 0)
1666
+ lines.push("");
1667
+ for (const { helperName, node } of support.finiteDomainEncodeHelpers) {
1668
+ const safeName = (0, boundary_codec_model_1.sanitizeIdentifier)(helperName);
1669
+ const cppType = mapCppType(node.typeText, node.cppNameHintParts);
1670
+ if (node.representation.kind === "coded-scalar") {
1671
+ lines.push(`inline std::uint32_t anqstFiniteDomainEncodeCode_${safeName}(const ${cppType}& value) {`);
1672
+ lines.push(" std::uint32_t code = 0;");
1673
+ emitCppFiniteDomainCodeAssignment(node.domain, cppType, "value", "code", lines, 1);
1674
+ lines.push(" return code;");
1675
+ lines.push("}");
1676
+ lines.push("");
1677
+ }
1678
+ else {
1679
+ const encodeBody = [];
1680
+ const textExpr = cppFiniteDomainTextExpr(node.domain, cppType, "value", encodeBody, 1, new CppEmitterContext());
1681
+ lines.push(`inline QString anqstFiniteDomainEncodeText_${safeName}(const ${cppType}& value) {`);
1682
+ lines.push(...encodeBody);
1683
+ lines.push(` return ${textExpr};`);
1684
+ lines.push("}");
1685
+ lines.push("");
1686
+ }
1687
+ }
1688
+ for (const { helperName, node } of support.finiteDomainDecodeHelpers) {
1689
+ const safeName = (0, boundary_codec_model_1.sanitizeIdentifier)(helperName);
1690
+ const cppType = mapCppType(node.typeText, node.cppNameHintParts);
1691
+ if (node.representation.kind === "coded-scalar") {
1692
+ lines.push(`inline ${cppType} anqstFiniteDomainDecodeCode_${safeName}(std::uint32_t code) {`);
1693
+ const firstVariant = node.domain.variants[0];
1694
+ lines.push(` ${cppType} value = ${cppFiniteDomainVariantExpr(cppType, firstVariant)};`);
1695
+ lines.push(" switch (code) {");
1696
+ for (const variant of node.domain.variants) {
1697
+ lines.push(` case ${variant.code}: value = ${cppFiniteDomainVariantExpr(cppType, variant)}; break;`);
1698
+ }
1699
+ lines.push(" }");
1700
+ lines.push(" return value;");
1701
+ lines.push("}");
1702
+ lines.push("");
1703
+ }
1704
+ else {
1705
+ lines.push(`inline ${cppType} anqstFiniteDomainDecodeText_${safeName}(const QString& text) {`);
1706
+ const firstVariant = node.domain.variants[0];
1707
+ lines.push(` ${cppType} value = ${cppFiniteDomainVariantExpr(cppType, firstVariant)};`);
1708
+ let started = false;
1709
+ for (const variant of node.domain.variants) {
1710
+ const keyword = started ? "else if" : "if";
1711
+ const encodedText = node.domain.primitive === "boolean" ? (variant.value ? '"1"' : '"0"') : JSON.stringify(String(variant.value));
1712
+ lines.push(` ${keyword} (text == QStringLiteral(${encodedText})) { value = ${cppFiniteDomainVariantExpr(cppType, variant)}; }`);
1713
+ started = true;
1714
+ }
1715
+ lines.push(" return value;");
1716
+ lines.push("}");
1717
+ lines.push("");
1718
+ }
1719
+ }
1720
+ if (support.needsBinaryHelpers) {
1721
+ lines.push("inline QString anqstEncodeBinary(const QByteArray& value) {");
1722
+ lines.push(" return anqstBase93Encode(std::vector<std::uint8_t>(value.begin(), value.end()));");
1723
+ lines.push("}");
1724
+ lines.push("");
1725
+ lines.push("inline QByteArray anqstDecodeBinary(const QString& encoded) {");
1726
+ lines.push(" const auto bytes = anqstBase93Decode(encoded);");
1727
+ lines.push(" return QByteArray(reinterpret_cast<const char*>(bytes.data()), static_cast<int>(bytes.size()));");
1728
+ lines.push("}");
1729
+ }
1730
+ return lines.join("\n");
1731
+ }
1732
+ function renderCppBoundaryCodecHelpers(catalog, mapCppType) {
1733
+ if (catalog.plans.length === 0)
1734
+ return "";
1735
+ const runtime = renderCppRuntimeSupport(catalog, mapCppType);
1736
+ const codecs = catalog.plans.map((plan) => renderCppPlanCodec(plan, mapCppType)).join("\n\n");
1737
+ return `${runtime}\n\n${codecs}\n`;
1738
+ }