ya-struct 0.0.3 → 0.0.4

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/.eslintrc CHANGED
@@ -6,11 +6,13 @@
6
6
  },
7
7
  "env": {
8
8
  "node": true,
9
- "es6": true
9
+ "es2020": true
10
10
  },
11
11
  "rules": {
12
12
  "global-require": "off",
13
- "quote-props": "warn",
13
+ "quote-props": ["warn", "consistent-as-needed"],
14
+ "comma-dangle": ["error", {"arrays": "always-multiline", "objects": "always-multiline", "functions": "never"}],
15
+ "id-length": ["error", {"min": 3, "properties": "always", "exceptions": ["i", "os", "fs"]}],
14
16
  "quotes": ["error", "double", { "allowTemplateLiterals": true }],
15
17
  "no-plusplus": "error",
16
18
  "no-nested-ternary": "error",
@@ -21,6 +23,8 @@
21
23
  "no-delete-var": "error",
22
24
  "no-param-reassign": "error",
23
25
  "no-return-assign": "error",
26
+ "no-import-assign": "error",
27
+ "no-multi-assign": "error",
24
28
  "keyword-spacing": "error",
25
29
  "max-len": [ "warn", { "code": 140 } ],
26
30
  "max-params": ["error", 4],
@@ -47,6 +51,7 @@
47
51
  "no-new-object": "error",
48
52
  "no-new-wrappers": "error",
49
53
  "no-useless-concat": "error",
54
+ "no-unused-vars": ["error", {"ignoreRestSiblings": true}],
50
55
  "array-bracket-newline": ["error", "consistent"],
51
56
  "func-names": ["error", "never"],
52
57
  "func-style": ["error", "expression", { "allowArrowFunctions": true }],
@@ -54,7 +59,6 @@
54
59
  "arrow-parens": "error",
55
60
  "no-confusing-arrow": "error",
56
61
  "prefer-const": "error",
57
- "prefer-destructuring": ["error", {"object": true, "array": false}],
58
62
  "rest-spread-spacing": ["error", "never"],
59
63
  "template-curly-spacing": ["error", "never"],
60
64
  "prefer-rest-params": "error",
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "trailingComma": "es5",
3
+ "tabWidth": 2,
4
+ "singleQuote": false
5
+ }
package/lib/builder.js CHANGED
@@ -1,126 +1,62 @@
1
- /* global BigInt */
1
+ import ctypes from "./types/ctypes.js";
2
+ import basicTypes from "./types/basic.js";
3
+ import marshallerFactory from "./marshaller.js";
2
4
 
3
- const bufferTypeSizes = {
4
- "UInt8": 1,
5
- "UInt16LE": 2,
6
- "UInt16BE": 2,
7
- "UInt32LE": 4,
8
- "UInt32BE": 4,
9
- "BigUInt64LE": 8,
10
- "BigUInt64BE": 8,
11
-
12
- "Int8": 1,
13
- "Int16LE": 2,
14
- "Int16BE": 2,
15
- "Int32LE": 4,
16
- "Int32BE": 4,
17
- "BigInt64LE": 8,
18
- "BigInt64BE": 8
19
- };
20
-
21
- const dataModelMaps = {
22
- "LP64": {
23
- "Pointer": "UInt64"
24
- }
25
- };
26
-
27
- const createAccessorFor = ({ type, offset }) => {
28
- const readFrom = ({ buffer }) => {
29
- return BigInt(buffer[`read${type}`](offset));
30
- };
31
-
32
- const writeTo = ({ buffer, value }) => {
33
- if (type.indexOf("Big") >= 0) {
34
- buffer[`write${type}`](value, offset);
35
- } else {
36
- buffer[`write${type}`](Number(value), offset);
37
- }
38
- };
39
-
40
- return {
41
- readFrom,
42
- writeTo
43
- };
44
- };
45
-
46
- const createFieldsViaBuilder = ({ builder, "abi": { endianness, dataModel, alignmentModel = {} } = {} }) => {
47
- let fields = {};
5
+ const createFieldsViaBuilder = ({
6
+ builder,
7
+ abi: { endianness = "LE", dataModel = "LP64", compiler = "gcc" } = {},
8
+ }) => {
9
+ let fieldDefinitions = {};
48
10
 
49
11
  let currentOffset = 0;
50
12
 
51
- const standardField = (type, alignment) => {
52
- return (name) => {
53
- const alignmentToUse = Math.max(alignment || 1, 1);
54
- currentOffset = Math.floor((currentOffset + alignmentToUse - 1) / alignmentToUse) * alignmentToUse;
55
- const offset = currentOffset;
56
-
57
- const size = bufferTypeSizes[type];
58
- if (size === undefined) {
59
- throw new Error(`could not map unknown type "${type}"`);
60
- }
61
-
62
- const { readFrom, writeTo } = createAccessorFor({ type, offset });
63
-
64
- fields = Object.assign({}, fields, {
65
- [name]: {
66
- name,
67
- readFrom,
68
- writeTo,
69
- offset,
70
- size
71
- }
72
- });
13
+ const fieldBuilderForAbi = ({ abi }) => {
14
+ let result = {};
15
+ Object.keys(abi).forEach((typeName) => {
16
+ result = {
17
+ ...result,
18
+ [typeName]: (name) => {
19
+ const def = abi[typeName]({ offset: currentOffset });
20
+
21
+ fieldDefinitions = {
22
+ ...fieldDefinitions,
23
+ [name]: def,
24
+ };
25
+
26
+ currentOffset = def.offset + def.size;
27
+ },
28
+ };
29
+ });
73
30
 
74
- currentOffset += size;
75
- };
31
+ return result;
76
32
  };
77
33
 
78
- let fieldObject = {
79
- "UInt8": standardField("UInt8", alignmentModel.UInt8),
80
- "UInt16LE": standardField("UInt16LE", alignmentModel.UInt16),
81
- "UInt16BE": standardField("UInt16BE", alignmentModel.UInt16),
82
- "UInt32LE": standardField("UInt32LE", alignmentModel.UInt32),
83
- "UInt32BE": standardField("UInt32BE", alignmentModel.UInt32),
84
- "UInt64LE": standardField("BigUInt64LE", alignmentModel.UInt64),
85
- "UInt64BE": standardField("BigUInt64BE", alignmentModel.UInt64),
34
+ const basicTypesAbi = basicTypes.abi({ dataModel, compiler, endianness });
35
+ const cTypesAbi = ctypes.abi({ dataModel, compiler, endianness });
86
36
 
87
- "Int8": standardField("Int8", alignmentModel.Int8),
88
- "Int16LE": standardField("Int16LE", alignmentModel.Int16),
89
- "Int16BE": standardField("Int16BE", alignmentModel.Int16),
90
- "Int32LE": standardField("Int32LE", alignmentModel.Int32),
91
- "Int32BE": standardField("Int32BE", alignmentModel.Int32),
92
- "Int64LE": standardField("BigInt64LE", alignmentModel.Int64),
93
- "Int64BE": standardField("BigInt64BE", alignmentModel.Int64)
37
+ const field = {
38
+ ...fieldBuilderForAbi({ abi: basicTypesAbi }),
39
+ CTypes: fieldBuilderForAbi({ abi: cTypesAbi }),
94
40
  };
95
41
 
96
- if (endianness === "LE" || endianness === "BE") {
97
- fieldObject = Object.assign({}, fieldObject, {
98
- "Int16": standardField(`Int16${endianness}`, alignmentModel.Int16),
99
- "Int32": standardField(`Int32${endianness}`, alignmentModel.Int32),
100
- "Int64": standardField(`BigInt64${endianness}`, alignmentModel.Int64),
101
- "UInt16": standardField(`UInt16${endianness}`, alignmentModel.UInt16),
102
- "UInt32": standardField(`UInt32${endianness}`, alignmentModel.UInt32),
103
- "UInt64": standardField(`BigUInt64${endianness}`, alignmentModel.UInt64),
104
- });
105
- }
106
-
107
- const dataModelMap = dataModelMaps[dataModel] || {};
108
- Object.keys(dataModelMap).forEach((key) => {
109
- fieldObject = Object.assign({}, fieldObject, {
110
- [key]: fieldObject[dataModelMap[key]]
111
- });
112
- });
113
-
114
- builder({ "field": fieldObject });
42
+ builder({ field });
115
43
 
116
44
  const size = currentOffset;
117
45
 
46
+ const { marshal, unmarshal } = marshallerFactory.create({
47
+ fieldDefinitions,
48
+ size,
49
+ });
50
+
118
51
  return {
119
- fields,
120
- size
52
+ fields: fieldDefinitions,
53
+ size,
54
+
55
+ marshal,
56
+ unmarshal,
121
57
  };
122
58
  };
123
59
 
124
60
  export default {
125
- createFieldsViaBuilder
61
+ createFieldsViaBuilder,
126
62
  };
package/lib/index.js CHANGED
@@ -2,28 +2,10 @@ import b2a from "buffer2address";
2
2
  import os from "os";
3
3
 
4
4
  import fieldBuilder from "./builder.js";
5
- import refbuf from "./refbuf.js";
6
5
 
7
6
  const defineWithBuilderAndAbi = ({ builder, abi }) => {
8
- const { fields, size } = fieldBuilder.createFieldsViaBuilder({ builder, abi });
9
-
10
- const offsetof = (fieldName) => {
11
- const field = fields[fieldName];
12
- if (!field) {
13
- throw new Error(`field "${fieldName}" not found`);
14
- }
15
-
16
- return field.offset;
17
- };
18
-
19
- const sizeof = (fieldName) => {
20
- const field = fields[fieldName];
21
- if (!field) {
22
- throw new Error(`field "${fieldName}" not found`);
23
- }
24
-
25
- return field.size;
26
- };
7
+ const { fields, size, marshal, unmarshal } =
8
+ fieldBuilder.createFieldsViaBuilder({ builder, abi });
27
9
 
28
10
  const parse = (buf) => {
29
11
  if (!Buffer.isBuffer(buf)) {
@@ -31,22 +13,22 @@ const defineWithBuilderAndAbi = ({ builder, abi }) => {
31
13
  }
32
14
 
33
15
  if (buf.length < size) {
34
- throw new Error(`given buffer is too small for structure (has ${buf.length} bytes, needs ${size} bytes)`);
16
+ throw new Error(
17
+ `given buffer is too small for structure (has ${buf.length} bytes, needs ${size} bytes)`
18
+ );
35
19
  }
36
20
 
37
- let result = {};
38
-
39
- Object.keys(fields).forEach((name) => {
40
- const field = fields[name];
41
-
42
- result = Object.assign({}, result, {
43
- [name]: field.readFrom({ "buffer": buf })
44
- });
45
- });
46
-
47
- return result;
21
+ return unmarshal({ buffer: buf });
48
22
  };
49
23
 
24
+ let emptyData = {};
25
+ Object.keys(fields).forEach((fieldName) => {
26
+ emptyData = {
27
+ ...emptyData,
28
+ [fieldName]: 0n,
29
+ };
30
+ });
31
+
50
32
  const format = (data) => {
51
33
  if (typeof data !== "object") {
52
34
  throw new Error(`given argument is not a object`);
@@ -60,53 +42,61 @@ const defineWithBuilderAndAbi = ({ builder, abi }) => {
60
42
  const value = data[fieldName];
61
43
  if (Buffer.isBuffer(value)) {
62
44
  buffers = Object.assign({}, buffers, {
63
- [fieldName]: value
45
+ [fieldName]: value,
64
46
  });
65
47
  links = [...links, value];
66
48
  } else if (typeof value === "bigint") {
67
49
  primitives = Object.assign({}, primitives, {
68
- [fieldName]: value
50
+ [fieldName]: value,
69
51
  });
70
52
  } else {
71
- throw new Error(`only Buffer and BigInt supported, "${fieldName}" was of type "${typeof value}"`);
53
+ throw new Error(
54
+ `only Buffer and BigInt supported, "${fieldName}" was of type "${typeof value}"`
55
+ );
72
56
  }
73
57
  });
74
58
 
75
- const result = refbuf.create({ links, size });
76
-
77
- Object.keys(primitives).forEach((fieldName) => {
78
- const value = data[fieldName];
79
- const field = fields[fieldName];
80
- field.writeTo({ "buffer": result, value });
81
- });
59
+ let bufferDataToMarshal = {};
82
60
 
83
61
  Object.keys(buffers).forEach((fieldName) => {
84
62
  const value = b2a.buffer2address(data[fieldName]);
85
- const field = fields[fieldName];
86
- field.writeTo({ "buffer": result, value });
63
+
64
+ bufferDataToMarshal = {
65
+ ...bufferDataToMarshal,
66
+ [fieldName]: value,
67
+ };
87
68
  });
88
69
 
89
- return result;
70
+ return marshal({
71
+ data: {
72
+ ...emptyData,
73
+ ...primitives,
74
+ ...bufferDataToMarshal,
75
+ },
76
+ links,
77
+ });
90
78
  };
91
79
 
92
80
  return {
93
- offsetof,
94
- sizeof,
81
+ fields,
95
82
  size,
96
83
 
97
84
  parse,
98
- format
85
+ format,
99
86
  };
100
87
  };
101
88
 
102
89
  const hostDataModels = {
103
- "x64": {
104
- "win32": "LLP64",
105
- "linux": "LP64"
90
+ x64: {
91
+ win32: "LLP64",
92
+ linux: "LP64",
93
+ },
94
+ arm: {
95
+ linux: "ILP32",
96
+ },
97
+ arm64: {
98
+ linux: "LP64",
106
99
  },
107
- "arm64": {
108
- "linux": "LP64"
109
- }
110
100
  };
111
101
 
112
102
  const findDataModelFor = ({ arch, platform }) => {
@@ -125,69 +115,45 @@ const findDataModelFor = ({ arch, platform }) => {
125
115
 
126
116
  const findHostDataModel = () => {
127
117
  return findDataModelFor({
128
- "arch": process.arch,
129
- "platform": process.platform
118
+ arch: process.arch,
119
+ platform: process.platform,
130
120
  });
131
121
  };
132
122
 
133
- const alignmentModels = {
134
- "LP64": {
135
- "gcc": {
136
- "Int8": 1,
137
- "UInt8": 1,
138
- "Int16": 2,
139
- "UInt16": 2,
140
- "Int32": 4,
141
- "UInt32": 4,
142
- "Int64": 8,
143
- "UInt64": 8
144
- }
145
- }
146
- };
147
-
148
123
  const findLikelyHostCompiler = () => {
149
124
  return "gcc";
150
125
  };
151
126
 
152
- const findHostAlignmentModel = () => {
153
- const dataModel = findHostDataModel();
154
-
155
- const compiler = findLikelyHostCompiler();
156
-
157
- return alignmentModels[dataModel] && alignmentModels[dataModel][compiler];
158
- };
159
-
160
127
  const define = (builder) => {
161
- const abi = ({ endianness, dataModel, alignmentModel }) => {
128
+ const abi = ({ endianness, dataModel, compiler }) => {
162
129
  return defineWithBuilderAndAbi({
163
130
  builder,
164
- "abi": {
131
+ abi: {
165
132
  endianness,
166
133
  dataModel,
167
- alignmentModel
168
- }
134
+ compiler,
135
+ },
169
136
  });
170
137
  };
171
138
 
172
139
  const forHost = () => {
173
140
  const endianness = os.endianness();
174
141
  const dataModel = findHostDataModel();
175
- const alignmentModel = findHostAlignmentModel();
176
-
142
+ const compiler = findLikelyHostCompiler();
143
+
177
144
  return abi({
178
145
  endianness,
179
146
  dataModel,
180
- alignmentModel
147
+ compiler,
181
148
  });
182
149
  };
183
-
150
+
184
151
  return {
185
152
  abi,
186
- forHost
153
+ forHost,
187
154
  };
188
155
  };
189
156
 
190
157
  export default {
191
158
  define,
192
- alignmentModels
193
159
  };
@@ -0,0 +1,80 @@
1
+ import refbuf from "./refbuf.js";
2
+
3
+ const createInterpretingMarshaller = ({ fieldDefinitions, size }) => {
4
+ const findReadWriteMethodName = ({ signed, fieldSizeInBits, endianness }) => {
5
+ const bigOrNot = fieldSizeInBits === 64 ? "Big" : "";
6
+ const signedOrNot = signed ? "" : "U";
7
+ const intType = `Int${fieldSizeInBits}`;
8
+
9
+ return `${bigOrNot}${signedOrNot}${intType}${endianness || ""}`;
10
+ };
11
+
12
+ const marshal = ({ data, links }) => {
13
+ const result = refbuf.create({ links, size });
14
+
15
+ Object.keys(fieldDefinitions).forEach((fieldName) => {
16
+ const {
17
+ signed,
18
+ offset,
19
+ size: fieldSize,
20
+ endianness,
21
+ } = fieldDefinitions[fieldName];
22
+
23
+ const fieldSizeInBits = fieldSize * 8;
24
+ const writeFuncName = `write${findReadWriteMethodName({
25
+ signed,
26
+ fieldSizeInBits,
27
+ endianness,
28
+ })}`;
29
+ const valueToWrite = writeFuncName.includes("Big")
30
+ ? BigInt(data[fieldName])
31
+ : Number(data[fieldName]);
32
+
33
+ result[writeFuncName](valueToWrite, offset);
34
+ });
35
+
36
+ return result;
37
+ };
38
+
39
+ const unmarshal = ({ buffer }) => {
40
+ let result = {};
41
+
42
+ Object.keys(fieldDefinitions).forEach((fieldName) => {
43
+ const {
44
+ signed,
45
+ offset,
46
+ size: fieldSize,
47
+ endianness,
48
+ } = fieldDefinitions[fieldName];
49
+
50
+ const fieldSizeInBits = fieldSize * 8;
51
+ const readFuncName = `read${findReadWriteMethodName({
52
+ signed,
53
+ fieldSizeInBits,
54
+ endianness,
55
+ })}`;
56
+
57
+ const val = buffer[readFuncName](offset);
58
+
59
+ result = {
60
+ ...result,
61
+ [fieldName]: BigInt(val),
62
+ };
63
+ });
64
+
65
+ return result;
66
+ };
67
+
68
+ return {
69
+ marshal,
70
+ unmarshal,
71
+ };
72
+ };
73
+
74
+ const create = ({ fieldDefinitions, size }) => {
75
+ return createInterpretingMarshaller({ fieldDefinitions, size });
76
+ };
77
+
78
+ export default {
79
+ create,
80
+ };
package/lib/refbuf.js CHANGED
@@ -7,5 +7,5 @@ const create = ({ links, size }) => {
7
7
  };
8
8
 
9
9
  export default {
10
- create
10
+ create,
11
11
  };