@exornea/zeno-compiler 1.7.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/bin/zeno-codegen.mjs +79 -31
  2. package/bin/zeno-diff-layout.mjs +60 -0
  3. package/bin/zeno-inspect.mjs +67 -0
  4. package/dist/analyzer.d.ts.map +1 -1
  5. package/dist/analyzer.js +48 -26
  6. package/dist/analyzer.js.map +1 -1
  7. package/dist/emitter-ast.d.ts +8 -0
  8. package/dist/emitter-ast.d.ts.map +1 -0
  9. package/dist/emitter-ast.js +34 -0
  10. package/dist/emitter-ast.js.map +1 -0
  11. package/dist/emitter-capabilities.d.ts +8 -0
  12. package/dist/emitter-capabilities.d.ts.map +1 -0
  13. package/dist/emitter-capabilities.js +59 -0
  14. package/dist/emitter-capabilities.js.map +1 -0
  15. package/dist/emitter-fields.d.ts +3 -0
  16. package/dist/emitter-fields.d.ts.map +1 -0
  17. package/dist/emitter-fields.js +180 -0
  18. package/dist/emitter-fields.js.map +1 -0
  19. package/dist/emitter-input.d.ts +3 -0
  20. package/dist/emitter-input.d.ts.map +1 -0
  21. package/dist/emitter-input.js +45 -0
  22. package/dist/emitter-input.js.map +1 -0
  23. package/dist/emitter-names.d.ts +5 -0
  24. package/dist/emitter-names.d.ts.map +1 -0
  25. package/dist/emitter-names.js +10 -0
  26. package/dist/emitter-names.js.map +1 -0
  27. package/dist/emitter-runtime-imports.d.ts +3 -0
  28. package/dist/emitter-runtime-imports.d.ts.map +1 -0
  29. package/dist/emitter-runtime-imports.js +73 -0
  30. package/dist/emitter-runtime-imports.js.map +1 -0
  31. package/dist/emitter-scan-kernels.d.ts +10 -0
  32. package/dist/emitter-scan-kernels.d.ts.map +1 -0
  33. package/dist/emitter-scan-kernels.js +174 -0
  34. package/dist/emitter-scan-kernels.js.map +1 -0
  35. package/dist/emitter-static-accessors.d.ts +4 -0
  36. package/dist/emitter-static-accessors.d.ts.map +1 -0
  37. package/dist/emitter-static-accessors.js +114 -0
  38. package/dist/emitter-static-accessors.js.map +1 -0
  39. package/dist/emitter-template.d.ts +7 -0
  40. package/dist/emitter-template.d.ts.map +1 -1
  41. package/dist/emitter-template.js +8 -3
  42. package/dist/emitter-template.js.map +1 -1
  43. package/dist/emitter-writers.d.ts +4 -0
  44. package/dist/emitter-writers.d.ts.map +1 -0
  45. package/dist/emitter-writers.js +246 -0
  46. package/dist/emitter-writers.js.map +1 -0
  47. package/dist/emitter.d.ts +9 -1
  48. package/dist/emitter.d.ts.map +1 -1
  49. package/dist/emitter.js +31 -660
  50. package/dist/emitter.js.map +1 -1
  51. package/dist/index.d.ts +5 -2
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +5 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/layout-manifest.d.ts +37 -0
  56. package/dist/layout-manifest.d.ts.map +1 -0
  57. package/dist/layout-manifest.js +251 -0
  58. package/dist/layout-manifest.js.map +1 -0
  59. package/dist/lowering.d.ts +5 -2
  60. package/dist/lowering.d.ts.map +1 -1
  61. package/dist/lowering.js +86 -27
  62. package/dist/lowering.js.map +1 -1
  63. package/dist/source-map.d.ts +10 -0
  64. package/dist/source-map.d.ts.map +1 -0
  65. package/dist/source-map.js +371 -0
  66. package/dist/source-map.js.map +1 -0
  67. package/dist/validator.d.ts.map +1 -1
  68. package/dist/validator.js +34 -16
  69. package/dist/validator.js.map +1 -1
  70. package/package.json +5 -3
package/dist/emitter.js CHANGED
@@ -1,10 +1,21 @@
1
- import { scalarGetterMethod, scalarSetterMethod, scalarTsType, } from "@exornea/zeno-schema";
2
- import { canWriteFixedArrayStructElement, collectFixedArrayRuntimeImports, emitFixedArrayFieldAccessor, emitFixedArrayObjectFieldWrite, fixedArrayInputElementType, } from "./emitter-fixed-array.js";
1
+ import {} from "@exornea/zeno-schema";
2
+ import { emitAstCheckedSource } from "./emitter-ast.js";
3
+ import { emitField } from "./emitter-fields.js";
4
+ import { emitInputInterface } from "./emitter-input.js";
5
+ import { toLittleEndianLiteral, toPascalCase } from "./emitter-names.js";
6
+ import { collectRuntimeImports } from "./emitter-runtime-imports.js";
7
+ import { emitScanRangeHelper, hasScanKernels, normalizeScanKernelMode, } from "./emitter-scan-kernels.js";
8
+ import { emitStaticFieldAccessor } from "./emitter-static-accessors.js";
3
9
  import { method } from "./emitter-template.js";
10
+ import { emitDynamicWriterMethods, emitObjectWriterMethod } from "./emitter-writers.js";
11
+ import { createProjectionSourceMap } from "./source-map.js";
4
12
  export function emitStructView(layout, options = {}) {
5
13
  return emitProjectionFile([layout], options);
6
14
  }
7
15
  export function emitProjectionFile(layouts, options = {}) {
16
+ return emitAstCheckedSource(emitProjectionFileText(layouts, options), "zeno.view.ts").code;
17
+ }
18
+ function emitProjectionFileText(layouts, options = {}) {
8
19
  const lines = [];
9
20
  const runtimeImports = collectRuntimeImports(layouts);
10
21
  const layoutMap = new Map(layouts.map((layout) => [layout.name, layout]));
@@ -20,109 +31,13 @@ export function emitProjectionFile(layouts, options = {}) {
20
31
  }
21
32
  return lines.join("\n");
22
33
  }
23
- function collectRuntimeImports(layouts) {
24
- const imports = new Set(["ProjectionView"]);
25
- const layoutMap = new Map(layouts.map((layout) => [layout.name, layout]));
26
- for (const layout of layouts) {
27
- if (hasTailWriterFields(layout, layoutMap)) {
28
- imports.add("DynamicLayoutWriter");
29
- }
30
- for (const field of layout.fields) {
31
- collectFieldRuntimeImports(field, imports);
32
- }
33
- }
34
- return Array.from(imports).sort();
35
- }
36
- function collectFieldRuntimeImports(field, imports) {
37
- switch (field.kind) {
38
- case "fixed-bytes":
39
- imports.add("fixedBytesView");
40
- imports.add("writeFixedBytes");
41
- return;
42
- case "fixed-string":
43
- imports.add("decodeFixedText");
44
- imports.add("fixedBytesView");
45
- imports.add("writeFixedText");
46
- return;
47
- case "dynamic-string":
48
- imports.add("Utf8SpanView");
49
- return;
50
- case "dynamic-bytes":
51
- imports.add("BytesSpanView");
52
- return;
53
- case "vector":
54
- switch (field.element.kind) {
55
- case "scalar":
56
- imports.add("ScalarVectorView");
57
- return;
58
- case "dynamic-string":
59
- imports.add("Utf8VectorView");
60
- return;
61
- case "dynamic-bytes":
62
- imports.add("BytesVectorView");
63
- return;
64
- case "fixed-bytes":
65
- imports.add("FixedBytesVectorView");
66
- return;
67
- case "fixed-string":
68
- imports.add("FixedStringVectorView");
69
- return;
70
- case "struct":
71
- imports.add("StructVectorView");
72
- return;
73
- case "pointer":
74
- imports.add("PointerVectorView");
75
- return;
76
- default:
77
- return;
78
- }
79
- case "fixed-array":
80
- collectFixedArrayRuntimeImports(field.element, imports);
81
- return;
82
- default:
83
- return;
84
- }
85
- }
86
- function emitInputInterface(layout) {
87
- const lines = [`export interface ${layout.name}ViewInput {`];
88
- for (const field of layout.fields) {
89
- lines.push(` readonly ${field.name}: ${fieldInputType(field)};`);
90
- }
91
- lines.push("}");
92
- return lines;
93
- }
94
- function fieldInputType(field) {
95
- switch (field.kind) {
96
- case "scalar":
97
- return scalarTsType(field.scalar);
98
- case "fixed-bytes":
99
- case "dynamic-bytes":
100
- return "ArrayLike<number> | Uint8Array";
101
- case "fixed-string":
102
- case "dynamic-string":
103
- return "string";
104
- case "struct":
105
- return `${field.typeName}ViewInput`;
106
- case "pointer":
107
- return "number | null";
108
- case "fixed-array":
109
- return `readonly ${fixedArrayInputElementType(field.element)}[]`;
110
- case "vector":
111
- switch (field.element.kind) {
112
- case "scalar":
113
- return `readonly ${scalarTsType(field.element.scalar)}[]`;
114
- case "fixed-bytes":
115
- case "dynamic-bytes":
116
- return "readonly (ArrayLike<number> | Uint8Array)[]";
117
- case "fixed-string":
118
- case "dynamic-string":
119
- return "readonly string[]";
120
- case "struct":
121
- return `readonly ${field.element.typeName}ViewInput[]`;
122
- case "pointer":
123
- return "readonly (number | null)[]";
124
- }
125
- }
34
+ export function emitProjectionFileWithSourceMap(layouts, generatedFile, options = {}) {
35
+ const sourceMapUrl = `${generatedFile.split(/[\\/]/).pop() ?? generatedFile}.map`;
36
+ const code = `${emitAstCheckedSource(emitProjectionFileText(layouts, options), generatedFile).code}\n//# sourceMappingURL=${sourceMapUrl}\n`;
37
+ return {
38
+ code,
39
+ sourceMap: createProjectionSourceMap(code, layouts, generatedFile),
40
+ };
126
41
  }
127
42
  function emitLayoutConstants(layout) {
128
43
  const lines = [];
@@ -134,8 +49,8 @@ function emitLayoutConstants(layout) {
134
49
  return lines;
135
50
  }
136
51
  function emitStructClass(layout, options, layoutMap) {
137
- const scalarFields = layout.fields.filter((field) => field.kind === "scalar");
138
52
  const littleEndianDefault = toLittleEndianLiteral(layout);
53
+ const scanKernelMode = normalizeScanKernelMode(options.scanKernels);
139
54
  const lines = [`export class ${layout.name}View extends ProjectionView {`];
140
55
  lines.push(...method `
141
56
  static readonly byteLength = ${layout.byteLength};
@@ -162,585 +77,41 @@ private static assertPointerTargetRange(view: DataView, targetOffset: number, by
162
77
  }
163
78
  }`);
164
79
  }
165
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
80
+ if (hasScanKernels(layout, scanKernelMode)) {
166
81
  lines.push("");
167
- lines.push(...method `${scalarFields.map((field) => `private $${field.name}Offset = ${field.offset};`)}`);
82
+ lines.push(...emitScanRangeHelper(layout));
168
83
  }
169
84
  lines.push("");
170
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
171
- lines.push(...method `
172
- constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
173
- super(view, baseOffset, littleEndian);
174
- this.$refreshOffsets();
175
- }
176
-
177
- private $refreshOffsets(): void {
178
- ${scalarFields.map((field) => ` this.$${field.name}Offset = this.baseOffset + ${field.offset};`)}
179
- }
180
-
181
- override rebase(baseOffset: number): this {
182
- super.rebase(baseOffset);
183
- this.$refreshOffsets();
184
- return this;
185
- }
186
-
187
- override rebaseUnchecked(baseOffset: number): this {
188
- super.rebaseUnchecked(baseOffset);
189
- this.$refreshOffsets();
190
- return this;
191
- }`);
192
- }
193
- else {
194
- lines.push(...method `
85
+ lines.push(...method `
195
86
  constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
196
87
  super(view, baseOffset, littleEndian);
197
88
  }`);
198
- }
199
89
  lines.push("");
200
90
  lines.push(...method `
201
91
  static at(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${layout.name}View {
202
92
  return new ${layout.name}View(view, baseOffset, littleEndian);
203
93
  }`);
204
94
  lines.push("");
205
- if (options.optimizeCursorOffsets && scalarFields.length > 0) {
206
- lines.push(...method `
207
- moveTo(index: number): this {
208
- this.moveToIndex(index, ${layout.name}View.byteLength);
209
- this.$refreshOffsets();
210
- return this;
211
- }`);
212
- }
213
- else {
214
- lines.push(...method `
95
+ lines.push(...method `
215
96
  moveTo(index: number): this {
216
97
  return this.moveToIndex(index, ${layout.name}View.byteLength);
217
98
  }`);
218
- }
219
99
  lines.push("");
220
100
  lines.push(...method `
221
101
  moveToUnchecked(index: number): this {
222
102
  return this.rebaseUnchecked(index * ${layout.byteLength});
223
103
  }`);
224
104
  lines.push("");
225
- const writerLines = emitDynamicWriterMethods(layout, layoutMap);
226
- for (const line of writerLines) {
227
- lines.push(line);
228
- }
229
- if (writerLines.length > 0) {
230
- lines.push("");
231
- }
232
- const objectWriterLines = emitObjectWriterMethod(layout, layoutMap);
233
- for (const line of objectWriterLines) {
234
- lines.push(line);
235
- }
236
- if (objectWriterLines.length > 0) {
237
- lines.push("");
238
- }
105
+ lines.push(...emitLayerBlock(emitDynamicWriterMethods(layout, layoutMap)));
106
+ lines.push(...emitLayerBlock(emitObjectWriterMethod(layout, layoutMap)));
239
107
  for (const field of layout.fields) {
240
- const staticAccessorLines = emitStaticFieldAccessor(layout, field);
241
- for (const line of staticAccessorLines) {
242
- lines.push(line);
243
- }
244
- if (staticAccessorLines.length > 0) {
245
- lines.push("");
246
- }
247
- for (const line of emitField(layout, field, options)) {
248
- lines.push(line);
249
- }
250
- lines.push("");
108
+ lines.push(...emitLayerBlock(emitStaticFieldAccessor(layout, field, scanKernelMode)));
109
+ lines.push(...emitLayerBlock(emitField(layout, field)));
251
110
  }
252
111
  lines.push("}");
253
112
  return lines;
254
113
  }
255
- function emitDynamicWriterMethods(layout, layoutMap) {
256
- const fields = layout.fields.filter((field) => isTailWriterField(field, layoutMap));
257
- if (fields.length === 0) {
258
- return [];
259
- }
260
- const lines = [
261
- ...method `
262
- static createWriter(view: DataView, baseOffset = 0, tailOffset = ${layout.name}View.byteLength, littleEndian = ${toLittleEndianLiteral(layout)}): DynamicLayoutWriter {
263
- return new DynamicLayoutWriter(view, tailOffset, baseOffset, littleEndian);
264
- }`,
265
- "",
266
- ];
267
- for (const field of fields) {
268
- const pascalName = toPascalCase(field.name);
269
- switch (field.kind) {
270
- case "dynamic-string":
271
- lines.push(...method `
272
- static write${pascalName}(writer: DynamicLayoutWriter, value: string) {
273
- return writer.writeText(${layout.name}View.${field.name}Offset, value, ${encodingLiteral(field.encoding)});
274
- }`);
275
- break;
276
- case "dynamic-bytes":
277
- lines.push(...method `
278
- static write${pascalName}(writer: DynamicLayoutWriter, value: ArrayLike<number> | Uint8Array) {
279
- return writer.writeBytes(${layout.name}View.${field.name}Offset, value);
280
- }`);
281
- break;
282
- case "vector":
283
- lines.push(...emitVectorWriterMethod(layout, layoutMap, field, pascalName));
284
- break;
285
- }
286
- lines.push("");
287
- }
288
- lines.pop();
289
- return lines;
290
- }
291
- const vectorWriterEmitters = {
292
- "dynamic-string": ({ layout, field, pascalName }) => method `
293
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly string[]) {
294
- return writer.writeTextVector(${layout.name}View.${field.name}Offset, values, ${encodingLiteral(field.element.encoding)});
295
- }`,
296
- "dynamic-bytes": ({ layout, field, pascalName }) => method `
297
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly (ArrayLike<number> | Uint8Array)[]) {
298
- return writer.writeBytesVector(${layout.name}View.${field.name}Offset, values);
299
- }`,
300
- "fixed-bytes": ({ layout, field, pascalName }) => method `
301
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly (ArrayLike<number> | Uint8Array)[]) {
302
- return writer.writeFixedBytesVector(${layout.name}View.${field.name}Offset, values, ${field.element.byteLength});
303
- }`,
304
- "fixed-string": ({ layout, field, pascalName }) => method `
305
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly string[]) {
306
- return writer.writeFixedTextVector(${layout.name}View.${field.name}Offset, values, ${field.element.byteLength}, ${encodingLiteral(field.element.encoding)});
307
- }`,
308
- scalar: ({ layout, field, pascalName }) => method `
309
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${scalarTsType(field.element.scalar)}[]) {
310
- return writer.writeScalarVector(${layout.name}View.${field.name}Offset, "${field.element.scalar}", values);
311
- }`,
312
- struct: ({ layout, layoutMap, field, pascalName }) => {
313
- const elementLayout = layoutMap.get(field.element.typeName);
314
- const alignment = elementLayout?.alignment ?? 1;
315
- return method `
316
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${field.element.typeName}ViewInput[]) {
317
- return writer.writeStructVector(${layout.name}View.${field.name}Offset, values, ${field.element.byteLength}, (view, value, baseOffset, littleEndian) => ${field.element.typeName}View.write(view, value, baseOffset, littleEndian), ${alignment});
318
- }`;
319
- },
320
- pointer: ({ layout, field, pascalName }) => method `
321
- static write${pascalName}(writer: DynamicLayoutWriter, values: readonly (number | null)[]) {
322
- return writer.writePointerVector(${layout.name}View.${field.name}Offset, values, ${field.element.targetTypeName}View.byteLength);
323
- }`,
324
- };
325
- function emitVectorWriterMethod(layout, layoutMap, field, pascalName) {
326
- const emitWriter = vectorWriterEmitters[field.element.kind];
327
- return emitWriter({ layout, layoutMap, field, pascalName });
328
- }
329
- function emitObjectWriterMethod(layout, layoutMap) {
330
- if (!canEmitObjectWriter(layout, layoutMap)) {
331
- return [];
332
- }
333
- const hasTailFields = hasTailWriterFields(layout, layoutMap);
334
- const returnType = hasTailFields ? "DynamicLayoutWriter" : "void";
335
- const bodyLines = [];
336
- if (hasTailFields) {
337
- bodyLines.push(` const writer = ${layout.name}View.createWriter(view, baseOffset, ${layout.name}View.byteLength, littleEndian);`);
338
- }
339
- for (const field of layout.fields) {
340
- bodyLines.push(...emitObjectFieldWrite(layout, field));
341
- }
342
- if (hasTailFields) {
343
- bodyLines.push(" return writer;");
344
- }
345
- return method `
346
- static write(view: DataView, value: ${layout.name}ViewInput, baseOffset = 0, littleEndian = ${toLittleEndianLiteral(layout)}): ${returnType} {
347
- ${bodyLines}
348
- }`;
349
- }
350
- function emitObjectFieldWrite(layout, field) {
351
- const pascalName = toPascalCase(field.name);
352
- switch (field.kind) {
353
- case "scalar":
354
- return [
355
- ` ${layout.name}View.set${pascalName}(view, value.${field.name}, baseOffset, littleEndian);`,
356
- ];
357
- case "fixed-bytes":
358
- return [
359
- ` writeFixedBytes(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name});`,
360
- ];
361
- case "fixed-string":
362
- return [
363
- ` writeFixedText(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name}, ${encodingLiteral(field.encoding)});`,
364
- ];
365
- case "dynamic-string":
366
- case "dynamic-bytes":
367
- return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
368
- case "struct":
369
- return [
370
- ` ${field.typeName}View.write(view, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
371
- ];
372
- case "pointer":
373
- return [
374
- ` ${layout.name}View.set${pascalName}TargetOffset(view, value.${field.name}, baseOffset, littleEndian);`,
375
- ];
376
- case "fixed-array":
377
- return emitFixedArrayObjectFieldWrite(field, encodingLiteral);
378
- case "vector":
379
- return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
380
- }
381
- }
382
- function emitStaticFieldAccessor(layout, field) {
383
- if (field.kind === "pointer") {
384
- const pascalName = toPascalCase(field.name);
385
- const littleEndianDefault = toLittleEndianLiteral(layout);
386
- const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
387
- const pointerPosition = `baseOffset + ${field.offset}`;
388
- return method `
389
- static getRaw${pascalName}RelativeOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
390
- return view.getUint32(baseOffset + ${field.offset}, littleEndian);
391
- }
392
- static get${pascalName}RelativeOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number | null {
393
- const rawValue = ${layout.name}View.getRaw${pascalName}RelativeOffset(view, baseOffset, littleEndian);
394
- if (rawValue === 0xffffffff) {
395
- return null;
396
- }
397
- return view.getInt32(baseOffset + ${field.offset}, littleEndian);
398
- }
399
- static set${pascalName}RelativeOffset(view: DataView, value: number | null, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {
400
- if (value === null) {
401
- view.setUint32(baseOffset + ${field.offset}, 0xffffffff, littleEndian);
402
- return;
403
- }
404
- ${layout.name}View.assertPointer32Payload(value);
405
- view.setInt32(baseOffset + ${field.offset}, value, littleEndian);
406
- }
407
- static getUnchecked${pascalName}TargetOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number | null {
408
- const relativeOffset = ${layout.name}View.get${pascalName}RelativeOffset(view, baseOffset, littleEndian);
409
- if (relativeOffset === null) {
410
- return null;
411
- }
412
- return ${pointerPosition} + relativeOffset;
413
- }
414
- static get${pascalName}TargetOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number | null {
415
- const targetOffset = ${layout.name}View.getUnchecked${pascalName}TargetOffset(view, baseOffset, littleEndian);
416
- if (targetOffset === null) {
417
- return null;
418
- }
419
- ${layout.name}View.assertPointerTargetRange(view, targetOffset, ${field.targetTypeName}View.byteLength);
420
- return targetOffset;
421
- }
422
- static setUnchecked${pascalName}TargetOffset(view: DataView, targetOffset: number | null, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {
423
- if (targetOffset === null) {
424
- ${layout.name}View.set${pascalName}RelativeOffset(view, null, baseOffset, littleEndian);
425
- return;
426
- }
427
- const relativeOffset = targetOffset - (${pointerPosition});
428
- ${layout.name}View.set${pascalName}RelativeOffset(view, relativeOffset, baseOffset, littleEndian);
429
- }
430
- static set${pascalName}TargetOffset(view: DataView, targetOffset: number | null, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {
431
- if (targetOffset !== null) {
432
- ${layout.name}View.assertPointerTargetRange(view, targetOffset, ${field.targetTypeName}View.byteLength);
433
- }
434
- ${layout.name}View.setUnchecked${pascalName}TargetOffset(view, targetOffset, baseOffset, littleEndian);
435
- }
436
- static get${pascalName}RelativeOffsetAt(view: DataView, index: number, littleEndian = ${littleEndianDefault}): number | null {
437
- const rawValue = ${layout.name}View.getRaw${pascalName}RelativeOffset(view, index * ${layout.byteLength}, littleEndian);
438
- if (rawValue === 0xffffffff) {
439
- return null;
440
- }
441
- return view.getInt32(${indexOffset}, littleEndian);
442
- }
443
- static set${pascalName}RelativeOffsetAt(view: DataView, value: number | null, index: number, littleEndian = ${littleEndianDefault}): void {
444
- if (value === null) {
445
- view.setUint32(${indexOffset}, 0xffffffff, littleEndian);
446
- return;
447
- }
448
- ${layout.name}View.assertPointer32Payload(value);
449
- view.setInt32(${indexOffset}, value, littleEndian);
450
- }`;
451
- }
452
- if (field.kind !== "scalar") {
453
- return [];
454
- }
455
- const getterMethod = scalarGetterMethod(field.scalar);
456
- const setterMethod = scalarSetterMethod(field.scalar);
457
- const typeName = scalarTsType(field.scalar);
458
- const littleEndianDefault = toLittleEndianLiteral(layout);
459
- const endianArg = field.byteLength === 1 || field.scalar === "bool" ? "" : ", littleEndian";
460
- const getterBody = field.scalar === "bool"
461
- ? ` return view.${getterMethod}(baseOffset + ${field.offset}) !== 0;`
462
- : ` return view.${getterMethod}(baseOffset + ${field.offset}${endianArg});`;
463
- const setterBody = field.scalar === "bool"
464
- ? ` view.${setterMethod}(baseOffset + ${field.offset}, value ? 1 : 0);`
465
- : ` view.${setterMethod}(baseOffset + ${field.offset}, value${endianArg});`;
466
- const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
467
- const indexGetterBody = field.scalar === "bool"
468
- ? ` return view.${getterMethod}(${indexOffset}) !== 0;`
469
- : ` return view.${getterMethod}(${indexOffset}${endianArg});`;
470
- const indexSetterBody = field.scalar === "bool"
471
- ? ` view.${setterMethod}(${indexOffset}, value ? 1 : 0);`
472
- : ` view.${setterMethod}(${indexOffset}, value${endianArg});`;
473
- const pascalName = toPascalCase(field.name);
474
- return [
475
- ` static get${pascalName}(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${typeName} {`,
476
- getterBody,
477
- " }",
478
- ` static set${pascalName}(view: DataView, value: ${typeName}, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {`,
479
- setterBody,
480
- " }",
481
- ` static get${pascalName}At(view: DataView, index: number, littleEndian = ${littleEndianDefault}): ${typeName} {`,
482
- indexGetterBody,
483
- " }",
484
- ` static set${pascalName}At(view: DataView, value: ${typeName}, index: number, littleEndian = ${littleEndianDefault}): void {`,
485
- indexSetterBody,
486
- " }",
487
- ...emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName),
488
- ];
489
- }
490
- function emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName) {
491
- if (!isNumberSumScalar(field.scalar)) {
492
- return [];
493
- }
494
- const endianArg = field.byteLength === 1 ? "" : ", littleEndian";
495
- return method `
496
- static sum${pascalName}(view: DataView, count: number, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
497
- if (!Number.isInteger(count) || count < 0) {
498
- throw new RangeError(\`Invalid record count: \${count}\`);
499
- }
500
- if (!Number.isFinite(baseOffset) || !Number.isInteger(baseOffset) || baseOffset < 0) {
501
- throw new RangeError(\`Invalid base offset: \${baseOffset}\`);
502
- }
503
- if (count === 0) {
504
- return 0;
505
- }
506
- const start = baseOffset + ${field.offset};
507
- const limit = start + count * ${layout.byteLength};
508
- const lastByte = start + (count - 1) * ${layout.byteLength} + ${field.byteLength};
509
- if (lastByte > view.byteLength) {
510
- throw new RangeError(\`scan range exceeds DataView length \${view.byteLength}\`);
511
- }
512
- let sum = 0;
513
- for (let offset = start; offset < limit; offset += ${layout.byteLength}) {
514
- sum += view.${getterMethod}(offset${endianArg});
515
- }
516
- return sum;
517
- }`;
518
- }
519
- function isNumberSumScalar(kind) {
520
- return kind !== "i64" && kind !== "u64" && kind !== "bool";
521
- }
522
- function emitField(layout, field, options) {
523
- switch (field.kind) {
524
- case "scalar": {
525
- const getterMethod = scalarGetterMethod(field.scalar);
526
- const setterMethod = scalarSetterMethod(field.scalar);
527
- const typeName = scalarTsType(field.scalar);
528
- const getterArgs = field.byteLength === 1 || field.scalar === "bool" ? "" : ", this.littleEndian";
529
- const instanceOffset = options.optimizeCursorOffsets
530
- ? `this.$${field.name}Offset`
531
- : `this.baseOffset + ${field.offset}`;
532
- const getterBody = field.scalar === "bool"
533
- ? `return this.view.${getterMethod}(${instanceOffset}) !== 0;`
534
- : `return this.view.${getterMethod}(${instanceOffset}${getterArgs});`;
535
- const setterBody = field.scalar === "bool"
536
- ? `this.view.${setterMethod}(${instanceOffset}, value ? 1 : 0);`
537
- : `this.view.${setterMethod}(${instanceOffset}, value${getterArgs});`;
538
- return method `
539
- get ${field.name}(): ${typeName} {
540
- ${getterBody}
541
- }
542
- set ${field.name}(value: ${typeName}) {
543
- ${setterBody}
544
- }`;
545
- }
546
- case "fixed-bytes":
547
- return method `
548
- ${field.name}Bytes(): Uint8Array {
549
- return fixedBytesView(this.backingBuffer(), this.backingOffset(${field.offset}), ${field.byteLength});
550
- }`;
551
- case "fixed-string":
552
- return method `
553
- ${field.name}Text(): string {
554
- return decodeFixedText(this.backingBuffer(), this.backingOffset(${field.offset}), ${field.byteLength}, ${encodingLiteral(field.encoding)});
555
- }
556
- ${field.name}Bytes(): Uint8Array {
557
- return fixedBytesView(this.backingBuffer(), this.backingOffset(${field.offset}), ${field.byteLength});
558
- }`;
559
- case "dynamic-string":
560
- return method `
561
- ${field.name}View(): Utf8SpanView {
562
- return new Utf8SpanView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.encoding)});
563
- }`;
564
- case "dynamic-bytes":
565
- return method `
566
- ${field.name}View(): BytesSpanView {
567
- return new BytesSpanView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
568
- }
569
- ${field.name}Bytes(): Uint8Array {
570
- return this.${field.name}View().bytes();
571
- }`;
572
- case "struct":
573
- return method `
574
- ${field.name}View(): ${field.typeName}View {
575
- return new ${field.typeName}View(this.view, this.absoluteOffset(${field.offset}), this.littleEndian);
576
- }`;
577
- case "pointer": {
578
- const pascalName = toPascalCase(field.name);
579
- return method `
580
- get raw${pascalName}RelativeOffset(): number {
581
- return this.view.getUint32(this.baseOffset + ${field.offset}, this.littleEndian);
582
- }
583
- get ${field.name}RelativeOffset(): number | null {
584
- const rawValue = this.raw${pascalName}RelativeOffset;
585
- if (rawValue === 0xffffffff) {
586
- return null;
587
- }
588
- return this.view.getInt32(this.baseOffset + ${field.offset}, this.littleEndian);
589
- }
590
- set ${field.name}RelativeOffset(value: number | null) {
591
- if (value === null) {
592
- this.view.setUint32(this.baseOffset + ${field.offset}, 0xffffffff, this.littleEndian);
593
- return;
594
- }
595
- if (!Number.isInteger(value) || value < -0x80000000 || value > 0x7fffffff || value === -1) {
596
- throw new RangeError(\`pointer32 target offset must encode to signed i32 except -1: \${value}\`);
597
- }
598
- this.view.setInt32(this.baseOffset + ${field.offset}, value, this.littleEndian);
599
- }
600
- get unchecked${pascalName}TargetOffset(): number | null {
601
- const relativeOffset = this.${field.name}RelativeOffset;
602
- if (relativeOffset === null) {
603
- return null;
604
- }
605
- return this.baseOffset + ${field.offset} + relativeOffset;
606
- }
607
- get ${field.name}TargetOffset(): number | null {
608
- const targetOffset = this.unchecked${pascalName}TargetOffset;
609
- if (targetOffset === null) {
610
- return null;
611
- }
612
- ${layout.name}View.assertPointerTargetRange(this.view, targetOffset, ${field.targetTypeName}View.byteLength);
613
- return targetOffset;
614
- }
615
- set unchecked${pascalName}TargetOffset(targetOffset: number | null) {
616
- if (targetOffset === null) {
617
- this.${field.name}RelativeOffset = null;
618
- return;
619
- }
620
- const relativeOffset = targetOffset - (this.baseOffset + ${field.offset});
621
- this.${field.name}RelativeOffset = relativeOffset;
622
- }
623
- set ${field.name}TargetOffset(targetOffset: number | null) {
624
- if (targetOffset !== null) {
625
- ${layout.name}View.assertPointerTargetRange(this.view, targetOffset, ${field.targetTypeName}View.byteLength);
626
- }
627
- this.unchecked${pascalName}TargetOffset = targetOffset;
628
- }
629
- ${field.name}View(): ${field.targetTypeName}View | null {
630
- const targetOffset = this.${field.name}TargetOffset;
631
- if (targetOffset === null) {
632
- return null;
633
- }
634
- const target = new ${field.targetTypeName}View(this.view, 0, this.littleEndian);
635
- target.moveToOffset(targetOffset, ${field.targetTypeName}View.byteLength);
636
- return target;
637
- }
638
- ${field.name}Into(out: ${field.targetTypeName}View): boolean {
639
- const targetOffset = this.${field.name}TargetOffset;
640
- if (targetOffset === null) {
641
- return false;
642
- }
643
- out.moveToOffset(targetOffset, ${field.targetTypeName}View.byteLength);
644
- return true;
645
- }`;
646
- }
647
- case "fixed-array":
648
- return emitFixedArrayFieldAccessor(field, encodingLiteral);
649
- case "vector":
650
- switch (field.element.kind) {
651
- case "scalar":
652
- return method `
653
- ${field.name}View(): ScalarVectorView<${scalarTsType(field.element.scalar)}> {
654
- return new ScalarVectorView(this.view, ${field.offset}, "${field.element.scalar}", this.baseOffset, this.littleEndian);
655
- }`;
656
- case "dynamic-string":
657
- return method `
658
- ${field.name}View(): Utf8VectorView {
659
- return new Utf8VectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
660
- }`;
661
- case "dynamic-bytes":
662
- return method `
663
- ${field.name}View(): BytesVectorView {
664
- return new BytesVectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
665
- }`;
666
- case "fixed-bytes":
667
- return method `
668
- ${field.name}View(): FixedBytesVectorView {
669
- return new FixedBytesVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian);
670
- }`;
671
- case "fixed-string":
672
- return method `
673
- ${field.name}View(): FixedStringVectorView {
674
- return new FixedStringVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
675
- }`;
676
- case "struct":
677
- return method `
678
- ${field.name}View(): StructVectorView<${field.element.typeName}View> {
679
- return new StructVectorView(this.view, ${field.offset}, ${field.element.byteLength}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
680
- }`;
681
- case "pointer":
682
- return method `
683
- ${field.name}View(): PointerVectorView<${field.element.targetTypeName}View> {
684
- return new PointerVectorView(this.view, ${field.offset}, ${field.element.targetTypeName}View.byteLength, (view, baseOffset, littleEndian) => new ${field.element.targetTypeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
685
- }`;
686
- }
687
- }
688
- }
689
- function toPascalCase(name) {
690
- return name.slice(0, 1).toUpperCase() + name.slice(1);
691
- }
692
- function toLittleEndianLiteral(layout) {
693
- return layout.endianness === "little" ? "true" : "false";
694
- }
695
- function encodingLiteral(encoding) {
696
- return encoding === "ascii" ? "\"ascii\"" : "\"utf8\"";
697
- }
698
- function hasTailWriterFields(layout, layoutMap) {
699
- return layout.fields.some((field) => isTailWriterField(field, layoutMap));
700
- }
701
- function isTailWriterField(field, layoutMap) {
702
- switch (field.kind) {
703
- case "dynamic-string":
704
- case "dynamic-bytes":
705
- return true;
706
- case "vector":
707
- return (field.element.kind === "scalar" ||
708
- field.element.kind === "fixed-bytes" ||
709
- field.element.kind === "fixed-string" ||
710
- field.element.kind === "dynamic-string" ||
711
- field.element.kind === "dynamic-bytes" ||
712
- field.element.kind === "pointer" ||
713
- canWriteStructVectorElement(field.element, layoutMap));
714
- default:
715
- return false;
716
- }
717
- }
718
- function canEmitObjectWriter(layout, layoutMap) {
719
- return layout.fields.every((field) => canWriteObjectField(field, layoutMap));
720
- }
721
- function canWriteObjectField(field, layoutMap) {
722
- switch (field.kind) {
723
- case "scalar":
724
- case "fixed-bytes":
725
- case "fixed-string":
726
- case "dynamic-string":
727
- case "dynamic-bytes":
728
- case "struct":
729
- case "pointer":
730
- return true;
731
- case "fixed-array":
732
- return (field.element.kind !== "struct" ||
733
- canWriteFixedArrayStructElement(field.element, layoutMap, hasTailWriterFields));
734
- case "vector":
735
- return (field.element.kind !== "struct" ||
736
- canWriteStructVectorElement(field.element, layoutMap));
737
- }
738
- }
739
- function canWriteStructVectorElement(element, layoutMap) {
740
- if (element.kind !== "struct") {
741
- return false;
742
- }
743
- const elementLayout = layoutMap.get(element.typeName);
744
- return elementLayout !== undefined && !hasTailWriterFields(elementLayout, layoutMap);
114
+ function emitLayerBlock(lines) {
115
+ return lines.length === 0 ? [] : [...lines, ""];
745
116
  }
746
117
  //# sourceMappingURL=emitter.js.map