ya-struct 0.0.8 → 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.
@@ -111,6 +111,14 @@ const layoutStruct = ({
111
111
 
112
112
  break;
113
113
  }
114
+ case "struct": {
115
+
116
+ if (currentOffsetInBits % 64 !== 0) {
117
+ throw Error("nested struct alignment handling not implemented yet");
118
+ }
119
+
120
+ break;
121
+ }
114
122
  case "string": {
115
123
  // no special alignment needed
116
124
  break;
@@ -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 {
@@ -37,7 +37,7 @@ const createStringParser = ({ length }/*: { length: number }*/)/*: TStringParser
37
37
 
38
38
  const encoded = encoder.encode(value);
39
39
 
40
- if (encoded.length + 1 >= length) {
40
+ if (encoded.length + 1 > length) {
41
41
  throw Error("string too long to fit in target");
42
42
  }
43
43
 
@@ -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/layout.ts CHANGED
@@ -111,6 +111,14 @@ const layoutStruct = ({
111
111
 
112
112
  break;
113
113
  }
114
+ case "struct": {
115
+
116
+ if (currentOffsetInBits % 64 !== 0) {
117
+ throw Error("nested struct alignment handling not implemented yet");
118
+ }
119
+
120
+ break;
121
+ }
114
122
  case "string": {
115
123
  // no special alignment needed
116
124
  break;
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 {
@@ -37,7 +37,7 @@ const createStringParser = ({ length }: { length: number }): TStringParser => {
37
37
 
38
38
  const encoded = encoder.encode(value);
39
39
 
40
- if (encoded.length + 1 >= length) {
40
+ if (encoded.length + 1 > length) {
41
41
  throw Error("string too long to fit in target");
42
42
  }
43
43
 
@@ -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.8",
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
  }