ya-struct 0.0.9 → 0.0.10
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/parser.js +1 -0
- package/dist/lib/types/struct.d.ts +2 -1
- package/dist/lib/types/struct.js +37 -4
- package/lib/parser.ts +1 -0
- package/lib/types/struct.ts +37 -4
- package/package.json +1 -1
- package/test/nested-structs.ts +78 -0
- package/tsconfig.json +2 -1
package/dist/lib/parser.js
CHANGED
|
@@ -51,6 +51,7 @@ const define = /*<const T extends TFieldType>*/({ definition }/*: { definition:
|
|
|
51
51
|
if (l.type === "struct") {
|
|
52
52
|
valueParser = createStructParser({
|
|
53
53
|
layoutedFields: l.fields,
|
|
54
|
+
structOffsetInBits: l.offsetInBits,
|
|
54
55
|
endianness: abi.endianness
|
|
55
56
|
}) /*as unknown*/ /*as TValueParser<TParsedValueOfDefinition<T>>*/;
|
|
56
57
|
} else {
|
|
@@ -2,10 +2,11 @@ import type { TEndianness } from "../common.js";
|
|
|
2
2
|
import type { TLayoutedField } from "../layout.js";
|
|
3
3
|
import type { TValueParser } from "./value.js";
|
|
4
4
|
type TStructParser = TValueParser<Record<string, unknown>>;
|
|
5
|
-
declare const createStructParser: ({ layoutedFields, endianness }: {
|
|
5
|
+
declare const createStructParser: ({ layoutedFields, structOffsetInBits, endianness }: {
|
|
6
6
|
layoutedFields: (TLayoutedField & {
|
|
7
7
|
type: "struct";
|
|
8
8
|
})["fields"];
|
|
9
|
+
structOffsetInBits: number;
|
|
9
10
|
endianness: TEndianness;
|
|
10
11
|
}) => TStructParser;
|
|
11
12
|
export { createStructParser };
|
package/dist/lib/types/struct.js
CHANGED
|
@@ -9,11 +9,20 @@ import { createArrayParser } from "./array.js";
|
|
|
9
9
|
|
|
10
10
|
/*type TStructParser = TValueParser<Record<string, unknown>>;*/
|
|
11
11
|
|
|
12
|
+
const subData = ({ data, offsetInBits, sizeInBits }/*: { data: Uint8Array, offsetInBits: number, sizeInBits: number }*/) => {
|
|
13
|
+
return {
|
|
14
|
+
data: new Uint8Array(data.buffer, data.byteOffset + Math.floor(offsetInBits / 8), Math.ceil(sizeInBits / 8)),
|
|
15
|
+
offsetInBits: offsetInBits % 8
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
const createStructParser = ({
|
|
13
20
|
layoutedFields,
|
|
21
|
+
structOffsetInBits,
|
|
14
22
|
endianness
|
|
15
23
|
}/*: {
|
|
16
24
|
layoutedFields: (TLayoutedField & { type: "struct" })["fields"];
|
|
25
|
+
structOffsetInBits: number;
|
|
17
26
|
endianness: TEndianness
|
|
18
27
|
}*/)/*: TStructParser*/ => {
|
|
19
28
|
|
|
@@ -37,6 +46,7 @@ const createStructParser = ({
|
|
|
37
46
|
if (field.definition.type === "struct") {
|
|
38
47
|
return createStructParser({
|
|
39
48
|
layoutedFields: field.definition.fields,
|
|
49
|
+
structOffsetInBits: field.definition.offsetInBits,
|
|
40
50
|
endianness
|
|
41
51
|
});
|
|
42
52
|
}
|
|
@@ -70,19 +80,35 @@ const createStructParser = ({
|
|
|
70
80
|
layoutedFields.forEach((field, idx) => {
|
|
71
81
|
const fieldParser = fieldParsers[idx];
|
|
72
82
|
|
|
83
|
+
// field definition is absolute, subtracting struct offset gives bit offset inside struct
|
|
84
|
+
// adding data bit offset gives bit offset inside provided data
|
|
85
|
+
const offsetToProvidedDataInBits = field.definition.offsetInBits - structOffsetInBits + offsetInBits;
|
|
86
|
+
|
|
73
87
|
const offsetInBitsInByte = field.definition.offsetInBits % 8;
|
|
74
88
|
if (offsetInBitsInByte !== 0) {
|
|
75
89
|
throw Error("not implemented yet: unaligned field parsing");
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
const
|
|
79
|
-
|
|
92
|
+
const {
|
|
93
|
+
data: fieldData,
|
|
94
|
+
offsetInBits: fieldOffsetInBits
|
|
95
|
+
} = subData({
|
|
96
|
+
data,
|
|
97
|
+
offsetInBits: offsetToProvidedDataInBits,
|
|
98
|
+
sizeInBits: field.definition.sizeInBits
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
result[field.name] = fieldParser.parse({ data: fieldData, offsetInBits: fieldOffsetInBits });
|
|
80
102
|
});
|
|
81
103
|
|
|
82
104
|
return result;
|
|
83
105
|
};
|
|
84
106
|
|
|
85
107
|
const format/*: TStructParser["format"]*/ = ({ value, target, offsetInBits }) => {
|
|
108
|
+
if (offsetInBits % 8 !== 0) {
|
|
109
|
+
throw Error("unaligned struct formatting not supported yet");
|
|
110
|
+
}
|
|
111
|
+
|
|
86
112
|
layoutedFields.forEach((field, idx) => {
|
|
87
113
|
const fieldValue = value[field.name];
|
|
88
114
|
const fieldParser = fieldParsers[idx];
|
|
@@ -92,10 +118,17 @@ const createStructParser = ({
|
|
|
92
118
|
throw Error("not implemented yet: unaligned field formatting");
|
|
93
119
|
}
|
|
94
120
|
|
|
95
|
-
const
|
|
121
|
+
const {
|
|
122
|
+
data: fieldTarget,
|
|
123
|
+
offsetInBits: fieldOffsetInBits
|
|
124
|
+
} = subData({
|
|
125
|
+
data: target,
|
|
126
|
+
offsetInBits: field.definition.offsetInBits - structOffsetInBits + offsetInBits,
|
|
127
|
+
sizeInBits: field.definition.sizeInBits
|
|
128
|
+
});
|
|
96
129
|
|
|
97
130
|
try {
|
|
98
|
-
fieldParser.format({ value: fieldValue, target: fieldTarget, offsetInBits });
|
|
131
|
+
fieldParser.format({ value: fieldValue, target: fieldTarget, offsetInBits: fieldOffsetInBits });
|
|
99
132
|
} catch (ex) {
|
|
100
133
|
throw Error(`failed to format field "${field.name}"`, { cause: ex });
|
|
101
134
|
}
|
package/lib/parser.ts
CHANGED
|
@@ -51,6 +51,7 @@ const define = <const T extends TFieldType>({ definition }: { definition: T }) =
|
|
|
51
51
|
if (l.type === "struct") {
|
|
52
52
|
valueParser = createStructParser({
|
|
53
53
|
layoutedFields: l.fields,
|
|
54
|
+
structOffsetInBits: l.offsetInBits,
|
|
54
55
|
endianness: abi.endianness
|
|
55
56
|
}) as unknown as TValueParser<TParsedValueOfDefinition<T>>;
|
|
56
57
|
} else {
|
package/lib/types/struct.ts
CHANGED
|
@@ -9,11 +9,20 @@ import type { TFieldType } from "./index.ts";
|
|
|
9
9
|
|
|
10
10
|
type TStructParser = TValueParser<Record<string, unknown>>;
|
|
11
11
|
|
|
12
|
+
const subData = ({ data, offsetInBits, sizeInBits }: { data: Uint8Array, offsetInBits: number, sizeInBits: number }) => {
|
|
13
|
+
return {
|
|
14
|
+
data: new Uint8Array(data.buffer, data.byteOffset + Math.floor(offsetInBits / 8), Math.ceil(sizeInBits / 8)),
|
|
15
|
+
offsetInBits: offsetInBits % 8
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
const createStructParser = ({
|
|
13
20
|
layoutedFields,
|
|
21
|
+
structOffsetInBits,
|
|
14
22
|
endianness
|
|
15
23
|
}: {
|
|
16
24
|
layoutedFields: (TLayoutedField & { type: "struct" })["fields"];
|
|
25
|
+
structOffsetInBits: number;
|
|
17
26
|
endianness: TEndianness
|
|
18
27
|
}): TStructParser => {
|
|
19
28
|
|
|
@@ -37,6 +46,7 @@ const createStructParser = ({
|
|
|
37
46
|
if (field.definition.type === "struct") {
|
|
38
47
|
return createStructParser({
|
|
39
48
|
layoutedFields: field.definition.fields,
|
|
49
|
+
structOffsetInBits: field.definition.offsetInBits,
|
|
40
50
|
endianness
|
|
41
51
|
});
|
|
42
52
|
}
|
|
@@ -70,19 +80,35 @@ const createStructParser = ({
|
|
|
70
80
|
layoutedFields.forEach((field, idx) => {
|
|
71
81
|
const fieldParser = fieldParsers[idx];
|
|
72
82
|
|
|
83
|
+
// field definition is absolute, subtracting struct offset gives bit offset inside struct
|
|
84
|
+
// adding data bit offset gives bit offset inside provided data
|
|
85
|
+
const offsetToProvidedDataInBits = field.definition.offsetInBits - structOffsetInBits + offsetInBits;
|
|
86
|
+
|
|
73
87
|
const offsetInBitsInByte = field.definition.offsetInBits % 8;
|
|
74
88
|
if (offsetInBitsInByte !== 0) {
|
|
75
89
|
throw Error("not implemented yet: unaligned field parsing");
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
const
|
|
79
|
-
|
|
92
|
+
const {
|
|
93
|
+
data: fieldData,
|
|
94
|
+
offsetInBits: fieldOffsetInBits
|
|
95
|
+
} = subData({
|
|
96
|
+
data,
|
|
97
|
+
offsetInBits: offsetToProvidedDataInBits,
|
|
98
|
+
sizeInBits: field.definition.sizeInBits
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
result[field.name] = fieldParser.parse({ data: fieldData, offsetInBits: fieldOffsetInBits });
|
|
80
102
|
});
|
|
81
103
|
|
|
82
104
|
return result;
|
|
83
105
|
};
|
|
84
106
|
|
|
85
107
|
const format: TStructParser["format"] = ({ value, target, offsetInBits }) => {
|
|
108
|
+
if (offsetInBits % 8 !== 0) {
|
|
109
|
+
throw Error("unaligned struct formatting not supported yet");
|
|
110
|
+
}
|
|
111
|
+
|
|
86
112
|
layoutedFields.forEach((field, idx) => {
|
|
87
113
|
const fieldValue = value[field.name];
|
|
88
114
|
const fieldParser = fieldParsers[idx];
|
|
@@ -92,10 +118,17 @@ const createStructParser = ({
|
|
|
92
118
|
throw Error("not implemented yet: unaligned field formatting");
|
|
93
119
|
}
|
|
94
120
|
|
|
95
|
-
const
|
|
121
|
+
const {
|
|
122
|
+
data: fieldTarget,
|
|
123
|
+
offsetInBits: fieldOffsetInBits
|
|
124
|
+
} = subData({
|
|
125
|
+
data: target,
|
|
126
|
+
offsetInBits: field.definition.offsetInBits - structOffsetInBits + offsetInBits,
|
|
127
|
+
sizeInBits: field.definition.sizeInBits
|
|
128
|
+
});
|
|
96
129
|
|
|
97
130
|
try {
|
|
98
|
-
fieldParser.format({ value: fieldValue, target: fieldTarget, offsetInBits });
|
|
131
|
+
fieldParser.format({ value: fieldValue, target: fieldTarget, offsetInBits: fieldOffsetInBits });
|
|
99
132
|
} catch (ex) {
|
|
100
133
|
throw Error(`failed to format field "${field.name}"`, { cause: ex });
|
|
101
134
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import nodeAssert from "node:assert";
|
|
2
|
+
import { define } from "../lib/parser.ts";
|
|
3
|
+
import { types } from "../lib/types/index.ts";
|
|
4
|
+
|
|
5
|
+
const abi = {
|
|
6
|
+
compiler: "gcc",
|
|
7
|
+
dataModel: "LP64",
|
|
8
|
+
endianness: "little",
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
11
|
+
const innerStructDefinition = {
|
|
12
|
+
type: "struct",
|
|
13
|
+
packed: false,
|
|
14
|
+
fixedAbi: {},
|
|
15
|
+
fields: [
|
|
16
|
+
{ name: "a", definition: types.UInt64 },
|
|
17
|
+
{ name: "b", definition: types.UInt32 },
|
|
18
|
+
{ name: "c", definition: types.UInt32 },
|
|
19
|
+
],
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
const outerStructDefinition = {
|
|
23
|
+
type: "struct",
|
|
24
|
+
packed: false,
|
|
25
|
+
fixedAbi: {},
|
|
26
|
+
fields: [
|
|
27
|
+
{ name: "x", definition: types.UInt64 },
|
|
28
|
+
{ name: "inner", definition: innerStructDefinition },
|
|
29
|
+
],
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
const expectedEncoded = () => {
|
|
33
|
+
const data = new Uint8Array(24);
|
|
34
|
+
const view = new DataView(data.buffer);
|
|
35
|
+
|
|
36
|
+
view.setBigUint64(0, 1n, true);
|
|
37
|
+
view.setBigUint64(8, 2n, true);
|
|
38
|
+
view.setUint32(16, 3, true);
|
|
39
|
+
view.setUint32(20, 4, true);
|
|
40
|
+
|
|
41
|
+
return data;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe("nested-structs", () => {
|
|
45
|
+
it("should format nested structs", () => {
|
|
46
|
+
const parser = define({ definition: outerStructDefinition }).parser({ abi });
|
|
47
|
+
|
|
48
|
+
const encoded = parser.format({
|
|
49
|
+
value: {
|
|
50
|
+
x: 1n,
|
|
51
|
+
inner: {
|
|
52
|
+
a: 2n,
|
|
53
|
+
b: 3n,
|
|
54
|
+
c: 4n,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
nodeAssert.deepStrictEqual(encoded, expectedEncoded());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should parse nested structs", () => {
|
|
63
|
+
const parser = define({ definition: outerStructDefinition }).parser({ abi });
|
|
64
|
+
|
|
65
|
+
const parsed = parser.parse({
|
|
66
|
+
data: expectedEncoded(),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
nodeAssert.deepStrictEqual(parsed, {
|
|
70
|
+
x: 1n,
|
|
71
|
+
inner: {
|
|
72
|
+
a: 2n,
|
|
73
|
+
b: 3n,
|
|
74
|
+
c: 4n,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|