ya-struct 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +8 -0
- package/.github/workflows/ci.yml +23 -0
- package/.github/workflows/npm-publish.yml +28 -0
- package/README.md +150 -18
- package/dist/lib/common.d.ts +14 -0
- package/dist/lib/common.js +30 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +21 -0
- package/dist/lib/layout.d.ts +48 -0
- package/dist/lib/layout.js +278 -0
- package/dist/lib/parser.d.ts +51 -0
- package/dist/lib/parser.js +87 -0
- package/dist/lib/types/array.d.ts +10 -0
- package/dist/lib/types/array.js +90 -0
- package/dist/lib/types/c-types.d.ts +13 -0
- package/dist/lib/types/c-types.js +222 -0
- package/dist/lib/types/index.d.ts +93 -0
- package/dist/lib/types/index.js +122 -0
- package/dist/lib/types/integer.d.ts +9 -0
- package/dist/lib/types/integer.js +160 -0
- package/dist/lib/types/pointer.d.ts +8 -0
- package/dist/lib/types/pointer.js +27 -0
- package/dist/lib/types/string.d.ts +6 -0
- package/dist/lib/types/string.js +56 -0
- package/dist/lib/types/struct.d.ts +11 -0
- package/dist/lib/types/struct.js +113 -0
- package/dist/lib/types/value.d.ts +12 -0
- package/dist/lib/types/value.js +8 -0
- package/eslint.config.js +127 -0
- package/lib/bit-buffer.ts +70 -0
- package/lib/common.ts +30 -0
- package/lib/index.ts +21 -0
- package/lib/layout.ts +278 -0
- package/lib/parser.ts +87 -0
- package/lib/types/array.ts +90 -0
- package/lib/types/c-types.ts +222 -0
- package/lib/types/index.ts +122 -0
- package/lib/types/integer.ts +160 -0
- package/lib/types/pointer.ts +27 -0
- package/lib/types/string.ts +56 -0
- package/lib/types/struct.ts +113 -0
- package/lib/types/value.ts +8 -0
- package/package.json +19 -15
- package/package.npm.json +25 -0
- package/samples/basic.ts +40 -0
- package/test/c-structs.ts +399 -0
- package/test/compile-util.ts +92 -0
- package/tsconfig.json +10 -0
- package/.eslintrc +0 -92
- package/.github/workflows/CI.yml +0 -39
- package/.prettierrc +0 -5
- package/lib/builder.js +0 -62
- package/lib/index.js +0 -159
- package/lib/marshaller.js +0 -88
- package/lib/refbuf.js +0 -11
- package/lib/types/basic.js +0 -200
- package/lib/types/ctypes.js +0 -160
- package/test/abi.js +0 -203
- package/test/basic.js +0 -92
- package/test/ctypes.js +0 -166
- package/test/ref.js +0 -35
package/lib/layout.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { align, type TAbi } from "./common.ts";
|
|
2
|
+
import { createCTypeNormalizer } from "./types/c-types.ts";
|
|
3
|
+
import type { TFieldType } from "./types/index.ts";
|
|
4
|
+
import nodeUtil from "node:util";
|
|
5
|
+
|
|
6
|
+
type TLayoutedField = {
|
|
7
|
+
readonly type: "integer";
|
|
8
|
+
readonly offsetInBits: number;
|
|
9
|
+
readonly sizeInBits: number;
|
|
10
|
+
readonly signed: boolean;
|
|
11
|
+
readonly fixedAbi: Partial<TAbi>;
|
|
12
|
+
} | {
|
|
13
|
+
readonly type: "float";
|
|
14
|
+
readonly offsetInBits: number;
|
|
15
|
+
readonly sizeInBits: number;
|
|
16
|
+
readonly fixedAbi: Partial<TAbi>;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "pointer";
|
|
19
|
+
readonly offsetInBits: number;
|
|
20
|
+
readonly sizeInBits: number;
|
|
21
|
+
readonly fixedAbi: Partial<TAbi>;
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: "array";
|
|
24
|
+
readonly elementType: TLayoutedField;
|
|
25
|
+
readonly length: number;
|
|
26
|
+
readonly offsetInBits: number;
|
|
27
|
+
readonly sizeInBits: number;
|
|
28
|
+
} | {
|
|
29
|
+
readonly type: "struct";
|
|
30
|
+
readonly offsetInBits: number;
|
|
31
|
+
readonly sizeInBits: number;
|
|
32
|
+
readonly fields: readonly { readonly name: string; readonly definition: TLayoutedField }[];
|
|
33
|
+
readonly packed: boolean;
|
|
34
|
+
readonly fixedAbi: Partial<TAbi>;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
readonly charSizeInBits: number;
|
|
38
|
+
readonly length: number;
|
|
39
|
+
readonly offsetInBits: number;
|
|
40
|
+
readonly sizeInBits: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const pointerSizeInBitsByDataModel = ({ dataModel }: { dataModel: TAbi["dataModel"] }): number => {
|
|
44
|
+
switch (dataModel) {
|
|
45
|
+
case "LP64":
|
|
46
|
+
return 64;
|
|
47
|
+
case "ILP32":
|
|
48
|
+
return 32;
|
|
49
|
+
default:
|
|
50
|
+
throw Error(`unsupported data model "${dataModel}" for pointer size determination`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const layoutStruct = ({
|
|
55
|
+
definition,
|
|
56
|
+
abi,
|
|
57
|
+
currentOffsetInBits: initialOffsetInBits
|
|
58
|
+
}: {
|
|
59
|
+
definition: TFieldType & { type: "struct" },
|
|
60
|
+
abi: TAbi,
|
|
61
|
+
currentOffsetInBits: number
|
|
62
|
+
}): TLayoutedField => {
|
|
63
|
+
|
|
64
|
+
// TODO: implement alignment handling
|
|
65
|
+
const structAlignmentInBits = 64;
|
|
66
|
+
// const fieldAlignmentInBits = 64;
|
|
67
|
+
const fieldAlignmentInBits = 1;
|
|
68
|
+
const pointerSizeInBits = pointerSizeInBitsByDataModel({ dataModel: abi.dataModel });
|
|
69
|
+
|
|
70
|
+
let currentOffsetInBits = align({ offset: initialOffsetInBits, alignment: structAlignmentInBits });
|
|
71
|
+
let layoutedFields: (TLayoutedField & { type: "struct" })["fields"] = [];
|
|
72
|
+
|
|
73
|
+
// eslint-disable-next-line max-statements,complexity
|
|
74
|
+
definition.fields.forEach((field) => {
|
|
75
|
+
|
|
76
|
+
let normalizedField: TFieldType = field.definition;
|
|
77
|
+
|
|
78
|
+
if (field.definition.type === "c-type") {
|
|
79
|
+
const cTypeNormalizer = createCTypeNormalizer({ abi });
|
|
80
|
+
normalizedField = cTypeNormalizer.normalize({ cField: field.definition });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!definition.packed) {
|
|
84
|
+
switch (normalizedField.type) {
|
|
85
|
+
case "integer":
|
|
86
|
+
case "float": {
|
|
87
|
+
const sizeInBits = normalizedField.sizeInBits;
|
|
88
|
+
|
|
89
|
+
if (abi.compiler === "gcc" && abi.dataModel === "ILP32" && sizeInBits === 64) {
|
|
90
|
+
// special handling for gcc 64-bit integers on ILP32 data model (alignment to 32 bits)
|
|
91
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: 32 });
|
|
92
|
+
} else {
|
|
93
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: sizeInBits });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case "array": {
|
|
99
|
+
|
|
100
|
+
// TODO: this is probably not sufficient for all cases
|
|
101
|
+
|
|
102
|
+
// eslint-disable-next-line no-use-before-define
|
|
103
|
+
const { sizeInBits } = layout({ definition: normalizedField.elementType, abi, currentOffsetInBits: 0 });
|
|
104
|
+
|
|
105
|
+
if (abi.compiler === "gcc" && abi.dataModel === "ILP32" && sizeInBits === 64) {
|
|
106
|
+
// special handling for gcc 64-bit integers on ILP32 data model (alignment to 32 bits)
|
|
107
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: 32 });
|
|
108
|
+
} else {
|
|
109
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: sizeInBits });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case "string": {
|
|
115
|
+
// no special alignment needed
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "pointer": {
|
|
119
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: pointerSizeInBits });
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
default:
|
|
123
|
+
throw Error(`unsupported field type for struct layout: ${nodeUtil.inspect(field.definition)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// eslint-disable-next-line no-use-before-define
|
|
128
|
+
const fieldLayout = layout({ definition: normalizedField, abi, currentOffsetInBits });
|
|
129
|
+
|
|
130
|
+
layoutedFields = [
|
|
131
|
+
...layoutedFields,
|
|
132
|
+
{
|
|
133
|
+
name: field.name,
|
|
134
|
+
definition: fieldLayout
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
currentOffsetInBits = align({ offset: fieldLayout.offsetInBits + fieldLayout.sizeInBits, alignment: fieldAlignmentInBits });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
type: "struct",
|
|
143
|
+
offsetInBits: initialOffsetInBits,
|
|
144
|
+
sizeInBits: currentOffsetInBits - initialOffsetInBits,
|
|
145
|
+
fields: layoutedFields,
|
|
146
|
+
packed: definition.packed,
|
|
147
|
+
fixedAbi: definition.fixedAbi
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const layoutPrimitive = ({
|
|
152
|
+
definition,
|
|
153
|
+
abi,
|
|
154
|
+
currentOffsetInBits
|
|
155
|
+
}: {
|
|
156
|
+
definition: TFieldType & ({ type: "integer" } | { type: "float" } | { type: "pointer" }),
|
|
157
|
+
abi: TAbi,
|
|
158
|
+
currentOffsetInBits: number
|
|
159
|
+
}): TLayoutedField => {
|
|
160
|
+
|
|
161
|
+
if (definition.type === "integer") {
|
|
162
|
+
return {
|
|
163
|
+
type: "integer",
|
|
164
|
+
offsetInBits: currentOffsetInBits,
|
|
165
|
+
sizeInBits: definition.sizeInBits,
|
|
166
|
+
signed: definition.signed,
|
|
167
|
+
fixedAbi: definition.fixedAbi
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (definition.type === "float") {
|
|
172
|
+
return {
|
|
173
|
+
type: "float",
|
|
174
|
+
offsetInBits: currentOffsetInBits,
|
|
175
|
+
sizeInBits: definition.sizeInBits,
|
|
176
|
+
fixedAbi: definition.fixedAbi
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const pointerSizeInBits = pointerSizeInBitsByDataModel({ dataModel: abi.dataModel });
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
type: definition.type,
|
|
184
|
+
offsetInBits: currentOffsetInBits,
|
|
185
|
+
sizeInBits: pointerSizeInBits,
|
|
186
|
+
fixedAbi: definition.fixedAbi
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const layoutString = ({
|
|
191
|
+
definition,
|
|
192
|
+
currentOffsetInBits
|
|
193
|
+
}: {
|
|
194
|
+
definition: TFieldType & { type: "string" },
|
|
195
|
+
abi: TAbi,
|
|
196
|
+
currentOffsetInBits: number
|
|
197
|
+
}): TLayoutedField => {
|
|
198
|
+
|
|
199
|
+
const sizeInBits = definition.charSizeInBits * definition.length;
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
type: "string",
|
|
203
|
+
offsetInBits: currentOffsetInBits,
|
|
204
|
+
sizeInBits,
|
|
205
|
+
charSizeInBits: definition.charSizeInBits,
|
|
206
|
+
length: definition.length
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const layoutArray = ({
|
|
211
|
+
definition,
|
|
212
|
+
abi,
|
|
213
|
+
currentOffsetInBits
|
|
214
|
+
}: {
|
|
215
|
+
definition: TFieldType & { type: "array" },
|
|
216
|
+
abi: TAbi,
|
|
217
|
+
currentOffsetInBits: number
|
|
218
|
+
}): TLayoutedField => {
|
|
219
|
+
|
|
220
|
+
// eslint-disable-next-line no-use-before-define
|
|
221
|
+
const elementLayout = layout({ definition: definition.elementType, abi, currentOffsetInBits: 0 });
|
|
222
|
+
|
|
223
|
+
// TODO: handle alignment
|
|
224
|
+
const sizeInBits = elementLayout.sizeInBits * definition.length;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
type: "array",
|
|
228
|
+
offsetInBits: currentOffsetInBits,
|
|
229
|
+
sizeInBits,
|
|
230
|
+
elementType: elementLayout,
|
|
231
|
+
length: definition.length
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const layout = ({
|
|
236
|
+
definition,
|
|
237
|
+
abi,
|
|
238
|
+
currentOffsetInBits
|
|
239
|
+
}: {
|
|
240
|
+
definition: TFieldType,
|
|
241
|
+
abi: TAbi,
|
|
242
|
+
currentOffsetInBits: number
|
|
243
|
+
// eslint-disable-next-line complexity
|
|
244
|
+
}): TLayoutedField => {
|
|
245
|
+
|
|
246
|
+
if (definition.type === "struct") {
|
|
247
|
+
return layoutStruct({ definition, abi, currentOffsetInBits });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (definition.type === "integer" || definition.type === "float" || definition.type === "pointer") {
|
|
251
|
+
return layoutPrimitive({ definition, abi, currentOffsetInBits });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (definition.type === "string") {
|
|
255
|
+
return layoutString({ definition, abi, currentOffsetInBits });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (definition.type === "array") {
|
|
259
|
+
return layoutArray({ definition, abi, currentOffsetInBits });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (definition.type === "c-type") {
|
|
263
|
+
const cTypeNormalizer = createCTypeNormalizer({ abi });
|
|
264
|
+
const basicField = cTypeNormalizer.normalize({ cField: definition });
|
|
265
|
+
|
|
266
|
+
return layout({ definition: basicField, abi, currentOffsetInBits });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
throw Error("not implemented yet");
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export {
|
|
273
|
+
layout
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export type {
|
|
277
|
+
TLayoutedField
|
|
278
|
+
};
|
package/lib/parser.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { TAbi } from "./common.ts";
|
|
2
|
+
import { layout, type TLayoutedField } from "./layout.ts";
|
|
3
|
+
import type { TFieldType } from "./types/index.ts";
|
|
4
|
+
import { createStructParser } from "./types/struct.ts";
|
|
5
|
+
import type { TValueParser } from "./types/value.ts";
|
|
6
|
+
|
|
7
|
+
type FieldValue<T extends TFieldType> =
|
|
8
|
+
T extends { type: "integer" } ? bigint :
|
|
9
|
+
T extends { type: "float" } ? number :
|
|
10
|
+
T extends { type: "pointer" } ? bigint :
|
|
11
|
+
T extends { type: "string" } ? string :
|
|
12
|
+
T extends { type: "array"; elementType: infer E; length: number }
|
|
13
|
+
? FieldValue<E & TFieldType>[] :
|
|
14
|
+
T extends { type: "struct"; fields: infer F }
|
|
15
|
+
? StructValue<F & readonly { name: string; definition: TFieldType }[]> :
|
|
16
|
+
never;
|
|
17
|
+
|
|
18
|
+
type StructValue<
|
|
19
|
+
F extends readonly { name: string; definition: TFieldType }[]
|
|
20
|
+
> = {
|
|
21
|
+
[K in F[number]as K["name"]]: FieldValue<K["definition"]>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type Simplify<T> = {
|
|
25
|
+
[K in keyof T]: T[K]
|
|
26
|
+
} & {};
|
|
27
|
+
|
|
28
|
+
type TParsedValueOfDefinition<T extends TFieldType> = Simplify<FieldValue<T>>;
|
|
29
|
+
|
|
30
|
+
type TParser<T extends TFieldType> = {
|
|
31
|
+
size: number;
|
|
32
|
+
parse: ({ data }: { data: Uint8Array }) => TParsedValueOfDefinition<T>;
|
|
33
|
+
format: ({ value }: { value: TParsedValueOfDefinition<T> }) => Uint8Array;
|
|
34
|
+
layout: TLayoutedField;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const define = <const T extends TFieldType>({ definition }: { definition: T }) => {
|
|
38
|
+
|
|
39
|
+
const parser = ({ abi }: { abi: TAbi }): TParser<T> => {
|
|
40
|
+
|
|
41
|
+
type P = TParser<T>;
|
|
42
|
+
|
|
43
|
+
const l = layout({ definition, abi, currentOffsetInBits: 0 });
|
|
44
|
+
|
|
45
|
+
const size = Math.ceil(l.sizeInBits / 8);
|
|
46
|
+
|
|
47
|
+
// console.log(l);
|
|
48
|
+
|
|
49
|
+
let valueParser: TValueParser<TParsedValueOfDefinition<T>>;
|
|
50
|
+
|
|
51
|
+
if (l.type === "struct") {
|
|
52
|
+
valueParser = createStructParser({
|
|
53
|
+
layoutedFields: l.fields,
|
|
54
|
+
endianness: abi.endianness
|
|
55
|
+
}) as unknown as TValueParser<TParsedValueOfDefinition<T>>;
|
|
56
|
+
} else {
|
|
57
|
+
throw Error("only struct root definitions are supported currently");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parse: P["parse"] = ({ data }) => {
|
|
61
|
+
return valueParser.parse({ data, offsetInBits: 0 });
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const format: P["format"] = ({ value }) => {
|
|
65
|
+
const data = new Uint8Array(l.sizeInBits / 8);
|
|
66
|
+
valueParser.format({ value, target: data, offsetInBits: 0 });
|
|
67
|
+
|
|
68
|
+
return data;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
size,
|
|
73
|
+
parse,
|
|
74
|
+
format,
|
|
75
|
+
layout: l
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
definition,
|
|
81
|
+
parser
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
define
|
|
87
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { TEndianness } from "../common.ts";
|
|
2
|
+
import type { TFieldType } from "./index.ts";
|
|
3
|
+
import { createIntegerParser } from "./integer.ts";
|
|
4
|
+
import type { TValueParser } from "./value.ts";
|
|
5
|
+
|
|
6
|
+
type TArrayParser = TValueParser<unknown[]>;
|
|
7
|
+
|
|
8
|
+
const createArrayParser = ({
|
|
9
|
+
elementType,
|
|
10
|
+
length,
|
|
11
|
+
endianness
|
|
12
|
+
}: {
|
|
13
|
+
elementType: TFieldType,
|
|
14
|
+
length: number,
|
|
15
|
+
endianness: TEndianness
|
|
16
|
+
}): TArrayParser => {
|
|
17
|
+
|
|
18
|
+
let fieldParser: TValueParser<unknown>;
|
|
19
|
+
|
|
20
|
+
if (elementType.type === "integer") {
|
|
21
|
+
fieldParser = createIntegerParser({
|
|
22
|
+
sizeInBits: elementType.sizeInBits,
|
|
23
|
+
signed: elementType.signed,
|
|
24
|
+
endianness
|
|
25
|
+
}) as TValueParser<unknown>;
|
|
26
|
+
} else {
|
|
27
|
+
throw Error("only integer array elements are supported currently");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const elementSizeInBytes = elementType.sizeInBits / 8;
|
|
31
|
+
if (!Number.isInteger(elementSizeInBytes)) {
|
|
32
|
+
throw Error("BUG: non byte-aligned array element size");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parse: TArrayParser["parse"] = ({ data, offsetInBits }) => {
|
|
36
|
+
if (offsetInBits !== 0) {
|
|
37
|
+
throw Error("unaligned array parsing not supported yet");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line prefer-const
|
|
41
|
+
let result: unknown[] = [];
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < length; i += 1) {
|
|
44
|
+
const fieldData = data.subarray(i * elementSizeInBytes, (i + 1) * elementSizeInBytes);
|
|
45
|
+
const fieldValue = fieldParser.parse({ data: fieldData, offsetInBits: 0 });
|
|
46
|
+
result.push(fieldValue);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line complexity
|
|
53
|
+
const format: TArrayParser["format"] = ({ value, target, offsetInBits }) => {
|
|
54
|
+
if (offsetInBits !== 0) {
|
|
55
|
+
throw Error("unaligned array formatting not supported yet");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (value.length !== length) {
|
|
59
|
+
throw Error(`array length mismatch: field length is ${length}, value length is ${value.length}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (target.length < length * elementSizeInBytes) {
|
|
63
|
+
throw Error("not enough space in target for array formatting");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < length; i += 1) {
|
|
67
|
+
const fieldTarget = target.subarray(i * elementSizeInBytes, (i + 1) * elementSizeInBytes);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
fieldParser.format({
|
|
71
|
+
value: value[i],
|
|
72
|
+
target: fieldTarget,
|
|
73
|
+
offsetInBits: 0
|
|
74
|
+
});
|
|
75
|
+
} catch (ex) {
|
|
76
|
+
throw Error(`failed to format array element at index ${i}`, { cause: ex });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
parse,
|
|
84
|
+
format
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
createArrayParser
|
|
90
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { type TCompiler, type TDataModel, type TAbi } from "../common.ts";
|
|
2
|
+
import { type TFieldType, type TCFieldType, type TPrimitiveBasicFieldType } from "./index.ts";
|
|
3
|
+
|
|
4
|
+
type TCField = TFieldType & { type: "c-type" };
|
|
5
|
+
|
|
6
|
+
type TPartialCTypeMappings = {
|
|
7
|
+
[dataModel in TDataModel]?: {
|
|
8
|
+
[compiler in TCompiler]?: {
|
|
9
|
+
[field in TCFieldType]?: TPrimitiveBasicFieldType
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const cTypeMappings: TPartialCTypeMappings = {
|
|
15
|
+
LP64: {
|
|
16
|
+
gcc: {
|
|
17
|
+
"char": {
|
|
18
|
+
type: "integer",
|
|
19
|
+
sizeInBits: 8,
|
|
20
|
+
fixedAbi: {},
|
|
21
|
+
signed: true
|
|
22
|
+
},
|
|
23
|
+
"unsigned char": {
|
|
24
|
+
type: "integer",
|
|
25
|
+
sizeInBits: 8,
|
|
26
|
+
fixedAbi: {},
|
|
27
|
+
signed: false
|
|
28
|
+
},
|
|
29
|
+
"short": {
|
|
30
|
+
type: "integer",
|
|
31
|
+
sizeInBits: 16,
|
|
32
|
+
fixedAbi: {},
|
|
33
|
+
signed: true
|
|
34
|
+
},
|
|
35
|
+
"unsigned short": {
|
|
36
|
+
type: "integer",
|
|
37
|
+
sizeInBits: 16,
|
|
38
|
+
fixedAbi: {},
|
|
39
|
+
signed: false
|
|
40
|
+
},
|
|
41
|
+
"int": {
|
|
42
|
+
type: "integer",
|
|
43
|
+
sizeInBits: 32,
|
|
44
|
+
fixedAbi: {},
|
|
45
|
+
signed: true
|
|
46
|
+
},
|
|
47
|
+
"unsigned int": {
|
|
48
|
+
type: "integer",
|
|
49
|
+
sizeInBits: 32,
|
|
50
|
+
fixedAbi: {},
|
|
51
|
+
signed: false
|
|
52
|
+
},
|
|
53
|
+
"long": {
|
|
54
|
+
type: "integer",
|
|
55
|
+
sizeInBits: 64,
|
|
56
|
+
fixedAbi: {},
|
|
57
|
+
signed: true
|
|
58
|
+
},
|
|
59
|
+
"unsigned long": {
|
|
60
|
+
type: "integer",
|
|
61
|
+
sizeInBits: 64,
|
|
62
|
+
fixedAbi: {},
|
|
63
|
+
signed: false
|
|
64
|
+
},
|
|
65
|
+
"long long": {
|
|
66
|
+
type: "integer",
|
|
67
|
+
sizeInBits: 64,
|
|
68
|
+
fixedAbi: {},
|
|
69
|
+
signed: true
|
|
70
|
+
},
|
|
71
|
+
"unsigned long long": {
|
|
72
|
+
type: "integer",
|
|
73
|
+
sizeInBits: 64,
|
|
74
|
+
fixedAbi: {},
|
|
75
|
+
signed: false
|
|
76
|
+
},
|
|
77
|
+
"float": {
|
|
78
|
+
type: "float",
|
|
79
|
+
sizeInBits: 32,
|
|
80
|
+
fixedAbi: {}
|
|
81
|
+
},
|
|
82
|
+
"double": {
|
|
83
|
+
type: "float",
|
|
84
|
+
sizeInBits: 64,
|
|
85
|
+
fixedAbi: {}
|
|
86
|
+
},
|
|
87
|
+
"long double": {
|
|
88
|
+
type: "float",
|
|
89
|
+
sizeInBits: 128,
|
|
90
|
+
fixedAbi: {}
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
ILP32: {
|
|
96
|
+
gcc: {
|
|
97
|
+
"char": {
|
|
98
|
+
type: "integer",
|
|
99
|
+
sizeInBits: 8,
|
|
100
|
+
fixedAbi: {},
|
|
101
|
+
signed: true
|
|
102
|
+
},
|
|
103
|
+
"unsigned char": {
|
|
104
|
+
type: "integer",
|
|
105
|
+
sizeInBits: 8,
|
|
106
|
+
fixedAbi: {},
|
|
107
|
+
signed: false
|
|
108
|
+
},
|
|
109
|
+
"short": {
|
|
110
|
+
type: "integer",
|
|
111
|
+
sizeInBits: 16,
|
|
112
|
+
fixedAbi: {},
|
|
113
|
+
signed: true
|
|
114
|
+
},
|
|
115
|
+
"unsigned short": {
|
|
116
|
+
type: "integer",
|
|
117
|
+
sizeInBits: 16,
|
|
118
|
+
fixedAbi: {},
|
|
119
|
+
signed: false
|
|
120
|
+
},
|
|
121
|
+
"int": {
|
|
122
|
+
type: "integer",
|
|
123
|
+
sizeInBits: 32,
|
|
124
|
+
fixedAbi: {},
|
|
125
|
+
signed: true
|
|
126
|
+
},
|
|
127
|
+
"unsigned int": {
|
|
128
|
+
type: "integer",
|
|
129
|
+
sizeInBits: 32,
|
|
130
|
+
fixedAbi: {},
|
|
131
|
+
signed: false
|
|
132
|
+
},
|
|
133
|
+
"long": {
|
|
134
|
+
type: "integer",
|
|
135
|
+
sizeInBits: 32,
|
|
136
|
+
fixedAbi: {},
|
|
137
|
+
signed: true
|
|
138
|
+
},
|
|
139
|
+
"unsigned long": {
|
|
140
|
+
type: "integer",
|
|
141
|
+
sizeInBits: 32,
|
|
142
|
+
fixedAbi: {},
|
|
143
|
+
signed: false
|
|
144
|
+
},
|
|
145
|
+
"long long": {
|
|
146
|
+
type: "integer",
|
|
147
|
+
sizeInBits: 64,
|
|
148
|
+
fixedAbi: {},
|
|
149
|
+
signed: true
|
|
150
|
+
},
|
|
151
|
+
"unsigned long long": {
|
|
152
|
+
type: "integer",
|
|
153
|
+
sizeInBits: 64,
|
|
154
|
+
fixedAbi: {},
|
|
155
|
+
signed: false
|
|
156
|
+
},
|
|
157
|
+
"float": {
|
|
158
|
+
type: "float",
|
|
159
|
+
sizeInBits: 32,
|
|
160
|
+
fixedAbi: {}
|
|
161
|
+
},
|
|
162
|
+
"double": {
|
|
163
|
+
type: "float",
|
|
164
|
+
sizeInBits: 64,
|
|
165
|
+
fixedAbi: {}
|
|
166
|
+
},
|
|
167
|
+
"long double": {
|
|
168
|
+
type: "float",
|
|
169
|
+
sizeInBits: 128,
|
|
170
|
+
fixedAbi: {}
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const createCTypeNormalizer = ({
|
|
177
|
+
abi
|
|
178
|
+
}: {
|
|
179
|
+
abi: TAbi
|
|
180
|
+
}) => {
|
|
181
|
+
|
|
182
|
+
const fieldMappings = cTypeMappings[abi.dataModel]?.[abi.compiler];
|
|
183
|
+
if (!fieldMappings) {
|
|
184
|
+
throw Error(`no c-type mappings for data model ${abi.dataModel} and compiler ${abi.compiler}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const normalize = ({ cField }: { cField: TCField }): TPrimitiveBasicFieldType => {
|
|
188
|
+
|
|
189
|
+
const mapping = fieldMappings[cField.cType];
|
|
190
|
+
if (mapping === undefined) {
|
|
191
|
+
throw Error(`no c-type mapping for c-type "${cField.cType}" for data model ${abi.dataModel} and compiler ${abi.compiler}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const mappingFixedAbi = mapping.fixedAbi;
|
|
195
|
+
const fieldFixedAbi = cField.fixedAbi;
|
|
196
|
+
|
|
197
|
+
const fixedAbi = {
|
|
198
|
+
...mappingFixedAbi,
|
|
199
|
+
...fieldFixedAbi
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
Object.keys(fixedAbi).forEach((keyAsString) => {
|
|
203
|
+
const key = keyAsString as keyof TAbi;
|
|
204
|
+
if (mappingFixedAbi[key] !== undefined && fieldFixedAbi[key] !== undefined) {
|
|
205
|
+
throw Error(`conflicting fixed ABI property for key ${key} between c-type mapping and c-field`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
...mapping,
|
|
211
|
+
fixedAbi
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
normalize
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
createCTypeNormalizer
|
|
222
|
+
};
|