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.
@@ -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 };
@@ -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 fieldData = new Uint8Array(data.buffer, data.byteOffset + (field.definition.offsetInBits / 8));
79
- result[field.name] = fieldParser.parse({ data: fieldData, offsetInBits: offsetInBitsInByte });
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 fieldTarget = new Uint8Array(target.buffer, target.byteOffset + (field.definition.offsetInBits / 8));
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 {
@@ -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 fieldData = new Uint8Array(data.buffer, data.byteOffset + (field.definition.offsetInBits / 8));
79
- result[field.name] = fieldParser.parse({ data: fieldData, offsetInBits: offsetInBitsInByte });
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 fieldTarget = new Uint8Array(target.buffer, target.byteOffset + (field.definition.offsetInBits / 8));
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.9",
2
+ "version": "0.0.10",
3
3
  "name": "ya-struct",
4
4
  "type": "module",
5
5
  "description": "Yet Another Node.js Structure API",
@@ -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
+ });
package/tsconfig.json CHANGED
@@ -5,6 +5,7 @@
5
5
  "moduleResolution": "NodeNext",
6
6
  "noEmit": true,
7
7
  "esModuleInterop": true,
8
- "strict": true
8
+ "strict": true,
9
+ "verbatimModuleSyntax": true
9
10
  }
10
11
  }