ya-struct 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/layout.d.ts +7 -2
- package/dist/lib/layout.js +112 -16
- package/dist/lib/parser.d.ts +27 -5
- package/dist/lib/parser.js +23 -4
- package/dist/lib/tests/compile-and-compare.vibe.js +5 -1
- package/dist/lib/types/index.d.ts +7 -2
- package/dist/lib/types/index.js +9 -1
- package/dist/lib/types/struct.js +10 -2
- package/package.json +1 -1
package/dist/lib/layout.d.ts
CHANGED
|
@@ -26,10 +26,15 @@ type TLayoutedField = {
|
|
|
26
26
|
readonly type: "struct";
|
|
27
27
|
readonly offsetInBits: number;
|
|
28
28
|
readonly sizeInBits: number;
|
|
29
|
-
readonly fields: readonly {
|
|
29
|
+
readonly fields: readonly ({
|
|
30
|
+
readonly pad?: false;
|
|
30
31
|
readonly name: string;
|
|
31
32
|
readonly definition: TLayoutedField;
|
|
32
|
-
}
|
|
33
|
+
} | {
|
|
34
|
+
readonly pad: true;
|
|
35
|
+
readonly name: string | undefined;
|
|
36
|
+
readonly definition: TLayoutedField;
|
|
37
|
+
})[];
|
|
33
38
|
readonly packed: boolean;
|
|
34
39
|
readonly fixedAbi: Partial<TAbi>;
|
|
35
40
|
} | {
|
package/dist/lib/layout.js
CHANGED
|
@@ -29,7 +29,15 @@ import nodeUtil from "node:util";
|
|
|
29
29
|
readonly type: "struct";
|
|
30
30
|
readonly offsetInBits: number;
|
|
31
31
|
readonly sizeInBits: number;
|
|
32
|
-
readonly fields: readonly {
|
|
32
|
+
readonly fields: readonly ({
|
|
33
|
+
readonly pad?: false;
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly definition: TLayoutedField;
|
|
36
|
+
} | {
|
|
37
|
+
readonly pad: true;
|
|
38
|
+
readonly name: string | undefined;
|
|
39
|
+
readonly definition: TLayoutedField;
|
|
40
|
+
})[];
|
|
33
41
|
readonly packed: boolean;
|
|
34
42
|
readonly fixedAbi: Partial<TAbi>;
|
|
35
43
|
} | {
|
|
@@ -51,6 +59,83 @@ const pointerSizeInBitsByDataModel = ({ dataModel }/*: { dataModel: TAbi["dataMo
|
|
|
51
59
|
}
|
|
52
60
|
};
|
|
53
61
|
|
|
62
|
+
// eslint-disable-next-line max-statements, complexity
|
|
63
|
+
const alignmentOfField = ({ definition, abi }/*: { definition: TFieldType, abi: TAbi }*/)/*: number*/ => {
|
|
64
|
+
if (definition.type === "c-type") {
|
|
65
|
+
const cTypeNormalizer = createCTypeNormalizer({ abi });
|
|
66
|
+
const normalizedField = cTypeNormalizer.normalize({ cField: definition });
|
|
67
|
+
return alignmentOfField({ definition: normalizedField, abi });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (definition.type === "integer" || definition.type === "float") {
|
|
71
|
+
const sizeInBits = definition.sizeInBits;
|
|
72
|
+
if (abi.compiler === "gcc" && abi.dataModel === "ILP32" && sizeInBits === 64) {
|
|
73
|
+
return 32;
|
|
74
|
+
}
|
|
75
|
+
return sizeInBits;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (definition.type === "pointer") {
|
|
79
|
+
return pointerSizeInBitsByDataModel({ dataModel: abi.dataModel });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (definition.type === "array") {
|
|
83
|
+
return alignmentOfField({ definition: definition.elementType, abi });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (definition.type === "struct") {
|
|
87
|
+
if (definition.packed) {
|
|
88
|
+
return 8;
|
|
89
|
+
}
|
|
90
|
+
if (definition.fields.length === 0) {
|
|
91
|
+
return 8;
|
|
92
|
+
}
|
|
93
|
+
return Math.max(...definition.fields.map((f) => {
|
|
94
|
+
return alignmentOfField({ definition: f.definition, abi });
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (definition.type === "string") {
|
|
99
|
+
return definition.charSizeInBits;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw Error(`unsupported field type for alignment calculation`);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// eslint-disable-next-line complexity
|
|
106
|
+
const translateLayoutOffset = ({ field, offset }/*: { field: TLayoutedField; offset: number }*/)/*: TLayoutedField*/ => {
|
|
107
|
+
if (offset === 0) {
|
|
108
|
+
return field;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
switch (field.type) {
|
|
112
|
+
case "integer":
|
|
113
|
+
return { ...field, offsetInBits: field.offsetInBits + offset };
|
|
114
|
+
case "float":
|
|
115
|
+
return { ...field, offsetInBits: field.offsetInBits + offset };
|
|
116
|
+
case "pointer":
|
|
117
|
+
return { ...field, offsetInBits: field.offsetInBits + offset };
|
|
118
|
+
case "string":
|
|
119
|
+
return { ...field, offsetInBits: field.offsetInBits + offset };
|
|
120
|
+
case "array":
|
|
121
|
+
return { ...field, offsetInBits: field.offsetInBits + offset };
|
|
122
|
+
case "struct":
|
|
123
|
+
return {
|
|
124
|
+
...field,
|
|
125
|
+
offsetInBits: field.offsetInBits + offset,
|
|
126
|
+
fields: field.fields.map((f) => {
|
|
127
|
+
const translated = translateLayoutOffset({ field: f.definition, offset });
|
|
128
|
+
if (f.pad) {
|
|
129
|
+
return { pad: true /*as const*/, name: f.name, definition: translated };
|
|
130
|
+
}
|
|
131
|
+
return { name: f.name, definition: translated };
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
default:
|
|
135
|
+
throw Error(`unsupported field type for layout offset translation`);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
54
139
|
const layoutStruct = ({
|
|
55
140
|
definition,
|
|
56
141
|
abi,
|
|
@@ -61,13 +146,13 @@ const layoutStruct = ({
|
|
|
61
146
|
currentOffsetInBits: number
|
|
62
147
|
}*/)/*: TLayoutedField*/ => {
|
|
63
148
|
|
|
64
|
-
|
|
65
|
-
const structAlignmentInBits = 64;
|
|
66
|
-
// const fieldAlignmentInBits = 64;
|
|
149
|
+
const structAlignmentInBits = definition.packed ? 8 : alignmentOfField({ definition, abi });
|
|
67
150
|
const fieldAlignmentInBits = 1;
|
|
68
151
|
const pointerSizeInBits = pointerSizeInBitsByDataModel({ dataModel: abi.dataModel });
|
|
69
152
|
|
|
70
|
-
|
|
153
|
+
// Compute internal layout from offset 0 to ensure correct internal alignment
|
|
154
|
+
// independent of the struct's placement position
|
|
155
|
+
let currentOffsetInBits = 0;
|
|
71
156
|
let layoutedFields/*: (TLayoutedField & { type: "struct" })["fields"]*/ = [];
|
|
72
157
|
|
|
73
158
|
// eslint-disable-next-line max-statements,complexity
|
|
@@ -112,11 +197,8 @@ const layoutStruct = ({
|
|
|
112
197
|
break;
|
|
113
198
|
}
|
|
114
199
|
case "struct": {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
throw Error("nested struct alignment handling not implemented yet");
|
|
118
|
-
}
|
|
119
|
-
|
|
200
|
+
const nestedAlignment = alignmentOfField({ definition: normalizedField, abi });
|
|
201
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: nestedAlignment });
|
|
120
202
|
break;
|
|
121
203
|
}
|
|
122
204
|
case "string": {
|
|
@@ -137,20 +219,34 @@ const layoutStruct = ({
|
|
|
137
219
|
|
|
138
220
|
layoutedFields = [
|
|
139
221
|
...layoutedFields,
|
|
140
|
-
|
|
141
|
-
name: field.name,
|
|
142
|
-
definition: fieldLayout
|
|
143
|
-
}
|
|
222
|
+
field.pad
|
|
223
|
+
? { pad: true /*as const*/, name: field.name, definition: fieldLayout }
|
|
224
|
+
: { name: field.name, definition: fieldLayout }
|
|
144
225
|
];
|
|
145
226
|
|
|
146
227
|
currentOffsetInBits = align({ offset: fieldLayout.offsetInBits + fieldLayout.sizeInBits, alignment: fieldAlignmentInBits });
|
|
147
228
|
});
|
|
148
229
|
|
|
230
|
+
if (!definition.packed) {
|
|
231
|
+
currentOffsetInBits = align({ offset: currentOffsetInBits, alignment: structAlignmentInBits });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const sizeInBits = currentOffsetInBits;
|
|
235
|
+
|
|
236
|
+
// Translate all field offsets to the actual placement position
|
|
237
|
+
const translatedFields = layoutedFields.map((f) => {
|
|
238
|
+
const translated = translateLayoutOffset({ field: f.definition, offset: initialOffsetInBits });
|
|
239
|
+
if (f.pad) {
|
|
240
|
+
return { pad: true /*as const*/, name: f.name, definition: translated };
|
|
241
|
+
}
|
|
242
|
+
return { name: f.name, definition: translated };
|
|
243
|
+
});
|
|
244
|
+
|
|
149
245
|
return {
|
|
150
246
|
type: "struct",
|
|
151
247
|
offsetInBits: initialOffsetInBits,
|
|
152
|
-
sizeInBits
|
|
153
|
-
fields:
|
|
248
|
+
sizeInBits,
|
|
249
|
+
fields: translatedFields,
|
|
154
250
|
packed: definition.packed,
|
|
155
251
|
fixedAbi: definition.fixedAbi
|
|
156
252
|
};
|
package/dist/lib/parser.d.ts
CHANGED
|
@@ -16,20 +16,42 @@ type FieldValue<T extends TFieldType> = T extends {
|
|
|
16
16
|
} ? FieldValue<E & TFieldType>[] : T extends {
|
|
17
17
|
type: "struct";
|
|
18
18
|
fields: infer F;
|
|
19
|
-
} ? StructValue<F & readonly {
|
|
19
|
+
} ? StructValue<F & readonly ({
|
|
20
|
+
pad?: false | undefined;
|
|
20
21
|
name: string;
|
|
21
22
|
definition: TFieldType;
|
|
22
|
-
}
|
|
23
|
+
} | {
|
|
24
|
+
pad: true;
|
|
25
|
+
name: string | undefined;
|
|
26
|
+
definition: TFieldType;
|
|
27
|
+
})[]> : T extends {
|
|
23
28
|
type: "c-type";
|
|
24
29
|
cType: "float" | "double" | "long double";
|
|
25
30
|
} ? number : T extends {
|
|
26
31
|
type: "c-type";
|
|
27
32
|
} ? bigint : never;
|
|
28
|
-
type
|
|
33
|
+
type NonPadFields<F extends readonly ({
|
|
34
|
+
pad?: false | undefined;
|
|
35
|
+
name: string;
|
|
36
|
+
definition: TFieldType;
|
|
37
|
+
} | {
|
|
38
|
+
pad: true;
|
|
39
|
+
name: string | undefined;
|
|
40
|
+
definition: TFieldType;
|
|
41
|
+
})[]> = Extract<F[number], {
|
|
42
|
+
pad?: false | undefined;
|
|
43
|
+
name: string;
|
|
44
|
+
}>;
|
|
45
|
+
type StructValue<F extends readonly ({
|
|
46
|
+
pad?: false | undefined;
|
|
29
47
|
name: string;
|
|
30
48
|
definition: TFieldType;
|
|
31
|
-
}
|
|
32
|
-
|
|
49
|
+
} | {
|
|
50
|
+
pad: true;
|
|
51
|
+
name: string | undefined;
|
|
52
|
+
definition: TFieldType;
|
|
53
|
+
})[]> = {
|
|
54
|
+
[K in NonPadFields<F> as K["name"]]: FieldValue<K["definition"]>;
|
|
33
55
|
};
|
|
34
56
|
type Simplify<T> = {
|
|
35
57
|
[K in keyof T]: T[K];
|
package/dist/lib/parser.js
CHANGED
|
@@ -12,15 +12,34 @@ import { createStructParser } from "./types/struct.js";
|
|
|
12
12
|
T extends { type: "array"; elementType: infer E; length: number }
|
|
13
13
|
? FieldValue<E & TFieldType>[] :
|
|
14
14
|
T extends { type: "struct"; fields: infer F }
|
|
15
|
-
? StructValue<
|
|
16
|
-
|
|
15
|
+
? StructValue<
|
|
16
|
+
F & readonly (
|
|
17
|
+
| { pad?: false | undefined; name: string; definition: TFieldType }
|
|
18
|
+
| { pad: true; name: string | undefined; definition: TFieldType }
|
|
19
|
+
)[]
|
|
20
|
+
> :
|
|
21
|
+
T extends {
|
|
22
|
+
type: "c-type";
|
|
23
|
+
cType: "float" | "double" | "long double";
|
|
24
|
+
} ? number :
|
|
17
25
|
T extends { type: "c-type" } ? bigint :
|
|
18
26
|
never;*/
|
|
19
27
|
|
|
28
|
+
/*type NonPadFields<
|
|
29
|
+
F extends readonly (
|
|
30
|
+
| { pad?: false | undefined; name: string; definition: TFieldType }
|
|
31
|
+
| { pad: true; name: string | undefined; definition: TFieldType }
|
|
32
|
+
)[]
|
|
33
|
+
> =
|
|
34
|
+
Extract<F[number], { pad?: false | undefined; name: string }>;*/
|
|
35
|
+
|
|
20
36
|
/*type StructValue<
|
|
21
|
-
F extends readonly
|
|
37
|
+
F extends readonly (
|
|
38
|
+
| { pad?: false | undefined; name: string; definition: TFieldType }
|
|
39
|
+
| { pad: true; name: string | undefined; definition: TFieldType }
|
|
40
|
+
)[]
|
|
22
41
|
> = {
|
|
23
|
-
[K in F
|
|
42
|
+
[K in NonPadFields<F> as K["name"]]: FieldValue<K["definition"]>;
|
|
24
43
|
};*/
|
|
25
44
|
|
|
26
45
|
/*type Simplify<T> = {
|
|
@@ -37,9 +37,13 @@ const flattenLayout = ({
|
|
|
37
37
|
parentPath: string;
|
|
38
38
|
}*/)/*: TFlatField[]*/ => {
|
|
39
39
|
return fields.flatMap((field) => {
|
|
40
|
+
if (field.pad) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
const fieldPath = parentPath
|
|
41
45
|
? `${parentPath}.${field.name}`
|
|
42
|
-
: field.name
|
|
46
|
+
: field.name/*!*/;
|
|
43
47
|
|
|
44
48
|
const isStruct = field.definition.type === "struct";
|
|
45
49
|
|
|
@@ -18,10 +18,15 @@ type TFieldType = {
|
|
|
18
18
|
readonly length: number;
|
|
19
19
|
} | {
|
|
20
20
|
readonly type: "struct";
|
|
21
|
-
readonly fields: readonly {
|
|
21
|
+
readonly fields: readonly ({
|
|
22
|
+
readonly pad?: false | undefined;
|
|
22
23
|
readonly name: string;
|
|
23
24
|
readonly definition: TFieldType;
|
|
24
|
-
}
|
|
25
|
+
} | {
|
|
26
|
+
readonly pad: true;
|
|
27
|
+
readonly name: string | undefined;
|
|
28
|
+
readonly definition: TFieldType;
|
|
29
|
+
})[];
|
|
25
30
|
readonly packed: boolean;
|
|
26
31
|
readonly fixedAbi: Partial<TAbi>;
|
|
27
32
|
} | {
|
package/dist/lib/types/index.js
CHANGED
|
@@ -26,7 +26,15 @@
|
|
|
26
26
|
readonly length: number;
|
|
27
27
|
} | {
|
|
28
28
|
readonly type: "struct";
|
|
29
|
-
readonly fields: readonly {
|
|
29
|
+
readonly fields: readonly ({
|
|
30
|
+
readonly pad?: false | undefined;
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly definition: TFieldType
|
|
33
|
+
} | {
|
|
34
|
+
readonly pad: true;
|
|
35
|
+
readonly name: string | undefined;
|
|
36
|
+
readonly definition: TFieldType;
|
|
37
|
+
})[];
|
|
30
38
|
readonly packed: boolean;
|
|
31
39
|
readonly fixedAbi: Partial<TAbi>;
|
|
32
40
|
} | {
|
package/dist/lib/types/struct.js
CHANGED
|
@@ -78,6 +78,10 @@ const createStructParser = ({
|
|
|
78
78
|
let result/*: Record<string, unknown>*/ = {};
|
|
79
79
|
|
|
80
80
|
layoutedFields.forEach((field, idx) => {
|
|
81
|
+
if (field.pad) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
81
85
|
const fieldParser = fieldParsers[idx];
|
|
82
86
|
|
|
83
87
|
// field definition is absolute, subtracting struct offset gives bit offset inside struct
|
|
@@ -99,7 +103,7 @@ const createStructParser = ({
|
|
|
99
103
|
});
|
|
100
104
|
|
|
101
105
|
// eslint-disable-next-line immutable/no-mutation -- performance
|
|
102
|
-
result[field.name] = fieldParser.parse({ data: fieldData, offsetInBits: fieldOffsetInBits });
|
|
106
|
+
result[field.name/*!*/] = fieldParser.parse({ data: fieldData, offsetInBits: fieldOffsetInBits });
|
|
103
107
|
});
|
|
104
108
|
|
|
105
109
|
return result;
|
|
@@ -111,7 +115,11 @@ const createStructParser = ({
|
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
layoutedFields.forEach((field, idx) => {
|
|
114
|
-
|
|
118
|
+
if (field.pad) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const fieldValue = value[field.name/*!*/];
|
|
115
123
|
const fieldParser = fieldParsers[idx];
|
|
116
124
|
|
|
117
125
|
const offsetInBitsInByte = field.definition.offsetInBits % 8;
|
package/package.json
CHANGED