node-ctypes 0.1.3 → 0.1.5

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/lib/index.js CHANGED
@@ -23,12 +23,7 @@ const getNativeModulePath = (moduleName, nativeFile, config = "Release") => {
23
23
  if (process.versions?.electron) {
24
24
  const electronProcess = process;
25
25
  if (electronProcess.resourcesPath) {
26
- const unpackedPath = path.join(
27
- electronProcess.resourcesPath,
28
- "app.asar.unpacked",
29
- "node_modules",
30
- moduleName,
31
- );
26
+ const unpackedPath = path.join(electronProcess.resourcesPath, "app.asar.unpacked", "node_modules", moduleName);
32
27
  if (existsSync(buildPath(unpackedPath))) {
33
28
  return buildPath(unpackedPath);
34
29
  }
@@ -36,9 +31,7 @@ const getNativeModulePath = (moduleName, nativeFile, config = "Release") => {
36
31
  }
37
32
 
38
33
  // Check in current working directory's node_modules
39
- const cwdPath = buildPath(
40
- path.join(process.cwd(), "node_modules", moduleName),
41
- );
34
+ const cwdPath = buildPath(path.join(process.cwd(), "node_modules", moduleName));
42
35
  if (existsSync(cwdPath)) {
43
36
  return cwdPath;
44
37
  }
@@ -58,10 +51,7 @@ const getNativeModulePath = (moduleName, nativeFile, config = "Release") => {
58
51
  }
59
52
  }
60
53
 
61
- throw new Error(
62
- `Native module "${nativeFile}" not found for "${moduleName}". ` +
63
- `Searched in Electron resources, node_modules, and local build.`,
64
- );
54
+ throw new Error(`Native module "${nativeFile}" not found for "${moduleName}". ` + `Searched in Electron resources, node_modules, and local build.`);
65
55
  };
66
56
 
67
57
  // Load the native module
@@ -71,25 +61,13 @@ try {
71
61
  } catch (e) {
72
62
  const platform = process.platform;
73
63
  const arch = process.arch;
74
- nativeModulePath = getNativeModulePath(
75
- "node-ctypes",
76
- `ctypes-${platform}-${arch}.node`,
77
- );
64
+ nativeModulePath = getNativeModulePath("node-ctypes", `ctypes-${platform}-${arch}.node`);
78
65
  }
79
66
  const require = createRequire(path.join(process.cwd(), "index.js"));
80
67
  const native = require(nativeModulePath);
81
68
 
82
69
  // Re-esporta le classi native
83
- const {
84
- Version,
85
- Library,
86
- FFIFunction,
87
- Callback,
88
- ThreadSafeCallback,
89
- CType,
90
- StructType,
91
- ArrayType,
92
- } = native;
70
+ const { Version, Library, FFIFunction, Callback, ThreadSafeCallback, CType, StructType, ArrayType } = native;
93
71
 
94
72
  // Tipi predefiniti
95
73
  const types = native.types;
@@ -112,10 +90,7 @@ function _toNativeType(type) {
112
90
  return type._type;
113
91
  }
114
92
  // Struct/Union classes pass through
115
- if (
116
- typeof type === "function" &&
117
- (type.prototype instanceof Structure || type.prototype instanceof Union)
118
- ) {
93
+ if (typeof type === "function" && (type.prototype instanceof Structure || type.prototype instanceof Union)) {
119
94
  return type;
120
95
  }
121
96
  // Native CType objects pass through
@@ -132,18 +107,6 @@ function _toNativeTypes(types) {
132
107
  return types.map(_toNativeType);
133
108
  }
134
109
 
135
- // ============================================================================
136
- // Type Lookup Tables
137
- // Centralizes type information to avoid duplicated switch statements
138
- // ============================================================================
139
-
140
- // TYPE_READERS removed - using SimpleCData._reader directly
141
-
142
- // TYPE_WRITERS removed - using SimpleCData._writer directly
143
-
144
- /** @type {Map<string, number>} */
145
- // TYPE_SIZES removed - using SimpleCData._size directly
146
-
147
110
  /**
148
111
  * Carica una libreria dinamica
149
112
  * @param {string|null} libPath - Percorso della libreria o null per l'eseguibile corrente
@@ -153,6 +116,62 @@ function load(libPath) {
153
116
  return new Library(libPath);
154
117
  }
155
118
 
119
+ /**
120
+ * Wrapper per funzioni con sintassi Python-like ctypes (argtypes/restype)
121
+ */
122
+ class FunctionWrapper {
123
+ constructor(cdll, name) {
124
+ const argtypes = [];
125
+ let restype = undefined;
126
+ let cachedFunc = null;
127
+
128
+ // Create a function and proxy it to make it callable
129
+ const func = (...args) => {
130
+ if (restype === undefined) {
131
+ throw new Error(`Function ${name}: restype not set`);
132
+ }
133
+ if (!cachedFunc) {
134
+ cachedFunc = cdll.func(name, restype, argtypes);
135
+ }
136
+ return cachedFunc(...args);
137
+ };
138
+
139
+ // Proxy the function to intercept property access
140
+ return new Proxy(func, {
141
+ get(target, prop) {
142
+ if (prop === "argtypes") return argtypes;
143
+ if (prop === "restype") return restype;
144
+ if (prop === "errcheck") return cachedFunc ? cachedFunc.errcheck : undefined;
145
+ return target[prop];
146
+ },
147
+ set(target, prop, value) {
148
+ if (prop === "argtypes") {
149
+ argtypes.splice(0, argtypes.length, ...value);
150
+ cachedFunc = null; // Invalidate cache
151
+ return true;
152
+ }
153
+ if (prop === "restype") {
154
+ restype = value;
155
+ cachedFunc = null; // Invalidate cache
156
+ return true;
157
+ }
158
+ if (prop === "errcheck") {
159
+ if (!cachedFunc) {
160
+ if (restype === undefined) {
161
+ throw new Error(`Function ${name}: restype not set`);
162
+ }
163
+ cachedFunc = cdll.func(name, restype, argtypes);
164
+ }
165
+ cachedFunc.errcheck = value;
166
+ return true;
167
+ }
168
+ target[prop] = value;
169
+ return true;
170
+ },
171
+ });
172
+ }
173
+ }
174
+
156
175
  /**
157
176
  * Wrapper conveniente per CDLL (come in Python ctypes)
158
177
  */
@@ -160,6 +179,18 @@ class CDLL {
160
179
  constructor(libPath) {
161
180
  this._lib = new Library(libPath);
162
181
  this._cache = new Map();
182
+
183
+ // Return a proxy to enable Python-like syntax: libc.abs.argtypes = [...]; libc.abs.restype = ...; libc.abs(...)
184
+ return new Proxy(this, {
185
+ get(target, prop, receiver) {
186
+ // Allow access to existing properties/methods
187
+ if (prop in target || typeof prop !== "string") {
188
+ return Reflect.get(target, prop, receiver);
189
+ }
190
+ // Assume it's a function name from the library
191
+ return new FunctionWrapper(target, prop);
192
+ },
193
+ });
163
194
  }
164
195
 
165
196
  /**
@@ -177,44 +208,99 @@ class CDLL {
177
208
  return this._cache.get(cacheKey);
178
209
  }
179
210
 
180
- const ffiFunc = this._lib.func(
181
- name,
182
- _toNativeType(returnType),
183
- _toNativeTypes(argTypes),
184
- options,
185
- );
186
-
187
- // Crea wrapper minimo ottimizzato
188
- // Estrae ._buffer solo da argomenti che sono oggetti NON-Buffer
189
- const callMethod = function (...args) {
190
- // Process args: extract _buffer from struct proxies
191
- const processedArgs = [];
192
-
193
- for (let i = 0; i < args.length; i++) {
194
- const arg = args[i];
195
- // Check for _buffer FIRST (handles Proxy-wrapped structs)
196
- // This avoids calling Buffer.isBuffer() on Proxy which can cause issues
197
- if (arg && typeof arg === "object") {
198
- // Check for struct proxy FIRST - accessing _buffer on Proxy is safe
199
- // but Buffer.isBuffer(proxy) can cause hangs
200
- if (arg._buffer !== undefined && Buffer.isBuffer(arg._buffer)) {
201
- // Struct proxy or object with _buffer
202
- processedArgs.push(arg._buffer);
203
- } else if (Buffer.isBuffer(arg)) {
204
- // Already a buffer, use directly
205
- processedArgs.push(arg);
211
+ const ffiFunc = this._lib.func(name, _toNativeType(returnType), _toNativeTypes(argTypes), options);
212
+
213
+ // =========================================================================
214
+ // OPTIMIZATION: Create specialized wrapper based on argument types
215
+ // For primitive-only args, bypass the processing loop entirely
216
+ // =========================================================================
217
+
218
+ const argCount = argTypes.length;
219
+
220
+ // Check if all args are primitive types (no structs/buffers that need _buffer extraction)
221
+ const allPrimitive = argTypes.every((t) => {
222
+ if (typeof t === "function" && t._isSimpleCData) {
223
+ // SimpleCData types that are NOT pointers are primitive
224
+ const typeName = t._type;
225
+ return typeName !== "pointer" && typeName !== "char_p" && typeName !== "wchar_p";
226
+ }
227
+ if (typeof t === "string") {
228
+ return t !== "pointer" && t !== "char_p" && t !== "wchar_p" && t !== "void_p";
229
+ }
230
+ // CType objects from native - check if it's a pointer type
231
+ if (t && typeof t === "object" && t.name) {
232
+ return !t.name.includes("pointer") && !t.name.includes("char_p");
233
+ }
234
+ return false;
235
+ });
236
+
237
+ let callMethod;
238
+
239
+ if (argCount === 0) {
240
+ // FAST PATH: No arguments - direct call
241
+ callMethod = function () {
242
+ return ffiFunc.call();
243
+ };
244
+ } else if (allPrimitive) {
245
+ // FAST PATH: All primitive args - direct call without processing
246
+ switch (argCount) {
247
+ case 1:
248
+ callMethod = function (a0) {
249
+ return ffiFunc.call(a0);
250
+ };
251
+ break;
252
+ case 2:
253
+ callMethod = function (a0, a1) {
254
+ return ffiFunc.call(a0, a1);
255
+ };
256
+ break;
257
+ case 3:
258
+ callMethod = function (a0, a1, a2) {
259
+ return ffiFunc.call(a0, a1, a2);
260
+ };
261
+ break;
262
+ case 4:
263
+ callMethod = function (a0, a1, a2, a3) {
264
+ return ffiFunc.call(a0, a1, a2, a3);
265
+ };
266
+ break;
267
+ default:
268
+ // Fallback for many args
269
+ callMethod = function (...args) {
270
+ return ffiFunc.call(...args);
271
+ };
272
+ }
273
+ } else {
274
+ // SLOW PATH: Has buffer/struct args - need to extract _buffer
275
+ callMethod = function (...args) {
276
+ const processedArgs = [];
277
+
278
+ for (let i = 0; i < args.length; i++) {
279
+ const arg = args[i];
280
+ // Check for _buffer FIRST (handles Proxy-wrapped structs)
281
+ // This avoids calling Buffer.isBuffer() on Proxy which can cause issues
282
+ if (arg && typeof arg === "object") {
283
+ // Check for struct proxy FIRST - accessing _buffer on Proxy is safe
284
+ // but Buffer.isBuffer(proxy) can cause hangs
285
+ if (arg._buffer !== undefined && Buffer.isBuffer(arg._buffer)) {
286
+ // Struct proxy or object with _buffer
287
+ processedArgs.push(arg._buffer);
288
+ } else if (Buffer.isBuffer(arg)) {
289
+ // Already a buffer, use directly
290
+ processedArgs.push(arg);
291
+ } else {
292
+ // Other object, pass as-is
293
+ processedArgs.push(arg);
294
+ }
206
295
  } else {
207
- // Other object, pass as-is
296
+ // Primitive value (number, string, bigint, null, undefined)
208
297
  processedArgs.push(arg);
209
298
  }
210
- } else {
211
- // Primitive value (number, string, bigint, null, undefined)
212
- processedArgs.push(arg);
213
299
  }
214
- }
215
300
 
216
- return ffiFunc.call(...processedArgs);
217
- };
301
+ return ffiFunc.call(...processedArgs);
302
+ };
303
+ }
218
304
 
219
305
  // Aggiungi metadata come proprietà non-enumerable per non interferire
220
306
  Object.defineProperties(callMethod, {
@@ -256,11 +342,7 @@ class CDLL {
256
342
  * @returns {Object} Oggetto callback con proprietà pointer e metodi
257
343
  */
258
344
  callback(fn, returnType, argTypes = []) {
259
- return this._lib.callback(
260
- _toNativeType(returnType),
261
- _toNativeTypes(argTypes),
262
- fn,
263
- );
345
+ return this._lib.callback(_toNativeType(returnType), _toNativeTypes(argTypes), fn);
264
346
  }
265
347
 
266
348
  /**
@@ -306,11 +388,7 @@ class WinDLL extends CDLL {
306
388
  * @returns {Object} Oggetto con .pointer (BigInt), .release()
307
389
  */
308
390
  function callback(fn, returnType, argTypes = []) {
309
- const cb = new Callback(
310
- fn,
311
- _toNativeType(returnType),
312
- _toNativeTypes(argTypes),
313
- );
391
+ const cb = new Callback(fn, _toNativeType(returnType), _toNativeTypes(argTypes));
314
392
 
315
393
  return {
316
394
  get pointer() {
@@ -338,11 +416,7 @@ function callback(fn, returnType, argTypes = []) {
338
416
  * @returns {Object} Oggetto con .pointer (BigInt), .release()
339
417
  */
340
418
  function threadSafeCallback(fn, returnType, argTypes = []) {
341
- const cb = new ThreadSafeCallback(
342
- fn,
343
- _toNativeType(returnType),
344
- _toNativeTypes(argTypes),
345
- );
419
+ const cb = new ThreadSafeCallback(fn, _toNativeType(returnType), _toNativeTypes(argTypes));
346
420
 
347
421
  return {
348
422
  get pointer() {
@@ -380,8 +454,23 @@ function cstring(str) {
380
454
  * @returns {Buffer} Buffer con stringa wide
381
455
  */
382
456
  function wstring(str) {
383
- // Converti stringa JS a UTF-16LE (che è come wchar_t su Windows)
384
- const buf = Buffer.from(str + "\0", "utf16le");
457
+ // Crea un buffer contenente la stringa wide null-terminata
458
+ // Su Windows wchar_t è 2 bytes (UTF-16LE), su Unix è 4 bytes (UTF-32LE)
459
+ const wcharSize = native.WCHAR_SIZE;
460
+ if (wcharSize === 2) {
461
+ return Buffer.from(str + "\0", "utf16le");
462
+ }
463
+
464
+ // Costruisci manualmente UTF-32LE
465
+ const codePoints = [];
466
+ for (const ch of str) {
467
+ codePoints.push(ch.codePointAt(0));
468
+ }
469
+ const buf = Buffer.alloc((codePoints.length + 1) * 4);
470
+ for (let i = 0; i < codePoints.length; i++) {
471
+ buf.writeUInt32LE(codePoints[i], i * 4);
472
+ }
473
+ // terminatore 0 già inizializzato
385
474
  return buf;
386
475
  }
387
476
 
@@ -547,12 +636,7 @@ function sizeof(type) {
547
636
  }
548
637
 
549
638
  // SimpleCData instance (e.g., sizeof(new c_uint64()))
550
- if (
551
- type &&
552
- type._buffer &&
553
- type.constructor &&
554
- type.constructor._isSimpleCData
555
- ) {
639
+ if (type && type._buffer && type.constructor && type.constructor._isSimpleCData) {
556
640
  return type.constructor._size;
557
641
  }
558
642
 
@@ -569,26 +653,16 @@ function sizeof(type) {
569
653
  }
570
654
 
571
655
  // Struct type
572
- if (
573
- typeof type === "object" &&
574
- type !== null &&
575
- typeof type.size === "number"
576
- ) {
656
+ if (typeof type === "object" && type !== null && typeof type.size === "number") {
577
657
  return type.size;
578
658
  }
579
659
 
580
660
  // Array type
581
- if (
582
- typeof type === "object" &&
583
- type !== null &&
584
- typeof type.getSize === "function"
585
- ) {
661
+ if (typeof type === "object" && type !== null && typeof type.getSize === "function") {
586
662
  return type.getSize();
587
663
  }
588
664
 
589
- throw new TypeError(
590
- `sizeof: unsupported type. Use SimpleCData classes like c_int32, c_uint64, etc.`,
591
- );
665
+ throw new TypeError(`sizeof: unsupported type. Use SimpleCData classes like c_int32, c_uint64, etc.`);
592
666
  }
593
667
 
594
668
  /**
@@ -690,8 +764,7 @@ function ptrToBuffer(address, size) {
690
764
  * @returns {Object} Tipo puntatore
691
765
  */
692
766
  function POINTER(baseType) {
693
- const baseSize =
694
- typeof baseType === "object" ? baseType.size : sizeof(baseType);
767
+ const baseSize = typeof baseType === "object" ? baseType.size : sizeof(baseType);
695
768
  const typeName = typeof baseType === "object" ? "struct" : baseType;
696
769
 
697
770
  return {
@@ -767,12 +840,28 @@ function union(fields) {
767
840
  let size, alignment;
768
841
  let fieldDef = { name, type, offset: 0 };
769
842
 
770
- // Nested struct
843
+ // Nested struct (object form)
771
844
  if (_isStruct(type)) {
772
845
  size = type.size;
773
846
  alignment = type.alignment;
774
847
  fieldDef.isNested = true;
775
848
  }
849
+ // Struct class (declarata come `class X extends Structure`)
850
+ else if (typeof type === "function" && type.prototype instanceof Structure) {
851
+ const nested = type._structDef || type._buildStruct();
852
+ size = nested.size;
853
+ alignment = nested.alignment;
854
+ fieldDef.type = nested;
855
+ fieldDef.isNested = true;
856
+ }
857
+ // Union class (declarata come `class X extends Union`)
858
+ else if (typeof type === "function" && type.prototype instanceof Union) {
859
+ const nested = type._unionDef || type._buildUnion();
860
+ size = nested.size;
861
+ alignment = nested.alignment;
862
+ fieldDef.type = nested;
863
+ fieldDef.isNested = true;
864
+ }
776
865
  // Array type
777
866
  else if (_isArrayType(type)) {
778
867
  size = type.getSize();
@@ -794,9 +883,7 @@ function union(fields) {
794
883
  else {
795
884
  // Validate type is a SimpleCData class
796
885
  if (!(typeof type === "function" && type._isSimpleCData)) {
797
- throw new TypeError(
798
- `union field "${name}": type must be a SimpleCData class, struct, union, or array`,
799
- );
886
+ throw new TypeError(`union field "${name}": type must be a SimpleCData class, struct, union, or array`);
800
887
  }
801
888
  size = type._size;
802
889
  alignment = Math.min(size, native.POINTER_SIZE);
@@ -854,13 +941,7 @@ function union(fields) {
854
941
  if (prop === Symbol.iterator) return undefined;
855
942
 
856
943
  // Handle Buffer/TypedArray properties FIRST before field checks
857
- if (
858
- prop === "length" ||
859
- prop === "byteLength" ||
860
- prop === "byteOffset" ||
861
- prop === "buffer" ||
862
- prop === "BYTES_PER_ELEMENT"
863
- ) {
944
+ if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
864
945
  return target[prop];
865
946
  }
866
947
 
@@ -895,10 +976,7 @@ function union(fields) {
895
976
  return fieldDefs.map((f) => f.name);
896
977
  },
897
978
  getOwnPropertyDescriptor(target, prop) {
898
- if (
899
- typeof prop === "string" &&
900
- (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")
901
- ) {
979
+ if (typeof prop === "string" && (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")) {
902
980
  return {
903
981
  enumerable: prop !== "_buffer" && prop !== "toObject",
904
982
  configurable: true,
@@ -928,13 +1006,7 @@ function union(fields) {
928
1006
 
929
1007
  // Bit field
930
1008
  if (field.isBitField) {
931
- return _readBitField(
932
- buf,
933
- field.offset,
934
- field.type,
935
- field.bitOffset,
936
- field.bitSize,
937
- );
1009
+ return _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
938
1010
  }
939
1011
 
940
1012
  // Nested struct
@@ -948,19 +1020,11 @@ function union(fields) {
948
1020
  if (prop === Symbol.toStringTag) return "NestedUnionInstance";
949
1021
  if (prop === Symbol.iterator) return undefined;
950
1022
  // Handle Buffer/TypedArray properties
951
- if (
952
- prop === "length" ||
953
- prop === "byteLength" ||
954
- prop === "byteOffset" ||
955
- prop === "buffer" ||
956
- prop === "BYTES_PER_ELEMENT"
957
- ) {
1023
+ if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
958
1024
  return target[prop];
959
1025
  }
960
1026
  if (typeof prop === "string" && field.type.fields) {
961
- if (
962
- field.type.fields.some((f) => f.name === prop && !f.isAnonymous)
963
- ) {
1027
+ if (field.type.fields.some((f) => f.name === prop && !f.isAnonymous)) {
964
1028
  return field.type.get(target, prop);
965
1029
  }
966
1030
  }
@@ -970,9 +1034,7 @@ function union(fields) {
970
1034
  },
971
1035
  set(target, prop, value, receiver) {
972
1036
  if (typeof prop === "string" && field.type.fields) {
973
- if (
974
- field.type.fields.some((f) => f.name === prop && !f.isAnonymous)
975
- ) {
1037
+ if (field.type.fields.some((f) => f.name === prop && !f.isAnonymous)) {
976
1038
  field.type.set(target, prop, value);
977
1039
  return true;
978
1040
  }
@@ -987,16 +1049,10 @@ function union(fields) {
987
1049
  return Reflect.has(target, prop);
988
1050
  },
989
1051
  ownKeys(target) {
990
- return (field.type.fields || [])
991
- .filter((f) => !f.isAnonymous)
992
- .map((f) => f.name);
1052
+ return (field.type.fields || []).filter((f) => !f.isAnonymous).map((f) => f.name);
993
1053
  },
994
1054
  getOwnPropertyDescriptor(target, prop) {
995
- if (
996
- typeof prop === "string" &&
997
- field.type.fields &&
998
- field.type.fields.some((f) => f.name === prop)
999
- ) {
1055
+ if (typeof prop === "string" && field.type.fields && field.type.fields.some((f) => f.name === prop)) {
1000
1056
  return {
1001
1057
  enumerable: true,
1002
1058
  configurable: true,
@@ -1010,9 +1066,7 @@ function union(fields) {
1010
1066
 
1011
1067
  // Array
1012
1068
  if (field.isArray) {
1013
- return field.type.wrap(
1014
- buf.subarray(field.offset, field.offset + field.size),
1015
- );
1069
+ return field.type.wrap(buf.subarray(field.offset, field.offset + field.size));
1016
1070
  }
1017
1071
 
1018
1072
  // Tipo base
@@ -1038,14 +1092,7 @@ function union(fields) {
1038
1092
 
1039
1093
  // Bit field
1040
1094
  if (field.isBitField) {
1041
- _writeBitField(
1042
- buf,
1043
- field.offset,
1044
- field.type,
1045
- field.bitOffset,
1046
- field.bitSize,
1047
- value,
1048
- );
1095
+ _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
1049
1096
  return;
1050
1097
  }
1051
1098
 
@@ -1054,10 +1101,7 @@ function union(fields) {
1054
1101
  if (Buffer.isBuffer(value)) {
1055
1102
  value.copy(buf, field.offset, 0, field.size);
1056
1103
  } else if (typeof value === "object") {
1057
- const nestedBuf = buf.subarray(
1058
- field.offset,
1059
- field.offset + field.size,
1060
- );
1104
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
1061
1105
  for (const [k, v] of Object.entries(value)) {
1062
1106
  field.type.set(nestedBuf, k, v);
1063
1107
  }
@@ -1070,9 +1114,7 @@ function union(fields) {
1070
1114
  if (Buffer.isBuffer(value)) {
1071
1115
  value.copy(buf, field.offset, 0, field.size);
1072
1116
  } else if (Array.isArray(value)) {
1073
- const wrapped = field.type.wrap(
1074
- buf.subarray(field.offset, field.offset + field.size),
1075
- );
1117
+ const wrapped = field.type.wrap(buf.subarray(field.offset, field.offset + field.size));
1076
1118
  for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
1077
1119
  wrapped[i] = value[i];
1078
1120
  }
@@ -1090,11 +1132,7 @@ function union(fields) {
1090
1132
  toObject(bufOrObj) {
1091
1133
  // Se è già un object (non buffer), ritorna così com'è
1092
1134
  if (!Buffer.isBuffer(bufOrObj)) {
1093
- if (
1094
- bufOrObj &&
1095
- bufOrObj._buffer &&
1096
- bufOrObj._buffer.length === maxSize
1097
- ) {
1135
+ if (bufOrObj && bufOrObj._buffer && bufOrObj._buffer.length === maxSize) {
1098
1136
  return bufOrObj; // Già convertito
1099
1137
  }
1100
1138
  // Se è un plain object, convertilo in union
@@ -1128,13 +1166,7 @@ function union(fields) {
1128
1166
  Object.defineProperty(obj, field.name, {
1129
1167
  get() {
1130
1168
  if (field.isBitField) {
1131
- return _readBitField(
1132
- buf,
1133
- 0,
1134
- field.type,
1135
- field.bitOffset,
1136
- field.bitSize,
1137
- );
1169
+ return _readBitField(buf, 0, field.type, field.bitOffset, field.bitSize);
1138
1170
  } else if (field.isNested) {
1139
1171
  // Cache nested objects per evitare di ricrearli ogni volta
1140
1172
  if (!nestedCache.has(field.name)) {
@@ -1152,14 +1184,7 @@ function union(fields) {
1152
1184
  },
1153
1185
  set(value) {
1154
1186
  if (field.isBitField) {
1155
- _writeBitField(
1156
- buf,
1157
- 0,
1158
- field.type,
1159
- field.bitOffset,
1160
- field.bitSize,
1161
- value,
1162
- );
1187
+ _writeBitField(buf, 0, field.type, field.bitOffset, field.bitSize, value);
1163
1188
  } else {
1164
1189
  writeValue(buf, field.type, value, 0);
1165
1190
  }
@@ -1229,12 +1254,7 @@ class Structure {
1229
1254
  buf.fill(0);
1230
1255
 
1231
1256
  // Support positional args: new Point(10,20)
1232
- if (
1233
- args.length === 1 &&
1234
- typeof args[0] === "object" &&
1235
- !Array.isArray(args[0]) &&
1236
- !Buffer.isBuffer(args[0])
1237
- ) {
1257
+ if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) && !Buffer.isBuffer(args[0])) {
1238
1258
  const initial = args[0];
1239
1259
  for (const [k, v] of Object.entries(initial)) {
1240
1260
  def.set(buf, k, v);
@@ -1312,8 +1332,7 @@ class Structure {
1312
1332
  return Reflect.set(target, prop, value, receiver);
1313
1333
  },
1314
1334
  has(target, prop) {
1315
- if (prop === "_buffer" || prop === "_structDef" || prop === "toObject")
1316
- return true;
1335
+ if (prop === "_buffer" || prop === "_structDef" || prop === "toObject") return true;
1317
1336
  if (typeof prop === "string" && fieldMap.has(prop)) return true;
1318
1337
  if (typeof prop === "string" && anonFieldNames.has(prop)) return true;
1319
1338
  return Reflect.has(target, prop);
@@ -1332,10 +1351,7 @@ class Structure {
1332
1351
  return keys;
1333
1352
  },
1334
1353
  getOwnPropertyDescriptor(target, prop) {
1335
- if (
1336
- typeof prop === "string" &&
1337
- (fieldMap.has(prop) || anonFieldNames.has(prop))
1338
- ) {
1354
+ if (typeof prop === "string" && (fieldMap.has(prop) || anonFieldNames.has(prop))) {
1339
1355
  return {
1340
1356
  enumerable: true,
1341
1357
  configurable: true,
@@ -1465,19 +1481,11 @@ class Structure {
1465
1481
 
1466
1482
  // Direct typed array access for numeric fields (maximum performance)
1467
1483
  getInt32Array(offset = 0, length) {
1468
- return new Int32Array(
1469
- this.__buffer.buffer,
1470
- this.__buffer.byteOffset + offset,
1471
- length,
1472
- );
1484
+ return new Int32Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
1473
1485
  }
1474
1486
 
1475
1487
  getFloat64Array(offset = 0, length) {
1476
- return new Float64Array(
1477
- this.__buffer.buffer,
1478
- this.__buffer.byteOffset + offset,
1479
- length,
1480
- );
1488
+ return new Float64Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
1481
1489
  }
1482
1490
 
1483
1491
  // Get raw buffer slice for external operations
@@ -1543,26 +1551,31 @@ class Union extends Structure {
1543
1551
  * console.log(arr[2]); // 42
1544
1552
  */
1545
1553
  function array(elementType, count) {
1546
- // Validate elementType is a SimpleCData class
1547
- if (!(typeof elementType === "function" && elementType._isSimpleCData)) {
1548
- throw new TypeError(
1549
- "array elementType must be a SimpleCData class (e.g., c_int32, c_uint8)",
1550
- );
1554
+ // Validate elementType: accept SimpleCData classes, Structure/Union classes,
1555
+ // or native CType-like objects.
1556
+ const isSimple = typeof elementType === "function" && elementType._isSimpleCData;
1557
+ const isStruct = typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union);
1558
+ const isNativeTypeObj = typeof elementType === "object" && elementType !== null && (typeof elementType.getSize === "function" || elementType.size !== undefined);
1559
+ if (!isSimple && !isStruct && !isNativeTypeObj) {
1560
+ throw new TypeError("array elementType must be a SimpleCData class or a Structure/Union class");
1551
1561
  }
1552
1562
 
1553
- const elementSize = elementType._size;
1563
+ const elementSize = sizeof(elementType);
1554
1564
  let nativeArray;
1555
- if (typeof elementType === "object") {
1565
+ if (isNativeTypeObj) {
1556
1566
  nativeArray = new ArrayType(elementType, count);
1557
1567
  } else {
1558
- // For strings, create a mock
1568
+ // Provide a small JS-side shim implementing the minimal ArrayType API
1559
1569
  nativeArray = {
1560
1570
  getSize: () => count * elementSize,
1561
1571
  getLength: () => count,
1562
- getAlignment: () => elementSize, // Approximation
1572
+ getAlignment: () => elementSize,
1563
1573
  create: (values) => {
1564
- // Already handled in JS
1565
- throw new Error("Should not be called");
1574
+ // JS create is implemented below; this should not be invoked by JS code
1575
+ const size = count * elementSize;
1576
+ const buffer = alloc(size);
1577
+ buffer.fill(0);
1578
+ return buffer;
1566
1579
  },
1567
1580
  };
1568
1581
  }
@@ -1592,7 +1605,13 @@ function array(elementType, count) {
1592
1605
  if (prop === Symbol.iterator) {
1593
1606
  return function* () {
1594
1607
  for (let i = 0; i < count; i++) {
1595
- yield elementType._reader(target, i * elementSize);
1608
+ const off = i * elementSize;
1609
+ // If elementType is a struct/union class, return an instance bound to the slice
1610
+ if (typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union)) {
1611
+ yield new elementType(target.subarray(off, off + elementSize));
1612
+ } else {
1613
+ yield readValue(target, elementType, off);
1614
+ }
1596
1615
  }
1597
1616
  };
1598
1617
  }
@@ -1610,7 +1629,11 @@ function array(elementType, count) {
1610
1629
  const index = Number(prop);
1611
1630
  if (Number.isInteger(index) && !isNaN(index)) {
1612
1631
  if (index >= 0 && index < count) {
1613
- return readValue(target, elementType, index * elementSize);
1632
+ const off = index * elementSize;
1633
+ if (typeof elementType === "function" && (elementType.prototype instanceof Structure || elementType.prototype instanceof Union)) {
1634
+ return new elementType(target.subarray(off, off + elementSize));
1635
+ }
1636
+ return readValue(target, elementType, off);
1614
1637
  }
1615
1638
  // Indice numerico fuori bounds -> undefined (comportamento JavaScript)
1616
1639
  return undefined;
@@ -1666,12 +1689,7 @@ function array(elementType, count) {
1666
1689
  }
1667
1690
  } else if (typeof values === "string") {
1668
1691
  for (let i = 0; i < Math.min(values.length, count); i++) {
1669
- writeValue(
1670
- buffer,
1671
- elementType,
1672
- values.charCodeAt(i),
1673
- i * sizeof(elementType),
1674
- );
1692
+ writeValue(buffer, elementType, values.charCodeAt(i), i * sizeof(elementType));
1675
1693
  }
1676
1694
  }
1677
1695
  return wrap(buffer);
@@ -1786,15 +1804,7 @@ function _initWinError() {
1786
1804
  _win_error_funcs = {
1787
1805
  GetLastError: kernel32.func("GetLastError", c_uint32, []),
1788
1806
  SetLastError: kernel32.func("SetLastError", c_void, [c_uint32]),
1789
- FormatMessageW: kernel32.func("FormatMessageW", c_uint32, [
1790
- c_uint32,
1791
- c_void_p,
1792
- c_uint32,
1793
- c_uint32,
1794
- c_void_p,
1795
- c_uint32,
1796
- c_void_p,
1797
- ]),
1807
+ FormatMessageW: kernel32.func("FormatMessageW", c_uint32, [c_uint32, c_void_p, c_uint32, c_uint32, c_void_p, c_uint32, c_void_p]),
1798
1808
  _lib: kernel32,
1799
1809
  };
1800
1810
  } catch (e) {
@@ -1893,9 +1903,7 @@ function bitfield(baseType, bits) {
1893
1903
  const maxBits = baseSize * 8;
1894
1904
 
1895
1905
  if (bits < 1 || bits > maxBits) {
1896
- throw new Error(
1897
- `Bit field size must be between 1 and ${maxBits} for ${baseType}`,
1898
- );
1906
+ throw new Error(`Bit field size must be between 1 and ${maxBits} for ${baseType}`);
1899
1907
  }
1900
1908
 
1901
1909
  return {
@@ -1910,22 +1918,14 @@ function bitfield(baseType, bits) {
1910
1918
  * Helper per verificare se un tipo è una struct
1911
1919
  */
1912
1920
  function _isStruct(type) {
1913
- return (
1914
- typeof type === "object" &&
1915
- type !== null &&
1916
- typeof type.size === "number" &&
1917
- Array.isArray(type.fields) &&
1918
- typeof type.create === "function"
1919
- );
1921
+ return typeof type === "object" && type !== null && typeof type.size === "number" && Array.isArray(type.fields) && typeof type.create === "function";
1920
1922
  }
1921
1923
 
1922
1924
  /**
1923
1925
  * Helper per verificare se un tipo è un array type
1924
1926
  */
1925
1927
  function _isArrayType(type) {
1926
- return (
1927
- typeof type === "object" && type !== null && type._isArrayType === true
1928
- );
1928
+ return typeof type === "object" && type !== null && type._isArrayType === true;
1929
1929
  }
1930
1930
 
1931
1931
  /**
@@ -2007,8 +2007,7 @@ function _writeBitField(buf, offset, baseType, bitOffset, bitSize, newValue) {
2007
2007
  if (baseSize === 8) {
2008
2008
  const mask = (1n << BigInt(bitSize)) - 1n;
2009
2009
  const clearMask = ~(mask << BigInt(bitOffset));
2010
- value =
2011
- (value & clearMask) | ((BigInt(newValue) & mask) << BigInt(bitOffset));
2010
+ value = (value & clearMask) | ((BigInt(newValue) & mask) << BigInt(bitOffset));
2012
2011
  } else {
2013
2012
  const mask = (1 << bitSize) - 1;
2014
2013
  const clearMask = ~(mask << bitOffset);
@@ -2046,12 +2045,7 @@ function struct(fields, options = {}) {
2046
2045
  let fieldDef;
2047
2046
 
2048
2047
  // Caso 0: Anonymous field - { type: SomeStruct, anonymous: true }
2049
- if (
2050
- typeof type === "object" &&
2051
- type !== null &&
2052
- type.anonymous === true &&
2053
- type.type
2054
- ) {
2048
+ if (typeof type === "object" && type !== null && type.anonymous === true && type.type) {
2055
2049
  // Reset bit field state
2056
2050
  currentBitFieldBase = null;
2057
2051
  currentBitOffset = 0;
@@ -2087,9 +2081,7 @@ function struct(fields, options = {}) {
2087
2081
  const alignment = packed ? 1 : Math.min(baseSize, native.POINTER_SIZE);
2088
2082
 
2089
2083
  // Verifica se possiamo continuare nel bit field corrente
2090
- const canContinue =
2091
- currentBitFieldBase === baseType &&
2092
- currentBitOffset + bits <= baseSize * 8;
2084
+ const canContinue = currentBitFieldBase === baseType && currentBitOffset + bits <= baseSize * 8;
2093
2085
 
2094
2086
  if (!canContinue) {
2095
2087
  // Inizia un nuovo bit field
@@ -2122,6 +2114,66 @@ function struct(fields, options = {}) {
2122
2114
  maxAlignment = alignment;
2123
2115
  }
2124
2116
  }
2117
+ // Caso 2a: Struct class (declarata come `class X extends Structure`)
2118
+ else if (typeof type === "function" && type.prototype instanceof Structure) {
2119
+ // Reset bit field state
2120
+ currentBitFieldBase = null;
2121
+ currentBitOffset = 0;
2122
+
2123
+ const nestedStruct = type._structDef || type._buildStruct();
2124
+ const size = nestedStruct.size;
2125
+ const alignment = packed ? 1 : nestedStruct.alignment;
2126
+
2127
+ // Applica padding per allineamento
2128
+ if (!packed && totalSize % alignment !== 0) {
2129
+ totalSize += alignment - (totalSize % alignment);
2130
+ }
2131
+
2132
+ fieldDef = {
2133
+ name,
2134
+ type: nestedStruct,
2135
+ offset: totalSize,
2136
+ size,
2137
+ alignment,
2138
+ isNested: true,
2139
+ };
2140
+
2141
+ totalSize += size;
2142
+
2143
+ if (alignment > maxAlignment) {
2144
+ maxAlignment = alignment;
2145
+ }
2146
+ }
2147
+ // Caso 2b: Union class (declarata come `class X extends Union`)
2148
+ else if (typeof type === "function" && type.prototype instanceof Union) {
2149
+ // Reset bit field state
2150
+ currentBitFieldBase = null;
2151
+ currentBitOffset = 0;
2152
+
2153
+ const nestedUnion = type._unionDef || type._buildUnion();
2154
+ const size = nestedUnion.size;
2155
+ const alignment = packed ? 1 : nestedUnion.alignment;
2156
+
2157
+ // Applica padding per allineamento
2158
+ if (!packed && totalSize % alignment !== 0) {
2159
+ totalSize += alignment - (totalSize % alignment);
2160
+ }
2161
+
2162
+ fieldDef = {
2163
+ name,
2164
+ type: nestedUnion,
2165
+ offset: totalSize,
2166
+ size,
2167
+ alignment,
2168
+ isNested: true,
2169
+ };
2170
+
2171
+ totalSize += size;
2172
+
2173
+ if (alignment > maxAlignment) {
2174
+ maxAlignment = alignment;
2175
+ }
2176
+ }
2125
2177
  // Caso 2: Nested struct
2126
2178
  else if (_isStruct(type)) {
2127
2179
  // Reset bit field state
@@ -2190,9 +2242,7 @@ function struct(fields, options = {}) {
2190
2242
 
2191
2243
  // Validate type is a SimpleCData class
2192
2244
  if (!(typeof type === "function" && type._isSimpleCData)) {
2193
- throw new TypeError(
2194
- `struct field "${name}": type must be a SimpleCData class, struct, union, or array`,
2195
- );
2245
+ throw new TypeError(`struct field "${name}": type must be a SimpleCData class, struct, union, or array`);
2196
2246
  }
2197
2247
 
2198
2248
  const size = type._size;
@@ -2252,9 +2302,7 @@ function struct(fields, options = {}) {
2252
2302
  if (fieldType && fieldType._isSimpleCData) {
2253
2303
  const fieldOffset = offset; // Capture offset for closure
2254
2304
  fieldReaders.set(name, (buf) => fieldType._reader(buf, fieldOffset));
2255
- fieldWriters.set(name, (buf, val) =>
2256
- fieldType._writer(buf, fieldOffset, val),
2257
- );
2305
+ fieldWriters.set(name, (buf, val) => fieldType._writer(buf, fieldOffset, val));
2258
2306
  }
2259
2307
  }
2260
2308
 
@@ -2303,13 +2351,7 @@ function struct(fields, options = {}) {
2303
2351
 
2304
2352
  // Handle Buffer/TypedArray properties FIRST before field checks
2305
2353
  // TypedArray methods need direct access to avoid Proxy interference
2306
- if (
2307
- prop === "length" ||
2308
- prop === "byteLength" ||
2309
- prop === "byteOffset" ||
2310
- prop === "buffer" ||
2311
- prop === "BYTES_PER_ELEMENT"
2312
- ) {
2354
+ if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
2313
2355
  return target[prop];
2314
2356
  }
2315
2357
 
@@ -2394,10 +2436,7 @@ function struct(fields, options = {}) {
2394
2436
  return keys;
2395
2437
  },
2396
2438
  getOwnPropertyDescriptor(target, prop) {
2397
- if (
2398
- typeof prop === "string" &&
2399
- (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")
2400
- ) {
2439
+ if (typeof prop === "string" && (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")) {
2401
2440
  return {
2402
2441
  enumerable: prop !== "_buffer" && prop !== "toObject",
2403
2442
  configurable: true,
@@ -2469,47 +2508,28 @@ function struct(fields, options = {}) {
2469
2508
  if (field) {
2470
2509
  // Bit field
2471
2510
  if (field.isBitField) {
2472
- return _readBitField(
2473
- buf,
2474
- field.offset,
2475
- field.type,
2476
- field.bitOffset,
2477
- field.bitSize,
2478
- );
2511
+ return _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
2479
2512
  }
2480
2513
 
2481
2514
  // Nested struct (senza dot = ritorna intero oggetto)
2482
2515
  if (field.isNested) {
2483
- const nestedBuf = buf.subarray(
2484
- field.offset,
2485
- field.offset + field.size,
2486
- );
2516
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2487
2517
  // Proxy-based nested instance
2488
2518
  return field.type.create
2489
2519
  ? // Se ha create(), usa quello per creare il proxy
2490
2520
  new Proxy(nestedBuf, {
2491
2521
  get(target, prop, receiver) {
2492
2522
  if (prop === "_buffer") return target;
2493
- if (prop === "toObject")
2494
- return () => field.type.toObject(target);
2495
- if (prop === Symbol.toStringTag)
2496
- return "NestedStructInstance";
2523
+ if (prop === "toObject") return () => field.type.toObject(target);
2524
+ if (prop === Symbol.toStringTag) return "NestedStructInstance";
2497
2525
  if (prop === Symbol.iterator) return undefined;
2498
2526
  // Handle Buffer/TypedArray properties
2499
- if (
2500
- prop === "length" ||
2501
- prop === "byteLength" ||
2502
- prop === "byteOffset" ||
2503
- prop === "buffer" ||
2504
- prop === "BYTES_PER_ELEMENT"
2505
- ) {
2527
+ if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
2506
2528
  return target[prop];
2507
2529
  }
2508
2530
  if (typeof prop === "string") {
2509
2531
  // Check direct fields
2510
- const nestedFieldMap = field.type.fields
2511
- ? new Map(field.type.fields.map((f) => [f.name, f]))
2512
- : new Map();
2532
+ const nestedFieldMap = field.type.fields ? new Map(field.type.fields.map((f) => [f.name, f])) : new Map();
2513
2533
  if (nestedFieldMap.has(prop)) {
2514
2534
  return field.type.get(target, prop);
2515
2535
  }
@@ -2528,9 +2548,7 @@ function struct(fields, options = {}) {
2528
2548
  },
2529
2549
  set(target, prop, value, receiver) {
2530
2550
  if (typeof prop === "string") {
2531
- const nestedFieldMap = field.type.fields
2532
- ? new Map(field.type.fields.map((f) => [f.name, f]))
2533
- : new Map();
2551
+ const nestedFieldMap = field.type.fields ? new Map(field.type.fields.map((f) => [f.name, f])) : new Map();
2534
2552
  if (nestedFieldMap.has(prop)) {
2535
2553
  field.type.set(target, prop, value);
2536
2554
  return true;
@@ -2550,8 +2568,7 @@ function struct(fields, options = {}) {
2550
2568
  has(target, prop) {
2551
2569
  if (prop === "_buffer" || prop === "toObject") return true;
2552
2570
  if (typeof prop === "string" && field.type.fields) {
2553
- if (field.type.fields.some((f) => f.name === prop))
2554
- return true;
2571
+ if (field.type.fields.some((f) => f.name === prop)) return true;
2555
2572
  }
2556
2573
  return Reflect.has(target, prop);
2557
2574
  },
@@ -2559,11 +2576,7 @@ function struct(fields, options = {}) {
2559
2576
  return (field.type.fields || []).map((f) => f.name);
2560
2577
  },
2561
2578
  getOwnPropertyDescriptor(target, prop) {
2562
- if (
2563
- typeof prop === "string" &&
2564
- field.type.fields &&
2565
- field.type.fields.some((f) => f.name === prop)
2566
- ) {
2579
+ if (typeof prop === "string" && field.type.fields && field.type.fields.some((f) => f.name === prop)) {
2567
2580
  return {
2568
2581
  enumerable: true,
2569
2582
  configurable: true,
@@ -2578,10 +2591,7 @@ function struct(fields, options = {}) {
2578
2591
 
2579
2592
  // Array field
2580
2593
  if (field.isArray) {
2581
- const arrayBuf = buf.subarray(
2582
- field.offset,
2583
- field.offset + field.size,
2584
- );
2594
+ const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2585
2595
  return field.type.wrap(arrayBuf);
2586
2596
  }
2587
2597
 
@@ -2599,9 +2609,7 @@ function struct(fields, options = {}) {
2599
2609
  if (!field) {
2600
2610
  for (const f of fieldDefs) {
2601
2611
  if (f.isAnonymous && f.type && f.type.fields) {
2602
- const hasField = f.type.fields.some(
2603
- (subField) => subField.name === firstPart,
2604
- );
2612
+ const hasField = f.type.fields.some((subField) => subField.name === firstPart);
2605
2613
  if (hasField) {
2606
2614
  const nestedBuf = buf.subarray(f.offset, f.offset + f.size);
2607
2615
  return f.type.get(nestedBuf, fieldName);
@@ -2650,14 +2658,7 @@ function struct(fields, options = {}) {
2650
2658
  if (field) {
2651
2659
  // Bit field
2652
2660
  if (field.isBitField) {
2653
- _writeBitField(
2654
- buf,
2655
- field.offset,
2656
- field.type,
2657
- field.bitOffset,
2658
- field.bitSize,
2659
- value,
2660
- );
2661
+ _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
2661
2662
  return;
2662
2663
  }
2663
2664
 
@@ -2666,10 +2667,7 @@ function struct(fields, options = {}) {
2666
2667
  if (Buffer.isBuffer(value)) {
2667
2668
  value.copy(buf, field.offset, 0, field.size);
2668
2669
  } else if (typeof value === "object") {
2669
- const nestedBuf = buf.subarray(
2670
- field.offset,
2671
- field.offset + field.size,
2672
- );
2670
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2673
2671
  for (const [k, v] of Object.entries(value)) {
2674
2672
  field.type.set(nestedBuf, k, v);
2675
2673
  }
@@ -2682,16 +2680,9 @@ function struct(fields, options = {}) {
2682
2680
  if (Buffer.isBuffer(value)) {
2683
2681
  value.copy(buf, field.offset, 0, field.size);
2684
2682
  } else if (Array.isArray(value)) {
2685
- const arrayBuf = buf.subarray(
2686
- field.offset,
2687
- field.offset + field.size,
2688
- );
2683
+ const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2689
2684
  const wrapped = field.type.wrap(arrayBuf);
2690
- for (
2691
- let i = 0;
2692
- i < Math.min(value.length, field.type.length);
2693
- i++
2694
- ) {
2685
+ for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
2695
2686
  wrapped[i] = value[i];
2696
2687
  }
2697
2688
  }
@@ -2714,9 +2705,7 @@ function struct(fields, options = {}) {
2714
2705
  for (const f of fieldDefs) {
2715
2706
  if (f.isAnonymous && f.type && f.type.fields) {
2716
2707
  // Verifica se il campo esiste nel tipo anonimo
2717
- const hasField = f.type.fields.some(
2718
- (subField) => subField.name === firstPart,
2719
- );
2708
+ const hasField = f.type.fields.some((subField) => subField.name === firstPart);
2720
2709
  if (hasField) {
2721
2710
  // Scrive nel campo dell'anonymous field
2722
2711
  const nestedBuf = buf.subarray(f.offset, f.offset + f.size);
@@ -2730,14 +2719,7 @@ function struct(fields, options = {}) {
2730
2719
 
2731
2720
  // Bit field
2732
2721
  if (field.isBitField) {
2733
- _writeBitField(
2734
- buf,
2735
- field.offset,
2736
- field.type,
2737
- field.bitOffset,
2738
- field.bitSize,
2739
- value,
2740
- );
2722
+ _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
2741
2723
  return;
2742
2724
  }
2743
2725
 
@@ -2745,20 +2727,14 @@ function struct(fields, options = {}) {
2745
2727
  if (field.isNested) {
2746
2728
  if (parts.length > 1) {
2747
2729
  // Accesso a campo annidato singolo
2748
- const nestedBuf = buf.subarray(
2749
- field.offset,
2750
- field.offset + field.size,
2751
- );
2730
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2752
2731
  field.type.set(nestedBuf, parts.slice(1).join("."), value);
2753
2732
  } else if (Buffer.isBuffer(value)) {
2754
2733
  // Copia buffer
2755
2734
  value.copy(buf, field.offset, 0, field.size);
2756
2735
  } else if (typeof value === "object") {
2757
2736
  // Imposta campi da oggetto
2758
- const nestedBuf = buf.subarray(
2759
- field.offset,
2760
- field.offset + field.size,
2761
- );
2737
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2762
2738
  for (const [k, v] of Object.entries(value)) {
2763
2739
  field.type.set(nestedBuf, k, v);
2764
2740
  }
@@ -2773,10 +2749,7 @@ function struct(fields, options = {}) {
2773
2749
  value.copy(buf, field.offset, 0, field.size);
2774
2750
  } else if (Array.isArray(value)) {
2775
2751
  // Inizializza da array JavaScript
2776
- const arrayBuf = buf.subarray(
2777
- field.offset,
2778
- field.offset + field.size,
2779
- );
2752
+ const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2780
2753
  const wrapped = field.type.wrap(arrayBuf);
2781
2754
  for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
2782
2755
  wrapped[i] = value[i];
@@ -2819,18 +2792,9 @@ function struct(fields, options = {}) {
2819
2792
  // Leggi tutti i campi in una volta (eager approach)
2820
2793
  for (const field of fieldDefs) {
2821
2794
  if (field.isBitField) {
2822
- result[field.name] = _readBitField(
2823
- buf,
2824
- field.offset,
2825
- field.type,
2826
- field.bitOffset,
2827
- field.bitSize,
2828
- );
2795
+ result[field.name] = _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
2829
2796
  } else if (field.isNested) {
2830
- const nestedBuf = buf.subarray(
2831
- field.offset,
2832
- field.offset + field.size,
2833
- );
2797
+ const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2834
2798
  const nestedObj = field.type.toObject(nestedBuf);
2835
2799
  if (field.isAnonymous) {
2836
2800
  // Anonymous fields: promote their fields to parent level
@@ -2839,10 +2803,7 @@ function struct(fields, options = {}) {
2839
2803
  result[field.name] = nestedObj;
2840
2804
  }
2841
2805
  } else if (field.isArray) {
2842
- const arrayBuf = buf.subarray(
2843
- field.offset,
2844
- field.offset + field.size,
2845
- );
2806
+ const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2846
2807
  result[field.name] = field.type.wrap(arrayBuf);
2847
2808
  } else {
2848
2809
  // Tipo base - lettura diretta
@@ -2863,8 +2824,7 @@ function struct(fields, options = {}) {
2863
2824
  // O(1) lookup con Map
2864
2825
  const field = fieldMap.get(fieldName);
2865
2826
  if (!field) throw new Error(`Unknown field: ${fieldName}`);
2866
- if (!field.isNested)
2867
- throw new Error(`Field ${fieldName} is not a nested struct`);
2827
+ if (!field.isNested) throw new Error(`Field ${fieldName} is not a nested struct`);
2868
2828
  return buf.subarray(field.offset, field.offset + field.size);
2869
2829
  },
2870
2830
  };
@@ -2876,10 +2836,7 @@ function struct(fields, options = {}) {
2876
2836
  structDef._cppStructType = cppStructType;
2877
2837
  } catch (e) {
2878
2838
  // Se fallisce, continua senza C++ (fallback a JS)
2879
- console.warn(
2880
- "Failed to create C++ StructType, using JavaScript fallback:",
2881
- e.message,
2882
- );
2839
+ console.warn("Failed to create C++ StructType, using JavaScript fallback:", e.message);
2883
2840
  }
2884
2841
 
2885
2842
  // fromObject: write plain object values into buffer
@@ -3026,7 +2983,10 @@ class c_int32 extends SimpleCData {
3026
2983
  static _size = 4;
3027
2984
  static _type = "int32";
3028
2985
  static _reader = (buf, off) => buf.readInt32LE(off);
3029
- static _writer = (buf, off, val) => buf.writeInt32LE(val, off);
2986
+ static _writer = (buf, off, val) => {
2987
+ const v = Number(val) | 0; // coerce to signed 32-bit
2988
+ return buf.writeInt32LE(v, off);
2989
+ };
3030
2990
  }
3031
2991
 
3032
2992
  class c_int64 extends SimpleCData {
@@ -3140,10 +3100,11 @@ class c_wchar extends SimpleCData {
3140
3100
  class c_void_p extends SimpleCData {
3141
3101
  static _size = native.POINTER_SIZE;
3142
3102
  static _type = "pointer";
3143
- static _reader = (buf, off) =>
3144
- native.POINTER_SIZE === 8
3145
- ? buf.readBigUInt64LE(off)
3146
- : BigInt(buf.readUInt32LE(off));
3103
+ static _reader = (buf, off) => {
3104
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
3105
+ if (!ptr) return null;
3106
+ return ptr;
3107
+ };
3147
3108
  static _writer = (buf, off, val) => {
3148
3109
  // Accept Buffers (pointer to memory) and struct proxies with _buffer
3149
3110
  if (Buffer.isBuffer(val)) {
@@ -3153,12 +3114,7 @@ class c_void_p extends SimpleCData {
3153
3114
  else buf.writeUInt32LE(Number(v), off);
3154
3115
  return;
3155
3116
  }
3156
- if (
3157
- val &&
3158
- typeof val === "object" &&
3159
- val._buffer &&
3160
- Buffer.isBuffer(val._buffer)
3161
- ) {
3117
+ if (val && typeof val === "object" && val._buffer && Buffer.isBuffer(val._buffer)) {
3162
3118
  const addr = addressOf(val._buffer);
3163
3119
  const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
3164
3120
  if (native.POINTER_SIZE === 8) buf.writeBigUInt64LE(v, off);
@@ -3179,10 +3135,11 @@ class c_void_p extends SimpleCData {
3179
3135
  class c_size_t extends SimpleCData {
3180
3136
  static _size = native.POINTER_SIZE;
3181
3137
  static _type = "size_t";
3182
- static _reader = (buf, off) =>
3183
- native.POINTER_SIZE === 8
3184
- ? buf.readBigUInt64LE(off)
3185
- : BigInt(buf.readUInt32LE(off));
3138
+ static _reader = (buf, off) => {
3139
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
3140
+ if (!ptr) return null;
3141
+ return ptr;
3142
+ };
3186
3143
  static _writer = (buf, off, val) => {
3187
3144
  const v = typeof val === "bigint" ? val : BigInt(val || 0);
3188
3145
  if (native.POINTER_SIZE === 8) {
@@ -3202,27 +3159,15 @@ const _longSize = native.sizeof ? native.sizeof("long") : 4;
3202
3159
  class c_long extends SimpleCData {
3203
3160
  static _size = _longSize;
3204
3161
  static _type = "long";
3205
- static _reader =
3206
- _longSize === 8
3207
- ? (buf, off) => buf.readBigInt64LE(off)
3208
- : (buf, off) => buf.readInt32LE(off);
3209
- static _writer =
3210
- _longSize === 8
3211
- ? (buf, off, val) => buf.writeBigInt64LE(BigInt(val), off)
3212
- : (buf, off, val) => buf.writeInt32LE(val, off);
3162
+ static _reader = _longSize === 8 ? (buf, off) => buf.readBigInt64LE(off) : (buf, off) => buf.readInt32LE(off);
3163
+ static _writer = _longSize === 8 ? (buf, off, val) => buf.writeBigInt64LE(BigInt(val), off) : (buf, off, val) => buf.writeInt32LE(val, off);
3213
3164
  }
3214
3165
 
3215
3166
  class c_ulong extends SimpleCData {
3216
3167
  static _size = _longSize;
3217
3168
  static _type = "ulong";
3218
- static _reader =
3219
- _longSize === 8
3220
- ? (buf, off) => buf.readBigUInt64LE(off)
3221
- : (buf, off) => buf.readUInt32LE(off);
3222
- static _writer =
3223
- _longSize === 8
3224
- ? (buf, off, val) => buf.writeBigUInt64LE(BigInt(val), off)
3225
- : (buf, off, val) => buf.writeUInt32LE(val, off);
3169
+ static _reader = _longSize === 8 ? (buf, off) => buf.readBigUInt64LE(off) : (buf, off) => buf.readUInt32LE(off);
3170
+ static _writer = _longSize === 8 ? (buf, off, val) => buf.writeBigUInt64LE(BigInt(val), off) : (buf, off, val) => buf.writeUInt32LE(val, off);
3226
3171
  }
3227
3172
 
3228
3173
  // ============================================================================
@@ -3232,11 +3177,15 @@ class c_ulong extends SimpleCData {
3232
3177
  class c_char_p extends SimpleCData {
3233
3178
  static _size = native.POINTER_SIZE;
3234
3179
  static _type = "char_p";
3235
- static _reader = (buf, off) =>
3236
- native.POINTER_SIZE === 8
3237
- ? buf.readBigUInt64LE(off)
3238
- : BigInt(buf.readUInt32LE(off));
3180
+ static _reader = (buf, off) => {
3181
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
3182
+ if (!ptr) return null;
3183
+ return readCString(ptr);
3184
+ };
3239
3185
  static _writer = (buf, off, val) => {
3186
+ if (typeof val === "string") {
3187
+ val = cstring(val);
3188
+ }
3240
3189
  if (Buffer.isBuffer(val)) {
3241
3190
  const addr = addressOf(val);
3242
3191
  const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
@@ -3259,11 +3208,15 @@ class c_char_p extends SimpleCData {
3259
3208
  class c_wchar_p extends SimpleCData {
3260
3209
  static _size = native.POINTER_SIZE;
3261
3210
  static _type = "wchar_p";
3262
- static _reader = (buf, off) =>
3263
- native.POINTER_SIZE === 8
3264
- ? buf.readBigUInt64LE(off)
3265
- : BigInt(buf.readUInt32LE(off));
3211
+ static _reader = (buf, off) => {
3212
+ const ptr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off));
3213
+ if (!ptr) return null;
3214
+ return readWString(ptr);
3215
+ };
3266
3216
  static _writer = (buf, off, val) => {
3217
+ if (typeof val === "string") {
3218
+ val = wstring(val);
3219
+ }
3267
3220
  if (Buffer.isBuffer(val)) {
3268
3221
  const addr = addressOf(val);
3269
3222
  const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
@@ -3319,9 +3272,7 @@ function create_string_buffer(init) {
3319
3272
  buf[init.length] = 0; // null terminator
3320
3273
  return buf;
3321
3274
  }
3322
- throw new TypeError(
3323
- "create_string_buffer requires number, string, or Buffer",
3324
- );
3275
+ throw new TypeError("create_string_buffer requires number, string, or Buffer");
3325
3276
  }
3326
3277
 
3327
3278
  /**