node-ctypes 0.1.7 → 1.0.0

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.

Potentially problematic release.


This version of node-ctypes might be problematic. Click here for more details.

package/lib/index.js CHANGED
@@ -3,12 +3,78 @@
3
3
  *
4
4
  * A simple FFI library that allows calling C functions from JavaScript,
5
5
  * similar to Python's ctypes module.
6
+ *
7
+ * @module node-ctypes
8
+ * @see {@link https://github.com/dmazzella/node-ctypes|GitHub Repository}
9
+ * @example
10
+ * import { CDLL, c_int, c_char_p } from 'node-ctypes';
11
+ *
12
+ * const libc = new CDLL(null); // Load C standard library
13
+ * const printf = libc.func('printf', c_int, [c_char_p]);
14
+ * printf('Hello from Node.js!\\n');
6
15
  */
7
16
 
8
17
  import { existsSync } from "node:fs";
9
18
  import { createRequire } from "node:module";
10
19
  import path from "node:path";
11
20
 
21
+ // Import refactored modules
22
+ import { LRUCache } from "./utils/cache.js";
23
+ import { _initConstants } from "./platform/constants.js";
24
+ import { _toNativeType, _toNativeTypes } from "./core/types.js";
25
+ import { callback as createCallback, threadSafeCallback as createThreadSafeCallback } from "./core/callback.js";
26
+ import { createLibraryClasses } from "./core/Library.js";
27
+ import {
28
+ alloc as _alloc,
29
+ cstring as _cstring,
30
+ wstring as _wstring,
31
+ create_string_buffer as _create_string_buffer,
32
+ create_unicode_buffer as _create_unicode_buffer,
33
+ string_at as _string_at,
34
+ wstring_at as _wstring_at,
35
+ memmove as _memmove,
36
+ memset as _memset,
37
+ } from "./memory/buffer.js";
38
+ import {
39
+ readValue as _readValue,
40
+ writeValue as _writeValue,
41
+ sizeof as _sizeof,
42
+ } from "./memory/operations.js";
43
+ import {
44
+ addressOf as _addressOf,
45
+ byref as _byref,
46
+ cast as _cast,
47
+ ptrToBuffer as _ptrToBuffer,
48
+ POINTER as _POINTER,
49
+ } from "./memory/pointer.js";
50
+ import {
51
+ get_errno as _get_errno,
52
+ set_errno as _set_errno,
53
+ GetLastError as _GetLastError,
54
+ SetLastError as _SetLastError,
55
+ FormatError as _FormatError,
56
+ WinError as _WinError,
57
+ } from "./platform/errors.js";
58
+ import {
59
+ bitfield as _bitfield,
60
+ _isStruct,
61
+ _isArrayType,
62
+ _isBitField,
63
+ } from "./structures/helpers/common.js";
64
+ import { array as _array } from "./structures/helpers/array.js";
65
+ import {
66
+ _readBitField as _readBitFieldHelper,
67
+ _writeBitField as _writeBitFieldHelper,
68
+ _readUintFromBuffer,
69
+ _writeUintToBuffer,
70
+ } from "./structures/helpers/bitfield.js";
71
+ import { union as _union } from "./structures/helpers/union.js";
72
+ import { struct as _struct } from "./structures/helpers/struct.js";
73
+ import { createStructureClass } from "./structures/Structure.js";
74
+ import { createUnionClass } from "./structures/Union.js";
75
+ import { createSimpleCDataClass } from "./types/SimpleCData.js";
76
+ import { createPrimitiveTypes } from "./types/primitives.js";
77
+
12
78
  /**
13
79
  * Get the path to the native module, checking multiple possible locations
14
80
  * @param {string} moduleName - The name of the npm module
@@ -59,54 +125,28 @@ let nativeModulePath;
59
125
  try {
60
126
  nativeModulePath = getNativeModulePath("node-ctypes", `ctypes.node`);
61
127
  } catch (e) {
62
- const platform = process.platform;
63
- const arch = process.arch;
64
- nativeModulePath = getNativeModulePath("node-ctypes", `ctypes-${platform}-${arch}.node`);
128
+ try {
129
+ const platform = process.platform;
130
+ const arch = process.arch;
131
+ nativeModulePath = getNativeModulePath("node-ctypes", `ctypes-${platform}-${arch}.node`);
132
+ } catch (fallbackError) {
133
+ throw new Error(`Failed to load native module: ${e.message}. Fallback also failed: ${fallbackError.message}`);
134
+ }
65
135
  }
66
136
  const require = createRequire(path.join(process.cwd(), "index.js"));
67
137
  const native = require(nativeModulePath);
68
138
 
139
+ // Initialize platform constants from native module
140
+ _initConstants(native);
141
+
69
142
  // Re-esporta le classi native
70
143
  const { Version, Library, FFIFunction, Callback, ThreadSafeCallback, CType, StructType, ArrayType } = native;
71
144
 
72
- // Tipi predefiniti
73
- const types = native.types;
74
-
75
145
  // ============================================================================
76
- // Type Normalization Helper
77
- // Converts SimpleCData classes to their string type names for FFI
146
+ // Type Normalization Helpers
147
+ // Now imported from ./core/types.js - see that file for implementation details
78
148
  // ============================================================================
79
149
 
80
- /**
81
- * Converte un tipo SimpleCData nel formato richiesto dal modulo nativo FFI
82
- * @param {Function} type - Classe SimpleCData
83
- * @returns {string|Object} Tipo per FFI nativo
84
- */
85
- function _toNativeType(type) {
86
- if (typeof type === "function" && type._isSimpleCData) {
87
- // String pointer types need native CType for automatic conversion
88
- if (type._type === "char_p") return types.c_char_p;
89
- if (type._type === "wchar_p") return types.c_wchar_p;
90
- return type._type;
91
- }
92
- // Struct/Union classes pass through
93
- if (typeof type === "function" && (type.prototype instanceof Structure || type.prototype instanceof Union)) {
94
- return type;
95
- }
96
- // Native CType objects pass through
97
- return type;
98
- }
99
-
100
- /**
101
- * Normalizza un array di tipi
102
- * @param {Array} types - Array di tipi
103
- * @returns {Array} Array di tipi normalizzati
104
- */
105
- function _toNativeTypes(types) {
106
- if (!Array.isArray(types)) return types;
107
- return types.map(_toNativeType);
108
- }
109
-
110
150
  /**
111
151
  * Carica una libreria dinamica
112
152
  * @param {string|null} libPath - Percorso della libreria o null per l'eseguibile corrente
@@ -116,713 +156,108 @@ function load(libPath) {
116
156
  return new Library(libPath);
117
157
  }
118
158
 
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
- }
159
+ // LRUCache is now imported from ./utils/cache.js
160
+ // See lib/utils/cache.js for implementation details
174
161
 
175
162
  /**
176
- * Wrapper conveniente per CDLL (come in Python ctypes)
163
+ * Wrapper per funzioni con sintassi Python-like ctypes (argtypes/restype)
177
164
  */
178
- class CDLL {
179
- constructor(libPath) {
180
- this._lib = new Library(libPath);
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
- });
194
- }
195
-
196
- /**
197
- * Ottiene una funzione dalla libreria
198
- * @param {string} name - Nome della funzione
199
- * @param {string|CType} returnType - Tipo di ritorno
200
- * @param {Array<string|CType>} argTypes - Tipi degli argomenti
201
- * @param {Object} [options] - Opzioni aggiuntive (es. { abi: 'stdcall' })
202
- * @returns {Function} Funzione callable
203
- */
204
- func(name, returnType, argTypes = [], options = {}) {
205
- const cacheKey = `${name}:${returnType}:${argTypes.join(",")}`;
206
-
207
- if (this._cache.has(cacheKey)) {
208
- return this._cache.get(cacheKey);
209
- }
210
-
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
- }
295
- } else {
296
- // Primitive value (number, string, bigint, null, undefined)
297
- processedArgs.push(arg);
298
- }
299
- }
300
-
301
- return ffiFunc.call(...processedArgs);
302
- };
303
- }
304
-
305
- // Aggiungi metadata come proprietà non-enumerable per non interferire
306
- Object.defineProperties(callMethod, {
307
- funcName: { value: name },
308
- address: { value: ffiFunc.address },
309
- _ffi: { value: ffiFunc },
310
- // Esponi errcheck come setter/getter
311
- errcheck: {
312
- get() {
313
- return ffiFunc._errcheck;
314
- },
315
- set(callback) {
316
- ffiFunc._errcheck = callback;
317
- ffiFunc.setErrcheck(callback);
318
- },
319
- enumerable: false,
320
- configurable: true,
321
- },
322
- });
323
-
324
- this._cache.set(cacheKey, callMethod);
325
- return callMethod;
326
- }
327
-
328
- /**
329
- * Ottiene l'indirizzo di un simbolo
330
- * @param {string} name - Nome del simbolo
331
- * @returns {BigInt} Indirizzo del simbolo
332
- */
333
- symbol(name) {
334
- return this._lib.symbol(name);
335
- }
336
-
337
- /**
338
- * Crea un callback JS chiamabile da C
339
- * @param {Function} fn - Funzione JavaScript
340
- * @param {string|CType} returnType - Tipo di ritorno
341
- * @param {Array<string|CType>} argTypes - Tipi degli argomenti
342
- * @returns {Object} Oggetto callback con proprietà pointer e metodi
343
- */
344
- callback(fn, returnType, argTypes = []) {
345
- return this._lib.callback(_toNativeType(returnType), _toNativeTypes(argTypes), fn);
346
- }
347
-
348
- /**
349
- * Chiude la libreria
350
- */
351
- close() {
352
- this._lib.close();
353
- this._cache.clear();
354
- }
355
-
356
- get path() {
357
- return this._lib.path;
358
- }
359
-
360
- get loaded() {
361
- return this._lib.loaded;
362
- }
363
- }
165
+ // Create Library classes (FunctionWrapper, CDLL, WinDLL)
166
+ const { FunctionWrapper, CDLL, WinDLL } = createLibraryClasses(
167
+ Library,
168
+ LRUCache,
169
+ _toNativeType,
170
+ _toNativeTypes,
171
+ native
172
+ );
364
173
 
365
- /**
366
- * WinDLL - come CDLL ma con stdcall di default (per Windows)
367
- */
368
- class WinDLL extends CDLL {
369
- func(name, returnType, argTypes = [], options = {}) {
370
- return super.func(name, returnType, argTypes, {
371
- abi: "stdcall",
372
- ...options,
373
- });
374
- }
375
- }
174
+ // ============================================================================
175
+ // Callback Functions
176
+ // Now imported from ./core/callback.js - see that file for implementation details
177
+ // ============================================================================
376
178
 
377
179
  /**
378
180
  * Crea un callback JS chiamabile da C (solo main thread)
379
- *
380
- * Questo callback è veloce e senza overhead, ma DEVE essere chiamato
381
- * solo dal main thread di Node.js (es: qsort, EnumWindows, etc.)
382
- *
383
- * Per callback da thread esterni (es: CreateThread), usa threadSafeCallback()
384
- *
385
- * @param {Function} fn - Funzione JavaScript
386
- * @param {string|CType} returnType - Tipo di ritorno
387
- * @param {Array<string|CType>} argTypes - Tipi degli argomenti
388
- * @returns {Object} Oggetto con .pointer (BigInt), .release()
181
+ * @see ./core/callback.js for full documentation
389
182
  */
390
183
  function callback(fn, returnType, argTypes = []) {
391
- const cb = new Callback(fn, _toNativeType(returnType), _toNativeTypes(argTypes));
392
-
393
- return {
394
- get pointer() {
395
- return cb.pointer;
396
- },
397
- release() {
398
- cb.release();
399
- },
400
- _callback: cb,
401
- };
184
+ return createCallback(fn, returnType, argTypes, native);
402
185
  }
403
186
 
404
187
  /**
405
188
  * Crea un callback JS chiamabile da qualsiasi thread
406
- *
407
- * Questo callback può essere invocato da thread C esterni (es: CreateThread,
408
- * thread pool, etc.) in modo sicuro. Ha overhead maggiore rispetto a callback()
409
- * per la sincronizzazione tra thread.
410
- *
411
- * Per callback dal main thread (es: qsort), preferisci callback() che è più veloce.
412
- *
413
- * @param {Function} fn - Funzione JavaScript
414
- * @param {string|CType} returnType - Tipo di ritorno
415
- * @param {Array<string|CType>} argTypes - Tipi degli argomenti
416
- * @returns {Object} Oggetto con .pointer (BigInt), .release()
189
+ * @see ./core/callback.js for full documentation
417
190
  */
418
191
  function threadSafeCallback(fn, returnType, argTypes = []) {
419
- const cb = new ThreadSafeCallback(fn, _toNativeType(returnType), _toNativeTypes(argTypes));
420
-
421
- return {
422
- get pointer() {
423
- return cb.pointer;
424
- },
425
- release() {
426
- cb.release();
427
- },
428
- _callback: cb,
429
- };
192
+ return createThreadSafeCallback(fn, returnType, argTypes, native);
430
193
  }
431
194
 
432
- /**
433
- * Alloca memoria nativa
434
- * @param {number} size - Dimensione in bytes
435
- * @returns {Buffer} Buffer allocato
436
- */
195
+ // ============================================================================
196
+ // Buffer Operations
197
+ // Now imported from ./memory/buffer.js - see that file for implementation details
198
+ // ============================================================================
199
+
437
200
  function alloc(size) {
438
- // Usa Buffer.alloc direttamente per performance (evita chiamata nativa)
439
- return Buffer.alloc(size);
201
+ return _alloc(size);
440
202
  }
441
203
 
442
- /**
443
- * Crea un buffer con una stringa C null-terminata
444
- * @param {string} str - Stringa JavaScript
445
- * @returns {Buffer} Buffer con stringa C
446
- */
447
204
  function cstring(str) {
448
- return native.cstring(str);
205
+ return _cstring(str, native);
449
206
  }
450
207
 
451
- /**
452
- * Crea un buffer con una stringa wide (wchar_t*) null-terminata
453
- * @param {string} str - Stringa JavaScript
454
- * @returns {Buffer} Buffer con stringa wide
455
- */
456
208
  function wstring(str) {
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
474
- return buf;
209
+ return _wstring(str, native);
475
210
  }
476
211
 
477
- /**
478
- * Legge una stringa C da un puntatore
479
- * @param {Buffer|BigInt|number} ptr - Puntatore alla stringa
480
- * @param {number} [maxLen] - Lunghezza massima da leggere
481
- * @returns {string|null} Stringa letta o null
482
- */
212
+ // Internal wrappers for backward compatibility (used by SimpleCData _reader/_writer)
483
213
  function readCString(ptr, maxLen) {
484
- return native.readCString(ptr, maxLen);
214
+ return _string_at(ptr, maxLen, native);
485
215
  }
486
216
 
487
- /**
488
- * Legge una stringa wide (wchar_t*) da un puntatore
489
- * @param {Buffer|BigInt|number} ptr - Puntatore alla stringa wide
490
- * @param {number} [size] - Numero di caratteri da leggere (non bytes)
491
- * @returns {string|null} Stringa letta o null
492
- */
493
217
  function readWString(ptr, size) {
494
- if (ptr === null || ptr === undefined) {
495
- return null;
496
- }
497
-
498
- const wcharSize = native.WCHAR_SIZE; // 2 su Windows, 4 su Unix
499
-
500
- let buf;
501
- if (Buffer.isBuffer(ptr)) {
502
- buf = ptr;
503
- } else {
504
- // Converti BigInt/number in buffer
505
- const maxBytes = size ? size * wcharSize : 1024;
506
- buf = native.ptrToBuffer(ptr, maxBytes);
507
- }
508
-
509
- // Trova il null terminator
510
- let end = size ? size * wcharSize : buf.length;
511
-
512
- if (wcharSize === 2) {
513
- // Windows: UTF-16LE
514
- for (let i = 0; i < end; i += 2) {
515
- if (i + 1 < buf.length && buf.readUInt16LE(i) === 0) {
516
- end = i;
517
- break;
518
- }
519
- }
520
- return buf.toString("utf16le", 0, end);
521
- } else {
522
- // Unix: UTF-32LE (wchar_t è 32-bit)
523
- let chars = [];
524
- for (let i = 0; i < end; i += 4) {
525
- if (i + 3 >= buf.length) break;
526
- const codePoint = buf.readUInt32LE(i);
527
- if (codePoint === 0) break;
528
- chars.push(String.fromCodePoint(codePoint));
529
- }
530
- return chars.join("");
531
- }
218
+ return _wstring_at(ptr, size, native);
532
219
  }
533
220
 
534
- /**
535
- * Legge un valore dalla memoria
536
- * Per tipi base comuni usa lookup table
537
- * @param {Buffer|BigInt|number} ptr - Puntatore
538
- * @param {string|CType} type - Tipo del valore
539
- * @param {number} [offset=0] - Offset in bytes
540
- * @returns {*} Valore letto
541
- */
542
- function readValue(ptr, type, offset = 0) {
543
- // SimpleCData class - use its _reader directly
544
- if (typeof type === "function" && type._isSimpleCData) {
545
- if (Buffer.isBuffer(ptr)) {
546
- return type._reader(ptr, offset);
547
- }
548
-
549
- // Allow passing an address (BigInt or number) and convert to buffer
550
- if (typeof ptr === "bigint" || typeof ptr === "number") {
551
- const size = type._size || sizeof(type);
552
- const buf = ptrToBuffer(ptr, size + offset);
553
- return type._reader(buf, offset);
554
- }
555
-
556
- throw new TypeError("readValue requires a Buffer or pointer address");
557
- }
558
-
559
- // Struct type (class)
560
- if (typeof type === "function" && type.prototype instanceof Structure) {
561
- const def = type._structDef || type._buildStruct();
562
- const tempBuf = ptr.subarray(offset, offset + def.size);
563
- return def.toObject(tempBuf);
564
- }
565
-
566
- // Union type (class)
567
- if (typeof type === "function" && type.prototype instanceof Union) {
568
- const def = type._unionDef || type._buildUnion();
569
- const tempBuf = ptr.subarray(offset, offset + def.size);
570
- return def.toObject(tempBuf);
571
- }
221
+ // ============================================================================
222
+ // Memory Operations
223
+ // Now imported from ./memory/operations.js - see that file for implementation details
224
+ // ============================================================================
572
225
 
573
- throw new TypeError(`readValue: unsupported type ${type}`);
226
+ function readValue(ptr, type, offset = 0) {
227
+ return _readValue(ptr, type, offset, sizeof, ptrToBuffer, Structure, Union);
574
228
  }
575
229
 
576
- /**
577
- * Scrive un valore in memoria
578
- * Per tipi base comuni usa lookup table
579
- * @param {Buffer|BigInt|number} ptr - Puntatore
580
- * @param {string|CType} type - Tipo del valore
581
- * @param {*} value - Valore da scrivere
582
- * @param {number} [offset=0] - Offset in bytes
583
- * @returns {number} Bytes scritti
584
- */
585
230
  function writeValue(ptr, type, value, offset = 0) {
586
- // SimpleCData class - use its _writer directly
587
- if (typeof type === "function" && type._isSimpleCData) {
588
- if (!Buffer.isBuffer(ptr)) {
589
- throw new TypeError("writeValue requires a Buffer");
590
- }
591
- type._writer(ptr, offset, value);
592
- return type._size;
593
- }
594
-
595
- // Struct type (class)
596
- if (typeof type === "function" && type.prototype instanceof Structure) {
597
- const def = type._structDef || type._buildStruct();
598
- const tempBuf = ptr.subarray(offset, offset + def.size);
599
- if (Buffer.isBuffer(value)) {
600
- value.copy(tempBuf, 0, 0, def.size);
601
- } else if (typeof value === "object" && value !== null) {
602
- for (const [k, v] of Object.entries(value)) {
603
- def.set(tempBuf, k, v);
604
- }
605
- }
606
- return def.size;
607
- }
608
-
609
- // Union type (class)
610
- if (typeof type === "function" && type.prototype instanceof Union) {
611
- const def = type._unionDef || type._buildUnion();
612
- const tempBuf = ptr.subarray(offset, offset + def.size);
613
- if (Buffer.isBuffer(value)) {
614
- value.copy(tempBuf, 0, 0, def.size);
615
- } else if (typeof value === "object" && value !== null) {
616
- for (const [k, v] of Object.entries(value)) {
617
- def.set(tempBuf, k, v);
618
- }
619
- }
620
- return def.size;
621
- }
622
-
623
- throw new TypeError(`writeValue: unsupported type ${type}`);
231
+ return _writeValue(ptr, type, value, offset, sizeof, Structure, Union);
624
232
  }
625
233
 
626
- /**
627
- * Restituisce la dimensione di un tipo
628
- * Per tipi base comuni usa lookup table
629
- * @param {string|CType|Object} type - Tipo
630
- * @returns {number} Dimensione in bytes
631
- */
632
234
  function sizeof(type) {
633
- // SimpleCData class (e.g., sizeof(c_uint64))
634
- if (typeof type === "function" && type._isSimpleCData) {
635
- return type._size;
636
- }
637
-
638
- // SimpleCData instance (e.g., sizeof(new c_uint64()))
639
- if (type && type._buffer && type.constructor && type.constructor._isSimpleCData) {
640
- return type.constructor._size;
641
- }
642
-
643
- // Struct type (class)
644
- if (typeof type === "function" && type.prototype instanceof Structure) {
645
- const def = type._structDef || type._buildStruct();
646
- return def.size;
647
- }
648
-
649
- // Union type (class)
650
- if (typeof type === "function" && type.prototype instanceof Union) {
651
- const def = type._unionDef || type._buildUnion();
652
- return def.size;
653
- }
654
-
655
- // Struct type
656
- if (typeof type === "object" && type !== null && typeof type.size === "number") {
657
- return type.size;
658
- }
659
-
660
- // Array type
661
- if (typeof type === "object" && type !== null && typeof type.getSize === "function") {
662
- return type.getSize();
663
- }
664
-
665
- throw new TypeError(`sizeof: unsupported type. Use SimpleCData classes like c_int32, c_uint64, etc.`);
235
+ return _sizeof(type, Structure, Union);
666
236
  }
667
237
 
668
- /**
669
- * Converte un Buffer o indirizzo in BigInt
670
- * @param {Buffer|BigInt|number} ptr - Puntatore
671
- * @returns {BigInt} Indirizzo come BigInt
672
- */
238
+ // ============================================================================
239
+ // Pointer Operations
240
+ // Now imported from ./memory/pointer.js - see that file for implementation details
241
+ // ============================================================================
242
+
673
243
  function addressOf(ptr) {
674
- if (Buffer.isBuffer(ptr)) {
675
- // Ottieni l'indirizzo del buffer
676
- // Questo è un po' hacky, ma funziona
677
- const tempBuf = alloc(native.POINTER_SIZE);
678
- // Use native.writeValue with a string 'pointer' to avoid JS-level
679
- // writeValue -> c_void_p._writer recursion. native.writeValue accepts
680
- // a string type name and will write the buffer address directly.
681
- native.writeValue(tempBuf, "pointer", ptr, 0);
682
- if (native.POINTER_SIZE === 8) return tempBuf.readBigUInt64LE(0);
683
- return BigInt(tempBuf.readUInt32LE(0));
684
- }
685
- if (typeof ptr === "bigint") {
686
- return ptr;
687
- }
688
- return BigInt(ptr);
244
+ return _addressOf(ptr, alloc, native);
689
245
  }
690
246
 
691
- /**
692
- * Passa un oggetto per riferimento (come Python byref)
693
- * Supporta sia Buffer che istanze SimpleCData
694
- *
695
- * @param {Buffer|SimpleCData} obj - Oggetto da passare per riferimento
696
- * @returns {Buffer} Il buffer sottostante
697
- */
698
247
  function byref(obj) {
699
- // SimpleCData instance - return its buffer
700
- if (obj && obj._buffer && Buffer.isBuffer(obj._buffer)) {
701
- return obj._buffer;
702
- }
703
- // Buffer - return as-is
704
- if (Buffer.isBuffer(obj)) {
705
- return obj;
706
- }
707
- throw new TypeError("byref() argument must be a ctypes instance or Buffer");
248
+ return _byref(obj);
708
249
  }
709
250
 
710
- /**
711
- * Cast di un puntatore a un tipo diverso
712
- * Legge il valore dalla memoria interpretandolo come il nuovo tipo.
713
- *
714
- * @param {Buffer|BigInt} ptr - Puntatore sorgente
715
- * @param {string|Object} targetType - Tipo destinazione ('int32', 'pointer', struct, etc.)
716
- * @returns {*} Valore letto con il nuovo tipo
717
- */
718
251
  function cast(ptr, targetType) {
719
- // Se è un tipo struct
720
- if (typeof targetType === "object" && targetType.size !== undefined) {
721
- // È una struct, restituisci un oggetto che permette di leggere i campi
722
- const buf = Buffer.isBuffer(ptr) ? ptr : ptrToBuffer(ptr, targetType.size);
723
- return {
724
- _buffer: buf,
725
- _struct: targetType,
726
- get contents() {
727
- return targetType.toObject(buf);
728
- },
729
- getField(name) {
730
- return targetType.get(buf, name);
731
- },
732
- setField(name, value) {
733
- targetType.set(buf, name, value);
734
- },
735
- };
736
- }
737
-
738
- // Se è un tipo base, leggi il valore
739
- if (Buffer.isBuffer(ptr)) {
740
- return readValue(ptr, targetType, 0);
741
- }
742
-
743
- // È un BigInt/number, converti in buffer temporaneo e leggi
744
- const tempBuf = ptrToBuffer(ptr, sizeof(targetType));
745
- return readValue(tempBuf, targetType, 0);
252
+ return _cast(ptr, targetType, readValue, sizeof, ptrToBuffer);
746
253
  }
747
254
 
748
- /**
749
- * Crea un buffer che punta a un indirizzo di memoria
750
- * ATTENZIONE: Usare con cautela! Accesso a memoria non valida causa crash.
751
- *
752
- * @param {BigInt|number} address - Indirizzo di memoria
753
- * @param {number} size - Dimensione del buffer
754
- * @returns {Buffer} Buffer che punta all'indirizzo
755
- */
756
255
  function ptrToBuffer(address, size) {
757
- return native.ptrToBuffer(address, size);
256
+ return _ptrToBuffer(address, size, native);
758
257
  }
759
258
 
760
- /**
761
- * Crea un tipo POINTER(type) - puntatore a un tipo specifico
762
- *
763
- * @param {string|Object} baseType - Tipo base
764
- * @returns {Object} Tipo puntatore
765
- */
766
259
  function POINTER(baseType) {
767
- const baseSize = typeof baseType === "object" ? baseType.size : sizeof(baseType);
768
- const typeName = typeof baseType === "object" ? "struct" : baseType;
769
-
770
- return {
771
- _pointerTo: baseType,
772
- _baseSize: baseSize,
773
- size: native.POINTER_SIZE,
774
-
775
- /**
776
- * Crea un puntatore NULL
777
- */
778
- create() {
779
- const buf = alloc(native.POINTER_SIZE);
780
- writeValue(buf, c_void_p, 0n);
781
- return buf;
782
- },
783
-
784
- /**
785
- * Crea un puntatore a un buffer esistente
786
- */
787
- fromBuffer(targetBuf) {
788
- const buf = alloc(native.POINTER_SIZE);
789
- writeValue(buf, c_void_p, targetBuf);
790
- return buf;
791
- },
792
-
793
- /**
794
- * Legge il puntatore (dereferenzia)
795
- */
796
- deref(ptrBuf) {
797
- const addr = readValue(ptrBuf, c_void_p);
798
- if (addr === 0n || addr === null) {
799
- return null;
800
- }
801
- // Per struct, restituisci wrapper
802
- if (typeof baseType === "object" && baseType.toObject) {
803
- // Non possiamo creare buffer a indirizzo arbitrario senza native
804
- // Restituiamo l'indirizzo
805
- return addr;
806
- }
807
- // Per tipi base, non possiamo leggere senza native.ptrToBuffer
808
- return addr;
809
- },
810
-
811
- /**
812
- * Scrive un indirizzo nel puntatore
813
- */
814
- set(ptrBuf, value) {
815
- if (Buffer.isBuffer(value)) {
816
- writeValue(ptrBuf, c_void_p, value);
817
- } else {
818
- writeValue(ptrBuf, c_void_p, value);
819
- }
820
- },
821
-
822
- toString() {
823
- return `POINTER(${typeName})`;
824
- },
825
- };
260
+ return _POINTER(baseType, sizeof, alloc, readValue, writeValue, c_void_p, native);
826
261
  }
827
262
 
828
263
  /**
@@ -832,382 +267,7 @@ function POINTER(baseType) {
832
267
  * @returns {Object} Definizione della union
833
268
  */
834
269
  function union(fields) {
835
- let maxSize = 0;
836
- let maxAlignment = 1;
837
- const fieldDefs = [];
838
-
839
- for (const [name, type] of Object.entries(fields)) {
840
- let size, alignment;
841
- let fieldDef = { name, type, offset: 0 };
842
-
843
- // Nested struct (object form)
844
- if (_isStruct(type)) {
845
- size = type.size;
846
- alignment = type.alignment;
847
- fieldDef.isNested = true;
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
- }
865
- // Array type
866
- else if (_isArrayType(type)) {
867
- size = type.getSize();
868
- const elemSize = sizeof(type.elementType);
869
- alignment = Math.min(elemSize, native.POINTER_SIZE);
870
- fieldDef.isArray = true;
871
- }
872
- // Bit field - in union ogni bitfield occupa l'intero baseType
873
- else if (_isBitField(type)) {
874
- size = type.baseSize;
875
- alignment = Math.min(size, native.POINTER_SIZE);
876
- fieldDef.isBitField = true;
877
- fieldDef.bitOffset = 0;
878
- fieldDef.bitSize = type.bits;
879
- fieldDef.baseSize = type.baseSize;
880
- fieldDef.type = type.baseType; // Usa il tipo base per lettura/scrittura
881
- }
882
- // Tipo base (SimpleCData)
883
- else {
884
- // Validate type is a SimpleCData class
885
- if (!(typeof type === "function" && type._isSimpleCData)) {
886
- throw new TypeError(`union field "${name}": type must be a SimpleCData class, struct, union, or array`);
887
- }
888
- size = type._size;
889
- alignment = Math.min(size, native.POINTER_SIZE);
890
- fieldDef.type = type;
891
- }
892
-
893
- fieldDef.size = size;
894
- fieldDef.alignment = alignment;
895
- fieldDefs.push(fieldDef);
896
-
897
- if (size > maxSize) {
898
- maxSize = size;
899
- }
900
- if (alignment > maxAlignment) {
901
- maxAlignment = alignment;
902
- }
903
- }
904
-
905
- // Padding finale per allineamento
906
- if (maxSize % maxAlignment !== 0) {
907
- maxSize += maxAlignment - (maxSize % maxAlignment);
908
- }
909
-
910
- // Crea Map per lookup O(1)
911
- const fieldMap = new Map();
912
- for (const field of fieldDefs) {
913
- fieldMap.set(field.name, field);
914
- }
915
-
916
- const unionDef = {
917
- size: maxSize,
918
- alignment: maxAlignment,
919
- fields: fieldDefs,
920
- isUnion: true,
921
- _isStructType: true, // Per compatibilità con _isStruct()
922
-
923
- create(values = {}) {
924
- const buf = alloc(maxSize);
925
- buf.fill(0);
926
-
927
- // Prima imposta i campi diretti
928
- for (const field of fieldDefs) {
929
- if (values[field.name] !== undefined) {
930
- unionDef.set(buf, field.name, values[field.name]);
931
- }
932
- }
933
-
934
- // Proxy-based instance (single Proxy instead of N defineProperty calls)
935
- return new Proxy(buf, {
936
- get(target, prop, receiver) {
937
- // Special properties
938
- if (prop === "_buffer") return target;
939
- if (prop === "toObject") return () => unionDef.toObject(target);
940
- if (prop === Symbol.toStringTag) return "UnionInstance";
941
- if (prop === Symbol.iterator) return undefined;
942
-
943
- // Handle Buffer/TypedArray properties FIRST before field checks
944
- if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
945
- return target[prop];
946
- }
947
-
948
- // Check if it's a known field
949
- if (typeof prop === "string" && fieldMap.has(prop)) {
950
- return unionDef.get(target, prop);
951
- }
952
-
953
- // Fallback to buffer properties and methods
954
- const value = target[prop];
955
- if (typeof value === "function") {
956
- return value.bind(target);
957
- }
958
- return value;
959
- },
960
- set(target, prop, value, receiver) {
961
- // Check if it's a known field
962
- if (typeof prop === "string" && fieldMap.has(prop)) {
963
- unionDef.set(target, prop, value);
964
- return true;
965
- }
966
-
967
- // Fallback
968
- return Reflect.set(target, prop, value, receiver);
969
- },
970
- has(target, prop) {
971
- if (prop === "_buffer" || prop === "toObject") return true;
972
- if (typeof prop === "string" && fieldMap.has(prop)) return true;
973
- return Reflect.has(target, prop);
974
- },
975
- ownKeys(target) {
976
- return fieldDefs.map((f) => f.name);
977
- },
978
- getOwnPropertyDescriptor(target, prop) {
979
- if (typeof prop === "string" && (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")) {
980
- return {
981
- enumerable: prop !== "_buffer" && prop !== "toObject",
982
- configurable: true,
983
- writable: true,
984
- };
985
- }
986
- return Reflect.getOwnPropertyDescriptor(target, prop);
987
- },
988
- });
989
- },
990
-
991
- get(bufOrObj, fieldName) {
992
- // NUOVO: supporta sia buffer che object wrapper
993
- let buf;
994
- if (Buffer.isBuffer(bufOrObj)) {
995
- buf = bufOrObj;
996
- } else if (bufOrObj && bufOrObj._buffer) {
997
- // Object wrapper - accesso diretto tramite property
998
- return bufOrObj[fieldName];
999
- } else {
1000
- throw new TypeError("Expected Buffer or union instance");
1001
- }
1002
-
1003
- // O(1) lookup con Map
1004
- const field = fieldMap.get(fieldName);
1005
- if (!field) throw new Error(`Unknown field: ${fieldName}`);
1006
-
1007
- // Bit field
1008
- if (field.isBitField) {
1009
- return _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
1010
- }
1011
-
1012
- // Nested struct
1013
- if (field.isNested) {
1014
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
1015
- // Proxy-based nested instance
1016
- return new Proxy(nestedBuf, {
1017
- get(target, prop, receiver) {
1018
- if (prop === "_buffer") return target;
1019
- if (prop === "toObject") return () => field.type.toObject(target);
1020
- if (prop === Symbol.toStringTag) return "NestedUnionInstance";
1021
- if (prop === Symbol.iterator) return undefined;
1022
- // Handle Buffer/TypedArray properties
1023
- if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
1024
- return target[prop];
1025
- }
1026
- if (typeof prop === "string" && field.type.fields) {
1027
- if (field.type.fields.some((f) => f.name === prop && !f.isAnonymous)) {
1028
- return field.type.get(target, prop);
1029
- }
1030
- }
1031
- const value = target[prop];
1032
- if (typeof value === "function") return value.bind(target);
1033
- return value;
1034
- },
1035
- set(target, prop, value, receiver) {
1036
- if (typeof prop === "string" && field.type.fields) {
1037
- if (field.type.fields.some((f) => f.name === prop && !f.isAnonymous)) {
1038
- field.type.set(target, prop, value);
1039
- return true;
1040
- }
1041
- }
1042
- return Reflect.set(target, prop, value, receiver);
1043
- },
1044
- has(target, prop) {
1045
- if (prop === "_buffer" || prop === "toObject") return true;
1046
- if (typeof prop === "string" && field.type.fields) {
1047
- if (field.type.fields.some((f) => f.name === prop)) return true;
1048
- }
1049
- return Reflect.has(target, prop);
1050
- },
1051
- ownKeys(target) {
1052
- return (field.type.fields || []).filter((f) => !f.isAnonymous).map((f) => f.name);
1053
- },
1054
- getOwnPropertyDescriptor(target, prop) {
1055
- if (typeof prop === "string" && field.type.fields && field.type.fields.some((f) => f.name === prop)) {
1056
- return {
1057
- enumerable: true,
1058
- configurable: true,
1059
- writable: true,
1060
- };
1061
- }
1062
- return Reflect.getOwnPropertyDescriptor(target, prop);
1063
- },
1064
- });
1065
- }
1066
-
1067
- // Array
1068
- if (field.isArray) {
1069
- return field.type.wrap(buf.subarray(field.offset, field.offset + field.size));
1070
- }
1071
-
1072
- // Tipo base
1073
- return readValue(buf, field.type, field.offset);
1074
- },
1075
-
1076
- set(bufOrObj, fieldName, value) {
1077
- // NUOVO: supporta sia buffer che object wrapper
1078
- let buf;
1079
- if (Buffer.isBuffer(bufOrObj)) {
1080
- buf = bufOrObj;
1081
- } else if (bufOrObj && bufOrObj._buffer) {
1082
- // Object wrapper - accesso diretto tramite property
1083
- bufOrObj[fieldName] = value;
1084
- return;
1085
- } else {
1086
- throw new TypeError("Expected Buffer or union instance");
1087
- }
1088
-
1089
- // Lookup with Map
1090
- const field = fieldMap.get(fieldName);
1091
- if (!field) throw new Error(`Unknown field: ${fieldName}`);
1092
-
1093
- // Bit field
1094
- if (field.isBitField) {
1095
- _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
1096
- return;
1097
- }
1098
-
1099
- // Nested struct
1100
- if (field.isNested) {
1101
- if (Buffer.isBuffer(value)) {
1102
- value.copy(buf, field.offset, 0, field.size);
1103
- } else if (typeof value === "object") {
1104
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
1105
- for (const [k, v] of Object.entries(value)) {
1106
- field.type.set(nestedBuf, k, v);
1107
- }
1108
- }
1109
- return;
1110
- }
1111
-
1112
- // Array
1113
- if (field.isArray) {
1114
- if (Buffer.isBuffer(value)) {
1115
- value.copy(buf, field.offset, 0, field.size);
1116
- } else if (Array.isArray(value)) {
1117
- const wrapped = field.type.wrap(buf.subarray(field.offset, field.offset + field.size));
1118
- for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
1119
- wrapped[i] = value[i];
1120
- }
1121
- } else if (value && value._buffer && Buffer.isBuffer(value._buffer)) {
1122
- // Handle array proxy instances
1123
- value._buffer.copy(buf, field.offset, 0, field.size);
1124
- }
1125
- return;
1126
- }
1127
-
1128
- // Tipo base
1129
- writeValue(buf, field.type, value, field.offset);
1130
- },
1131
-
1132
- toObject(bufOrObj) {
1133
- // Se è già un object (non buffer), ritorna così com'è
1134
- if (!Buffer.isBuffer(bufOrObj)) {
1135
- if (bufOrObj && bufOrObj._buffer && bufOrObj._buffer.length === maxSize) {
1136
- return bufOrObj; // Già convertito
1137
- }
1138
- // Se è un plain object, convertilo in union
1139
- if (typeof bufOrObj === "object") {
1140
- const buf = alloc(maxSize);
1141
- buf.fill(0);
1142
- for (const [name, value] of Object.entries(bufOrObj)) {
1143
- unionDef.set(buf, name, value);
1144
- }
1145
- return unionDef.toObject(buf);
1146
- }
1147
- throw new TypeError("Expected Buffer or union instance");
1148
- }
1149
-
1150
- const buf = bufOrObj;
1151
- const obj = {};
1152
-
1153
- // Cache per oggetti nested (struct/union) per evitare di ricrearli ogni volta
1154
- const nestedCache = new Map();
1155
-
1156
- // Aggiungi _buffer come property nascosta
1157
- Object.defineProperty(obj, "_buffer", {
1158
- value: buf,
1159
- writable: false,
1160
- enumerable: false,
1161
- configurable: false,
1162
- });
1163
-
1164
- // Per union, tutte le properties leggono/scrivono all'offset 0
1165
- for (const field of fieldDefs) {
1166
- Object.defineProperty(obj, field.name, {
1167
- get() {
1168
- if (field.isBitField) {
1169
- return _readBitField(buf, 0, field.type, field.bitOffset, field.bitSize);
1170
- } else if (field.isNested) {
1171
- // Cache nested objects per evitare di ricrearli ogni volta
1172
- if (!nestedCache.has(field.name)) {
1173
- const nestedBuf = buf.subarray(0, field.size);
1174
- const nestedObj = field.type.toObject(nestedBuf);
1175
- nestedCache.set(field.name, nestedObj);
1176
- }
1177
- return nestedCache.get(field.name);
1178
- } else if (field.isArray) {
1179
- const wrapped = field.type.wrap(buf.subarray(0, field.size));
1180
- return [...wrapped];
1181
- } else {
1182
- return readValue(buf, field.type, 0);
1183
- }
1184
- },
1185
- set(value) {
1186
- if (field.isBitField) {
1187
- _writeBitField(buf, 0, field.type, field.bitOffset, field.bitSize, value);
1188
- } else {
1189
- writeValue(buf, field.type, value, 0);
1190
- }
1191
- },
1192
- enumerable: true,
1193
- configurable: false,
1194
- });
1195
- }
1196
-
1197
- return obj;
1198
- },
1199
-
1200
- // fromObject: write plain object values into buffer
1201
- fromObject: function (buf, obj) {
1202
- for (const [key, value] of Object.entries(obj)) {
1203
- if (this.fields.some((f) => f.name === key)) {
1204
- this.set(buf, key, value);
1205
- }
1206
- }
1207
- },
1208
- };
1209
-
1210
- return unionDef;
270
+ return _union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _isArrayType, _isBitField, Structure, Union, native);
1211
271
  }
1212
272
 
1213
273
  /**
@@ -1233,10 +293,8 @@ function defineUnion(fields) {
1233
293
  AutoUnion._fields_ = fields;
1234
294
  return AutoUnion;
1235
295
  }
1236
-
1237
296
  // --------------------------------------------------------------------------
1238
- // Python-compatible class wrappers to allow `class X extends Structure` and
1239
- // `class Y extends Union` patterns similar to Python ctypes.
297
+ // Structure and Union base classes (created via factory functions)
1240
298
  // These classes read static properties on the subclass:
1241
299
  // - `_fields_`: array of [name, type] or object map { name: type }
1242
300
  // - `_anonymous_`: optional array of anonymous field names (for structs)
@@ -1245,651 +303,62 @@ function defineUnion(fields) {
1245
303
  // `_buffer` when an instance is created via `new`.
1246
304
  // --------------------------------------------------------------------------
1247
305
 
1248
- class Structure {
1249
- constructor(...args) {
1250
- const Ctor = this.constructor;
1251
- const def = Ctor._structDef || Ctor._buildStruct();
1252
- // Allocate buffer for instance
1253
- let buf = alloc(def.size);
1254
- buf.fill(0);
1255
-
1256
- // Support positional args: new Point(10,20)
1257
- if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0]) && !Buffer.isBuffer(args[0])) {
1258
- const initial = args[0];
1259
- for (const [k, v] of Object.entries(initial)) {
1260
- def.set(buf, k, v);
1261
- }
1262
- } else if (args.length === 1 && Buffer.isBuffer(args[0])) {
1263
- // new Point(buffer) - wrap existing buffer
1264
- buf = args[0];
1265
- } else if (args.length > 0) {
1266
- // Positional mapping to non-anonymous declared fields order
1267
- const ordered = def.fields.filter((f) => !f.isAnonymous);
1268
- for (let i = 0; i < Math.min(args.length, ordered.length); i++) {
1269
- def.set(buf, ordered[i].name, args[i]);
1270
- }
1271
- }
1272
-
1273
- // Store internal references on this (needed for methods like toObject)
1274
- this.__buffer = buf;
1275
- this.__structDef = def;
1276
-
1277
- // Build field map for O(1) lookup
1278
- const fieldMap = new Map();
1279
- const anonFieldNames = new Set();
1280
- for (const field of def.fields) {
1281
- fieldMap.set(field.name, field);
1282
- if (field.isAnonymous && field.type && Array.isArray(field.type.fields)) {
1283
- for (const subField of field.type.fields) {
1284
- anonFieldNames.add(subField.name);
1285
- }
1286
- }
1287
- }
306
+ // Create Structure and Union classes with injected dependencies
307
+ const Structure = createStructureClass(alloc, struct);
308
+ const Union = createUnionClass(Structure, union);
1288
309
 
1289
- // Return Proxy instead of using Object.defineProperty for each field
1290
- return new Proxy(this, {
1291
- get(target, prop, receiver) {
1292
- // Special properties that exist on the instance
1293
- if (prop === "_buffer") return buf;
1294
- if (prop === "_structDef") return def;
1295
- if (prop === "__buffer") return buf;
1296
- if (prop === "__structDef") return def;
1297
- if (prop === Symbol.toStringTag) return Ctor.name || "Structure";
1298
- if (prop === Symbol.iterator) return undefined;
1299
-
1300
- // Methods defined on the prototype
1301
- if (typeof target[prop] === "function") {
1302
- return target[prop].bind(target);
1303
- }
1304
-
1305
- // Check if it's a known field (O(1) lookup)
1306
- if (typeof prop === "string" && fieldMap.has(prop)) {
1307
- return def.get(buf, prop);
1308
- }
1309
-
1310
- // Check anonymous fields
1311
- if (typeof prop === "string" && anonFieldNames.has(prop)) {
1312
- return def.get(buf, prop);
1313
- }
1314
-
1315
- // Fallback to target properties
1316
- return Reflect.get(target, prop, receiver);
1317
- },
1318
- set(target, prop, value, receiver) {
1319
- // Check if it's a known field (O(1) lookup)
1320
- if (typeof prop === "string" && fieldMap.has(prop)) {
1321
- def.set(buf, prop, value);
1322
- return true;
1323
- }
1324
-
1325
- // Check anonymous fields
1326
- if (typeof prop === "string" && anonFieldNames.has(prop)) {
1327
- def.set(buf, prop, value);
1328
- return true;
1329
- }
1330
-
1331
- // Fallback
1332
- return Reflect.set(target, prop, value, receiver);
1333
- },
1334
- has(target, prop) {
1335
- if (prop === "_buffer" || prop === "_structDef" || prop === "toObject") return true;
1336
- if (typeof prop === "string" && fieldMap.has(prop)) return true;
1337
- if (typeof prop === "string" && anonFieldNames.has(prop)) return true;
1338
- return Reflect.has(target, prop);
1339
- },
1340
- ownKeys(target) {
1341
- const keys = [];
1342
- for (const f of def.fields) {
1343
- if (f.isAnonymous && f.type && Array.isArray(f.type.fields)) {
1344
- for (const sf of f.type.fields) {
1345
- keys.push(sf.name);
1346
- }
1347
- } else {
1348
- keys.push(f.name);
1349
- }
1350
- }
1351
- return keys;
1352
- },
1353
- getOwnPropertyDescriptor(target, prop) {
1354
- if (typeof prop === "string" && (fieldMap.has(prop) || anonFieldNames.has(prop))) {
1355
- return {
1356
- enumerable: true,
1357
- configurable: true,
1358
- writable: true,
1359
- };
1360
- }
1361
- if (prop === "_buffer" || prop === "_structDef") {
1362
- return {
1363
- enumerable: false,
1364
- configurable: true,
1365
- writable: true,
1366
- };
1367
- }
1368
- return Reflect.getOwnPropertyDescriptor(target, prop);
1369
- },
1370
- });
1371
- }
1372
310
 
1373
- // Build the underlying struct definition and cache it on the constructor
1374
- static _buildStruct() {
1375
- // Accept either array _fields_ (Python style) or object map
1376
- const fields = this._fields_ || {};
1377
- let mapFields = {};
1378
-
1379
- if (Array.isArray(fields)) {
1380
- for (const entry of fields) {
1381
- if (Array.isArray(entry) && entry.length >= 2) {
1382
- mapFields[entry[0]] = entry[1];
1383
- }
1384
- }
1385
- } else if (typeof fields === "object" && fields !== null) {
1386
- mapFields = { ...fields };
1387
- }
311
+ /**
312
+ * Helper per creare array a dimensione fissa (come c_int * 5 in Python)
313
+ * Ritorna un ArrayType wrapper con supporto per indexing arr[i]
314
+ *
315
+ * @param {string} elementType - Tipo degli elementi ('int32', 'float', etc.)
316
+ * @param {number} count - Numero di elementi
317
+ * @returns {Object} ArrayType con metodi getSize(), getLength(), create(), wrap()
318
+ *
319
+ * @example
320
+ * const IntArray5 = array('int32', 5);
321
+ * const arr = IntArray5.create([1, 2, 3, 4, 5]);
322
+ * console.log(arr[0]); // 1 - indexing Python-like!
323
+ * arr[2] = 42;
324
+ * console.log(arr[2]); // 42
325
+ */
326
+ function array(elementType, count) {
327
+ return _array(elementType, count, sizeof, alloc, readValue, writeValue, Structure, Union, ArrayType);
328
+ }
1388
329
 
1389
- // Handle anonymous fields list: convert to { anonymous: true, type: T }
1390
- if (Array.isArray(this._anonymous_)) {
1391
- for (const anonName of this._anonymous_) {
1392
- if (mapFields[anonName] !== undefined) {
1393
- mapFields[anonName] = {
1394
- anonymous: true,
1395
- type: mapFields[anonName],
1396
- };
1397
- }
1398
- }
1399
- }
330
+ // ============================================================================
331
+ // Error Handling
332
+ // ============================================================================
333
+ // ============================================================================
1400
334
 
1401
- const options = {};
1402
- if (this._pack_ !== undefined) options.packed = !!this._pack_;
335
+ // ============================================================================
336
+ // Error Handling
337
+ // Now imported from ./platform/errors.js - see that file for implementation details
338
+ // ============================================================================
1403
339
 
1404
- const def = struct(mapFields, options);
1405
- this._structDef = def;
1406
- return def;
1407
- }
340
+ function get_errno() {
341
+ return _get_errno(CDLL, alloc, readValue, c_int32, c_void_p);
342
+ }
1408
343
 
1409
- // Create returns an instance of the JS class (not a plain object)
1410
- static create(values = {}) {
1411
- const def = this._structDef || this._buildStruct();
1412
- // If a Buffer provided, wrap it
1413
- if (Buffer.isBuffer(values)) {
1414
- const inst = new this(values);
1415
- return inst;
1416
- }
1417
- // If values is instance of this class, return it
1418
- if (values && values._buffer && values._structDef === def) {
1419
- return values;
1420
- }
1421
- return new this(values);
1422
- }
344
+ function set_errno(value) {
345
+ return _set_errno(value, CDLL, writeValue, c_int32, c_void_p);
346
+ }
1423
347
 
1424
- // Create raw plain object without synchronization (for performance)
1425
- static createRaw(values = {}) {
1426
- const def = this._structDef || this._buildStruct();
1427
- return def.toObject(def.create(values)._buffer);
1428
- }
348
+ function GetLastError() {
349
+ return _GetLastError(WinDLL, c_uint32, c_void, c_void_p);
350
+ }
1429
351
 
1430
- // Synchronize plain object into instance buffer
1431
- syncFromObject(obj) {
1432
- const plain = this.__structDef.toObject(this.__buffer);
1433
- Object.assign(plain, obj);
1434
- this.__structDef.fromObject(this.__buffer, plain);
1435
- }
352
+ function SetLastError(code) {
353
+ return _SetLastError(code, WinDLL, c_uint32, c_void, c_void_p);
354
+ }
1436
355
 
1437
- // toObject: accept Buffer, instance or plain buffer
1438
- static toObject(bufOrInst) {
1439
- const def = this._structDef || this._buildStruct();
1440
- if (bufOrInst && bufOrInst._buffer) {
1441
- return def.toObject(bufOrInst._buffer);
1442
- }
1443
- return def.toObject(bufOrInst);
1444
- }
1445
-
1446
- // fromObject: write plain object into buffer
1447
- static fromObject(bufOrInst, obj) {
1448
- const def = this._structDef || this._buildStruct();
1449
- const buf = bufOrInst && bufOrInst._buffer ? bufOrInst._buffer : bufOrInst;
1450
- return def.fromObject(buf, obj);
1451
- }
1452
-
1453
- // Instance convenience
1454
- get(fieldName) {
1455
- return this.__structDef.get(this.__buffer, fieldName);
1456
- }
1457
-
1458
- set(fieldName, value) {
1459
- return this.__structDef.set(this.__buffer, fieldName, value);
1460
- }
1461
-
1462
- // Bulk operations for better performance
1463
- setFields(fields) {
1464
- for (const [name, value] of Object.entries(fields)) {
1465
- this.__structDef.set(this.__buffer, name, value);
1466
- }
1467
- }
1468
-
1469
- getFields(fieldNames) {
1470
- const result = {};
1471
- for (const name of fieldNames) {
1472
- result[name] = this.__structDef.get(this.__buffer, name);
1473
- }
1474
- return result;
1475
- }
1476
-
1477
- // bulk operations (Proxy reads directly from buffer - no cache needed)
1478
- withBulkUpdate(callback) {
1479
- return callback(this);
1480
- }
1481
-
1482
- // Direct typed array access for numeric fields (maximum performance)
1483
- getInt32Array(offset = 0, length) {
1484
- return new Int32Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
1485
- }
1486
-
1487
- getFloat64Array(offset = 0, length) {
1488
- return new Float64Array(this.__buffer.buffer, this.__buffer.byteOffset + offset, length);
1489
- }
1490
-
1491
- // Get raw buffer slice for external operations
1492
- getBufferSlice(offset = 0, size = this.__buffer.length - offset) {
1493
- return this.__buffer.subarray(offset, offset + size);
1494
- }
1495
-
1496
- // Vectorized operations for arrays of structs
1497
- static createArray(count, initialValues = []) {
1498
- const instances = [];
1499
- for (let i = 0; i < count; i++) {
1500
- instances.push(this.create(initialValues[i] || {}));
1501
- }
1502
- return instances;
1503
- }
1504
-
1505
- // Bulk update all instances in array
1506
- static updateArray(instances, updates) {
1507
- for (let i = 0; i < instances.length && i < updates.length; i++) {
1508
- if (updates[i] && typeof updates[i] === "object") {
1509
- instances[i].setFields(updates[i]);
1510
- }
1511
- }
1512
- }
1513
-
1514
- toObject() {
1515
- return this.__structDef.toObject(this.__buffer);
1516
- }
1517
- }
1518
-
1519
- class Union extends Structure {
1520
- static _buildStruct() {
1521
- const fields = this._fields_ || {};
1522
- let mapFields = {};
1523
- if (Array.isArray(fields)) {
1524
- for (const entry of fields) {
1525
- if (Array.isArray(entry) && entry.length >= 2) {
1526
- mapFields[entry[0]] = entry[1];
1527
- }
1528
- }
1529
- } else if (typeof fields === "object" && fields !== null) {
1530
- mapFields = fields;
1531
- }
1532
- const def = union(mapFields);
1533
- this._structDef = def;
1534
- return def;
1535
- }
1536
- }
1537
-
1538
- /**
1539
- * Helper per creare array a dimensione fissa (come c_int * 5 in Python)
1540
- * Ritorna un ArrayType wrapper con supporto per indexing arr[i]
1541
- *
1542
- * @param {string} elementType - Tipo degli elementi ('int32', 'float', etc.)
1543
- * @param {number} count - Numero di elementi
1544
- * @returns {Object} ArrayType con metodi getSize(), getLength(), create(), wrap()
1545
- *
1546
- * @example
1547
- * const IntArray5 = array('int32', 5);
1548
- * const arr = IntArray5.create([1, 2, 3, 4, 5]);
1549
- * console.log(arr[0]); // 1 - indexing Python-like!
1550
- * arr[2] = 42;
1551
- * console.log(arr[2]); // 42
1552
- */
1553
- function array(elementType, count) {
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");
1561
- }
1562
-
1563
- const elementSize = sizeof(elementType);
1564
- let nativeArray;
1565
- if (isNativeTypeObj) {
1566
- nativeArray = new ArrayType(elementType, count);
1567
- } else {
1568
- // Provide a small JS-side shim implementing the minimal ArrayType API
1569
- nativeArray = {
1570
- getSize: () => count * elementSize,
1571
- getLength: () => count,
1572
- getAlignment: () => elementSize,
1573
- create: (values) => {
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;
1579
- },
1580
- };
1581
- }
1582
-
1583
- // Funzione wrap che ritorna un Proxy
1584
- const wrap = function (buffer) {
1585
- return new Proxy(buffer, {
1586
- get(target, prop, receiver) {
1587
- // Special case for _buffer to return the underlying buffer
1588
- if (prop === "_buffer") {
1589
- return target;
1590
- }
1591
-
1592
- // Special case for toString to show array contents
1593
- if (prop === "toString") {
1594
- return function () {
1595
- try {
1596
- const arr = Array.from(this);
1597
- return `[${arr.join(", ")}]`;
1598
- } catch (e) {
1599
- return "[error]";
1600
- }
1601
- }.bind(receiver);
1602
- }
1603
-
1604
- // Intercetta Symbol.iterator per iterare sugli elementi, non sui bytes
1605
- if (prop === Symbol.iterator) {
1606
- return function* () {
1607
- for (let i = 0; i < count; i++) {
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
- }
1615
- }
1616
- };
1617
- }
1618
-
1619
- // Ignora altri Symbol
1620
- if (typeof prop === "symbol") {
1621
- const value = Reflect.get(target, prop, target);
1622
- if (typeof value === "function") {
1623
- return value.bind(target);
1624
- }
1625
- return value;
1626
- }
1627
-
1628
- // Se è un numero (indice) - intercetta PRIMA di controllare target
1629
- const index = Number(prop);
1630
- if (Number.isInteger(index) && !isNaN(index)) {
1631
- if (index >= 0 && index < count) {
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);
1637
- }
1638
- // Indice numerico fuori bounds -> undefined (comportamento JavaScript)
1639
- return undefined;
1640
- }
1641
-
1642
- // Per tutto il resto, usa il buffer normale
1643
- const value = Reflect.get(target, prop, target);
1644
- // Se è una funzione, bindala al target
1645
- if (typeof value === "function") {
1646
- return value.bind(target);
1647
- }
1648
- return value;
1649
- },
1650
- set(target, prop, value) {
1651
- // Ignora i Symbol
1652
- if (typeof prop === "symbol") {
1653
- return Reflect.set(target, prop, value, target);
1654
- }
1655
-
1656
- const index = Number(prop);
1657
- if (Number.isInteger(index) && !isNaN(index)) {
1658
- if (index >= 0 && index < count) {
1659
- writeValue(target, elementType, value, index * elementSize);
1660
- return true;
1661
- }
1662
- // Indice fuori bounds - non scrive nulla ma ritorna false
1663
- return false;
1664
- }
1665
- return Reflect.set(target, prop, value, target);
1666
- },
1667
- });
1668
- };
1669
-
1670
- // Ritorna un wrapper object che delega a nativeArray ma override create()
1671
- return {
1672
- // Informazioni sul tipo
1673
- elementType,
1674
- length: count,
1675
-
1676
- // Metodi delegati
1677
- getSize: () => nativeArray.getSize(),
1678
- getLength: () => nativeArray.getLength(),
1679
- getAlignment: () => nativeArray.getAlignment(),
1680
-
1681
- // create ritorna automaticamente il proxy
1682
- create: (values) => {
1683
- const size = count * sizeof(elementType);
1684
- const buffer = alloc(size);
1685
- buffer.fill(0);
1686
- if (Array.isArray(values)) {
1687
- for (let i = 0; i < Math.min(values.length, count); i++) {
1688
- writeValue(buffer, elementType, values[i], i * sizeof(elementType));
1689
- }
1690
- } else if (typeof values === "string") {
1691
- for (let i = 0; i < Math.min(values.length, count); i++) {
1692
- writeValue(buffer, elementType, values.charCodeAt(i), i * sizeof(elementType));
1693
- }
1694
- }
1695
- return wrap(buffer);
1696
- },
1697
-
1698
- // wrap esplicito
1699
- wrap: wrap,
1700
-
1701
- // Per compatibilità, esponi il native array (serve per StructType.addField)
1702
- _native: nativeArray,
1703
-
1704
- // Helper per verificare se è un ArrayType wrapper
1705
- _isArrayType: true,
1706
- };
1707
- }
1708
-
1709
- // ============================================================================
1710
- // Error Handling
1711
- // ============================================================================
1712
- // ============================================================================
1713
-
1714
- let _errno_funcs = null;
1715
- let _win_error_funcs = null;
1716
-
1717
- /**
1718
- * Inizializza le funzioni errno (lazy loading)
1719
- */
1720
- function _initErrno() {
1721
- if (_errno_funcs) return _errno_funcs;
1722
-
1723
- try {
1724
- if (process.platform === "win32") {
1725
- const msvcrt = new CDLL("msvcrt.dll");
1726
- _errno_funcs = {
1727
- _get: msvcrt.func("_get_errno", c_int32, [c_void_p]),
1728
- _set: msvcrt.func("_set_errno", c_int32, [c_int32]),
1729
- _lib: msvcrt,
1730
- };
1731
- } else if (process.platform === "darwin") {
1732
- // macOS usa __error invece di __errno_location
1733
- const libc = new CDLL(null);
1734
- _errno_funcs = {
1735
- _location: libc.func("__error", c_void_p, []),
1736
- _lib: libc,
1737
- };
1738
- } else {
1739
- // Linux e altri Unix usano __errno_location
1740
- const libc = new CDLL(null);
1741
- _errno_funcs = {
1742
- _location: libc.func("__errno_location", c_void_p, []),
1743
- _lib: libc,
1744
- };
1745
- }
1746
- } catch (e) {
1747
- _errno_funcs = { error: e.message };
1748
- }
1749
-
1750
- return _errno_funcs;
1751
- }
1752
-
1753
- /**
1754
- * Ottiene il valore corrente di errno
1755
- * @returns {number} Valore di errno
1756
- */
1757
- function get_errno() {
1758
- const funcs = _initErrno();
1759
- if (funcs.error) {
1760
- throw new Error("errno not available: " + funcs.error);
1761
- }
1762
-
1763
- if (process.platform === "win32") {
1764
- const buf = alloc(4);
1765
- funcs._get(buf);
1766
- return readValue(buf, c_int32);
1767
- } else {
1768
- const ptr = funcs._location();
1769
- return readValue(ptr, c_int32);
1770
- }
1771
- }
1772
-
1773
- /**
1774
- * Imposta il valore di errno
1775
- * @param {number} value - Nuovo valore
1776
- */
1777
- function set_errno(value) {
1778
- const funcs = _initErrno();
1779
- if (funcs.error) {
1780
- throw new Error("errno not available: " + funcs.error);
1781
- }
1782
-
1783
- if (process.platform === "win32") {
1784
- funcs._set(value);
1785
- } else {
1786
- const ptr = funcs._location();
1787
- writeValue(ptr, c_int32, value);
1788
- }
1789
- }
1790
-
1791
- /**
1792
- * Inizializza le funzioni Windows error (lazy loading)
1793
- */
1794
- function _initWinError() {
1795
- if (_win_error_funcs) return _win_error_funcs;
1796
-
1797
- if (process.platform !== "win32") {
1798
- _win_error_funcs = { error: "Windows only" };
1799
- return _win_error_funcs;
1800
- }
1801
-
1802
- try {
1803
- const kernel32 = new WinDLL("kernel32.dll");
1804
- _win_error_funcs = {
1805
- GetLastError: kernel32.func("GetLastError", c_uint32, []),
1806
- SetLastError: kernel32.func("SetLastError", c_void, [c_uint32]),
1807
- FormatMessageW: kernel32.func("FormatMessageW", c_uint32, [c_uint32, c_void_p, c_uint32, c_uint32, c_void_p, c_uint32, c_void_p]),
1808
- _lib: kernel32,
1809
- };
1810
- } catch (e) {
1811
- _win_error_funcs = { error: e.message };
1812
- }
1813
-
1814
- return _win_error_funcs;
1815
- }
1816
-
1817
- /**
1818
- * Ottiene l'ultimo errore Windows (GetLastError)
1819
- * @returns {number} Codice errore
1820
- */
1821
- function GetLastError() {
1822
- const funcs = _initWinError();
1823
- if (funcs.error) {
1824
- throw new Error("GetLastError not available: " + funcs.error);
1825
- }
1826
- return funcs.GetLastError();
1827
- }
1828
-
1829
- /**
1830
- * Imposta l'ultimo errore Windows (SetLastError)
1831
- * @param {number} code - Codice errore
1832
- */
1833
- function SetLastError(code) {
1834
- const funcs = _initWinError();
1835
- if (funcs.error) {
1836
- throw new Error("SetLastError not available: " + funcs.error);
1837
- }
1838
- funcs.SetLastError(code);
1839
- }
1840
-
1841
- /**
1842
- * Formatta un codice errore Windows in stringa
1843
- * @param {number} [code] - Codice errore (default: GetLastError())
1844
- * @returns {string} Messaggio di errore
1845
- */
1846
356
  function FormatError(code) {
1847
- const funcs = _initWinError();
1848
- if (funcs.error) {
1849
- throw new Error("FormatError not available: " + funcs.error);
1850
- }
1851
-
1852
- if (code === undefined) {
1853
- code = funcs.GetLastError();
1854
- }
1855
-
1856
- const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
1857
- const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
1858
-
1859
- const bufSize = 512;
1860
- const buf = alloc(bufSize * 2); // Wide chars
1861
-
1862
- const len = funcs.FormatMessageW(
1863
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1864
- null,
1865
- code,
1866
- 0, // Default language
1867
- buf,
1868
- bufSize,
1869
- null,
1870
- );
1871
-
1872
- if (len === 0) {
1873
- return `Unknown error ${code}`;
1874
- }
1875
-
1876
- // Converti da UTF-16LE a string
1877
- return buf.toString("utf16le", 0, len * 2).replace(/\r?\n$/, "");
357
+ return _FormatError(code, WinDLL, alloc, c_uint32, c_void, c_void_p);
1878
358
  }
1879
359
 
1880
- /**
1881
- * Crea un Error da un codice errore Windows
1882
- * @param {number} [code] - Codice errore (default: GetLastError())
1883
- * @returns {Error} Oggetto Error con messaggio
1884
- */
1885
360
  function WinError(code) {
1886
- if (code === undefined) {
1887
- code = GetLastError();
1888
- }
1889
- const msg = FormatError(code);
1890
- const err = new Error(`[WinError ${code}] ${msg}`);
1891
- err.winerror = code;
1892
- return err;
361
+ return _WinError(code, WinDLL, alloc, c_uint32, c_void, c_void_p);
1893
362
  }
1894
363
 
1895
364
  /**
@@ -1899,122 +368,23 @@ function WinError(code) {
1899
368
  * @returns {Object} Definizione del bit field
1900
369
  */
1901
370
  function bitfield(baseType, bits) {
1902
- const baseSize = sizeof(baseType);
1903
- const maxBits = baseSize * 8;
1904
-
1905
- if (bits < 1 || bits > maxBits) {
1906
- throw new Error(`Bit field size must be between 1 and ${maxBits} for ${baseType}`);
1907
- }
1908
-
1909
- return {
1910
- _isBitField: true,
1911
- baseType: baseType,
1912
- bits: bits,
1913
- baseSize: baseSize,
1914
- };
1915
- }
1916
-
1917
- /**
1918
- * Helper per verificare se un tipo è una struct
1919
- */
1920
- function _isStruct(type) {
1921
- return typeof type === "object" && type !== null && typeof type.size === "number" && Array.isArray(type.fields) && typeof type.create === "function";
1922
- }
1923
-
1924
- /**
1925
- * Helper per verificare se un tipo è un array type
1926
- */
1927
- function _isArrayType(type) {
1928
- return typeof type === "object" && type !== null && type._isArrayType === true;
1929
- }
1930
-
1931
- /**
1932
- * Helper per verificare se un tipo è un bit field
1933
- */
1934
- function _isBitField(type) {
1935
- return typeof type === "object" && type !== null && type._isBitField === true;
1936
- }
1937
-
1938
- /**
1939
- * Helper per leggere un valore intero unsigned da un buffer
1940
- * @private
1941
- */
1942
- function _readUintFromBuffer(buf, offset, byteSize) {
1943
- switch (byteSize) {
1944
- case 1:
1945
- return buf.readUInt8(offset);
1946
- case 2:
1947
- return buf.readUInt16LE(offset);
1948
- case 4:
1949
- return buf.readUInt32LE(offset);
1950
- case 8:
1951
- return buf.readBigUInt64LE(offset);
1952
- default:
1953
- throw new Error(`Unsupported byte size: ${byteSize}`);
1954
- }
1955
- }
1956
-
1957
- /**
1958
- * Helper per scrivere un valore intero unsigned in un buffer
1959
- * @private
1960
- */
1961
- function _writeUintToBuffer(buf, offset, byteSize, value) {
1962
- switch (byteSize) {
1963
- case 1:
1964
- buf.writeUInt8(value, offset);
1965
- break;
1966
- case 2:
1967
- buf.writeUInt16LE(value, offset);
1968
- break;
1969
- case 4:
1970
- buf.writeUInt32LE(value, offset);
1971
- break;
1972
- case 8:
1973
- buf.writeBigUInt64LE(value, offset);
1974
- break;
1975
- default:
1976
- throw new Error(`Unsupported byte size: ${byteSize}`);
1977
- }
371
+ return _bitfield(baseType, bits, sizeof);
1978
372
  }
1979
373
 
1980
374
  /**
1981
- * Legge un valore bit field da un buffer
375
+ * Helper per leggere un valore bit field da un buffer
1982
376
  * @private
1983
377
  */
1984
378
  function _readBitField(buf, offset, baseType, bitOffset, bitSize) {
1985
- const baseSize = sizeof(baseType);
1986
- const value = _readUintFromBuffer(buf, offset, baseSize);
1987
-
1988
- // Estrai i bit
1989
- if (baseSize === 8) {
1990
- const mask = (1n << BigInt(bitSize)) - 1n;
1991
- return Number((value >> BigInt(bitOffset)) & mask);
1992
- } else {
1993
- const mask = (1 << bitSize) - 1;
1994
- return (value >> bitOffset) & mask;
1995
- }
379
+ return _readBitFieldHelper(buf, offset, baseType, bitOffset, bitSize, sizeof);
1996
380
  }
1997
381
 
1998
382
  /**
1999
- * Scrive un valore bit field in un buffer
383
+ * Helper per scrivere un valore bit field in un buffer
2000
384
  * @private
2001
385
  */
2002
386
  function _writeBitField(buf, offset, baseType, bitOffset, bitSize, newValue) {
2003
- const baseSize = sizeof(baseType);
2004
- let value = _readUintFromBuffer(buf, offset, baseSize);
2005
-
2006
- // Modifica i bit
2007
- if (baseSize === 8) {
2008
- const mask = (1n << BigInt(bitSize)) - 1n;
2009
- const clearMask = ~(mask << BigInt(bitOffset));
2010
- value = (value & clearMask) | ((BigInt(newValue) & mask) << BigInt(bitOffset));
2011
- } else {
2012
- const mask = (1 << bitSize) - 1;
2013
- const clearMask = ~(mask << bitOffset);
2014
- value = (value & clearMask) | ((newValue & mask) << bitOffset);
2015
- }
2016
-
2017
- _writeUintToBuffer(buf, offset, baseSize, value);
387
+ return _writeBitFieldHelper(buf, offset, baseType, bitOffset, bitSize, newValue, sizeof);
2018
388
  }
2019
389
 
2020
390
  /**
@@ -2031,1323 +401,75 @@ function _writeBitField(buf, offset, baseType, bitOffset, bitSize, newValue) {
2031
401
  * @returns {Object} Definizione della struttura
2032
402
  */
2033
403
  function struct(fields, options = {}) {
2034
- const packed = options.packed || false;
2035
- let totalSize = 0;
2036
- const fieldDefs = [];
2037
- let maxAlignment = 1;
2038
-
2039
- // Stato per bit fields consecutivi
2040
- let currentBitFieldBase = null;
2041
- let currentBitFieldOffset = 0;
2042
- let currentBitOffset = 0;
2043
-
2044
- for (const [name, type] of Object.entries(fields)) {
2045
- let fieldDef;
2046
-
2047
- // Caso 0: Anonymous field - { type: SomeStruct, anonymous: true }
2048
- if (typeof type === "object" && type !== null && type.anonymous === true && type.type) {
2049
- // Reset bit field state
2050
- currentBitFieldBase = null;
2051
- currentBitOffset = 0;
2052
-
2053
- const actualType = type.type;
2054
- const size = actualType.size;
2055
- const alignment = packed ? 1 : actualType.alignment;
2056
-
2057
- // Applica padding per allineamento
2058
- if (!packed && totalSize % alignment !== 0) {
2059
- totalSize += alignment - (totalSize % alignment);
2060
- }
2061
-
2062
- fieldDef = {
2063
- name,
2064
- type: actualType,
2065
- offset: totalSize,
2066
- size,
2067
- alignment,
2068
- isNested: true,
2069
- isAnonymous: true,
2070
- };
2071
-
2072
- totalSize += size;
2073
-
2074
- if (alignment > maxAlignment) {
2075
- maxAlignment = alignment;
2076
- }
2077
- }
2078
- // Caso 1: Bit field
2079
- else if (_isBitField(type)) {
2080
- const { baseType, bits, baseSize } = type;
2081
- const alignment = packed ? 1 : Math.min(baseSize, native.POINTER_SIZE);
2082
-
2083
- // Verifica se possiamo continuare nel bit field corrente
2084
- const canContinue = currentBitFieldBase === baseType && currentBitOffset + bits <= baseSize * 8;
2085
-
2086
- if (!canContinue) {
2087
- // Inizia un nuovo bit field
2088
- // Applica padding per allineamento
2089
- if (!packed && totalSize % alignment !== 0) {
2090
- totalSize += alignment - (totalSize % alignment);
2091
- }
2092
-
2093
- currentBitFieldBase = baseType;
2094
- currentBitFieldOffset = totalSize;
2095
- currentBitOffset = 0;
2096
- totalSize += baseSize;
2097
- }
2098
-
2099
- fieldDef = {
2100
- name,
2101
- type: baseType,
2102
- offset: currentBitFieldOffset,
2103
- size: 0, // Bit fields non aggiungono size extra
2104
- alignment,
2105
- isBitField: true,
2106
- bitOffset: currentBitOffset,
2107
- bitSize: bits,
2108
- baseSize,
2109
- };
2110
-
2111
- currentBitOffset += bits;
2112
-
2113
- if (alignment > maxAlignment) {
2114
- maxAlignment = alignment;
2115
- }
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
- }
2177
- // Caso 2: Nested struct
2178
- else if (_isStruct(type)) {
2179
- // Reset bit field state
2180
- currentBitFieldBase = null;
2181
- currentBitOffset = 0;
2182
-
2183
- const nestedStruct = type;
2184
- const size = nestedStruct.size;
2185
- const alignment = packed ? 1 : nestedStruct.alignment;
2186
-
2187
- // Applica padding per allineamento
2188
- if (!packed && totalSize % alignment !== 0) {
2189
- totalSize += alignment - (totalSize % alignment);
2190
- }
2191
-
2192
- fieldDef = {
2193
- name,
2194
- type: nestedStruct,
2195
- offset: totalSize,
2196
- size,
2197
- alignment,
2198
- isNested: true,
2199
- };
2200
-
2201
- totalSize += size;
2202
-
2203
- if (alignment > maxAlignment) {
2204
- maxAlignment = alignment;
2205
- }
2206
- }
2207
- // Caso 3: Array type
2208
- else if (_isArrayType(type)) {
2209
- // Reset bit field state
2210
- currentBitFieldBase = null;
2211
- currentBitOffset = 0;
2212
-
2213
- const size = type.getSize();
2214
- const elemSize = sizeof(type.elementType);
2215
- const alignment = packed ? 1 : Math.min(elemSize, native.POINTER_SIZE);
2216
-
2217
- // Applica padding per allineamento
2218
- if (!packed && totalSize % alignment !== 0) {
2219
- totalSize += alignment - (totalSize % alignment);
2220
- }
2221
-
2222
- fieldDef = {
2223
- name,
2224
- type,
2225
- offset: totalSize,
2226
- size,
2227
- alignment,
2228
- isArray: true,
2229
- };
2230
-
2231
- totalSize += size;
2232
-
2233
- if (alignment > maxAlignment) {
2234
- maxAlignment = alignment;
2235
- }
2236
- }
2237
- // Caso 4: Tipo base (SimpleCData)
2238
- else {
2239
- // Reset bit field state
2240
- currentBitFieldBase = null;
2241
- currentBitOffset = 0;
2242
-
2243
- // Validate type is a SimpleCData class
2244
- if (!(typeof type === "function" && type._isSimpleCData)) {
2245
- throw new TypeError(`struct field "${name}": type must be a SimpleCData class, struct, union, or array`);
2246
- }
2247
-
2248
- const size = type._size;
2249
- const naturalAlign = Math.min(size, native.POINTER_SIZE);
2250
- const alignment = packed ? 1 : naturalAlign;
2251
-
2252
- // Applica padding per allineamento
2253
- if (!packed && totalSize % alignment !== 0) {
2254
- totalSize += alignment - (totalSize % alignment);
2255
- }
2256
-
2257
- fieldDef = {
2258
- name,
2259
- type,
2260
- offset: totalSize,
2261
- size,
2262
- alignment,
2263
- };
2264
-
2265
- totalSize += size;
2266
-
2267
- if (alignment > maxAlignment) {
2268
- maxAlignment = alignment;
2269
- }
2270
- }
2271
-
2272
- fieldDefs.push(fieldDef);
2273
- }
2274
-
2275
- // Padding finale per allineamento della struct
2276
- if (!packed && totalSize % maxAlignment !== 0) {
2277
- totalSize += maxAlignment - (totalSize % maxAlignment);
2278
- }
2279
-
2280
- // Crea Map per lookup O(1) invece di find() O(n)
2281
- const fieldMap = new Map();
2282
- for (const field of fieldDefs) {
2283
- fieldMap.set(field.name, field);
2284
- }
2285
-
2286
- // Pre-compile reader/writer functions for each field
2287
- const fieldReaders = new Map();
2288
- const fieldWriters = new Map();
2289
-
2290
- for (const field of fieldDefs) {
2291
- const offset = field.offset;
2292
- const name = field.name;
2293
-
2294
- // Skip complex types - they need special handling
2295
- if (field.isBitField || field.isNested || field.isArray) {
2296
- continue;
2297
- }
2298
-
2299
- // Pre-compile based on SimpleCData type
2300
- // Use the type's _reader and _writer directly for optimization
2301
- const fieldType = field.type;
2302
- if (fieldType && fieldType._isSimpleCData) {
2303
- const fieldOffset = offset; // Capture offset for closure
2304
- fieldReaders.set(name, (buf) => fieldType._reader(buf, fieldOffset));
2305
- fieldWriters.set(name, (buf, val) => fieldType._writer(buf, fieldOffset, val));
2306
- }
2307
- }
2308
-
2309
- const structDef = {
2310
- size: totalSize,
2311
- alignment: maxAlignment,
2312
- fields: fieldDefs,
2313
- packed: packed,
2314
- _isStructType: true,
2315
-
2316
- /**
2317
- * Alloca e inizializza una nuova istanza
2318
- * @param {Object} [values] - Valori iniziali
2319
- * @returns {Proxy} Proxy-based struct instance
2320
- */
2321
- create(values = {}) {
2322
- const buf = alloc(totalSize);
2323
- buf.fill(0); // Inizializza a zero
2324
-
2325
- // Prima imposta i campi diretti
2326
- for (const field of fieldDefs) {
2327
- if (values[field.name] !== undefined) {
2328
- structDef.set(buf, field.name, values[field.name]);
2329
- }
2330
- }
2331
-
2332
- // Poi gestisci i campi anonymous - i loro campi possono essere passati direttamente nell'oggetto values
2333
- for (const field of fieldDefs) {
2334
- if (field.isAnonymous && field.type && field.type.fields) {
2335
- for (const subField of field.type.fields) {
2336
- if (values[subField.name] !== undefined) {
2337
- structDef.set(buf, subField.name, values[subField.name]);
2338
- }
2339
- }
2340
- }
2341
- }
2342
-
2343
- // Proxy-based instance with pre-compiled readers/writers
2344
- return new Proxy(buf, {
2345
- get(target, prop, receiver) {
2346
- // Special properties
2347
- if (prop === "_buffer") return target;
2348
- if (prop === "toObject") return () => structDef.toObject(target);
2349
- if (prop === Symbol.toStringTag) return "StructInstance";
2350
- if (prop === Symbol.iterator) return undefined;
2351
-
2352
- // Handle Buffer/TypedArray properties FIRST before field checks
2353
- // TypedArray methods need direct access to avoid Proxy interference
2354
- if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
2355
- return target[prop];
2356
- }
2357
-
2358
- // Use pre-compiled reader if available
2359
- if (typeof prop === "string") {
2360
- const reader = fieldReaders.get(prop);
2361
- if (reader) return reader(target);
2362
-
2363
- // Fallback to general get for complex types
2364
- if (fieldMap.has(prop)) {
2365
- return structDef.get(target, prop);
2366
- }
2367
- }
2368
-
2369
- // Check anonymous fields
2370
- for (const f of fieldDefs) {
2371
- if (f.isAnonymous && f.type && f.type.fields) {
2372
- if (f.type.fields.some((sf) => sf.name === prop)) {
2373
- return structDef.get(target, prop);
2374
- }
2375
- }
2376
- }
2377
-
2378
- // Fallback to buffer properties and methods
2379
- const value = target[prop];
2380
- if (typeof value === "function") {
2381
- return value.bind(target);
2382
- }
2383
- return value;
2384
- },
2385
- set(target, prop, value, receiver) {
2386
- // FAST PATH: Use pre-compiled writer if available
2387
- if (typeof prop === "string") {
2388
- const writer = fieldWriters.get(prop);
2389
- if (writer) {
2390
- writer(target, value);
2391
- return true;
2392
- }
2393
-
2394
- // Fallback to general set for complex types
2395
- if (fieldMap.has(prop)) {
2396
- structDef.set(target, prop, value);
2397
- return true;
2398
- }
2399
- }
2400
-
2401
- // Check anonymous fields
2402
- for (const f of fieldDefs) {
2403
- if (f.isAnonymous && f.type && f.type.fields) {
2404
- if (f.type.fields.some((sf) => sf.name === prop)) {
2405
- structDef.set(target, prop, value);
2406
- return true;
2407
- }
2408
- }
2409
- }
2410
-
2411
- // Fallback
2412
- return Reflect.set(target, prop, value, target);
2413
- },
2414
- has(target, prop) {
2415
- if (prop === "_buffer" || prop === "toObject") return true;
2416
- if (typeof prop === "string" && fieldMap.has(prop)) return true;
2417
- // Check anonymous fields
2418
- for (const f of fieldDefs) {
2419
- if (f.isAnonymous && f.type && f.type.fields) {
2420
- if (f.type.fields.some((sf) => sf.name === prop)) return true;
2421
- }
2422
- }
2423
- return Reflect.has(target, prop);
2424
- },
2425
- ownKeys(target) {
2426
- const keys = [];
2427
- for (const f of fieldDefs) {
2428
- if (f.isAnonymous && f.type && f.type.fields) {
2429
- for (const sf of f.type.fields) {
2430
- keys.push(sf.name);
2431
- }
2432
- } else {
2433
- keys.push(f.name);
2434
- }
2435
- }
2436
- return keys;
2437
- },
2438
- getOwnPropertyDescriptor(target, prop) {
2439
- if (typeof prop === "string" && (fieldMap.has(prop) || prop === "_buffer" || prop === "toObject")) {
2440
- return {
2441
- enumerable: prop !== "_buffer" && prop !== "toObject",
2442
- configurable: true,
2443
- writable: true,
2444
- };
2445
- }
2446
- // Check anonymous fields
2447
- for (const f of fieldDefs) {
2448
- if (f.isAnonymous && f.type && f.type.fields) {
2449
- if (f.type.fields.some((sf) => sf.name === prop)) {
2450
- return {
2451
- enumerable: true,
2452
- configurable: true,
2453
- writable: true,
2454
- };
2455
- }
2456
- }
2457
- }
2458
- return Reflect.getOwnPropertyDescriptor(target, prop);
2459
- },
2460
- });
2461
- },
2462
-
2463
- /**
2464
- * Legge un campo dalla struttura
2465
- * @param {Buffer|Object} bufOrObj - Buffer della struttura O object wrapper
2466
- * @param {string} fieldName - Nome del campo (supporta 'outer.inner' per nested)
2467
- * @returns {*} Valore del campo
2468
- */
2469
- get(bufOrObj, fieldName) {
2470
- // supporta sia buffer che object wrapper o plain object
2471
- let buf;
2472
- if (Buffer.isBuffer(bufOrObj)) {
2473
- buf = bufOrObj;
2474
- } else if (bufOrObj && bufOrObj._buffer) {
2475
- // Object wrapper - gestisci dot notation
2476
- if (fieldName.includes(".")) {
2477
- const parts = fieldName.split(".");
2478
- let current = bufOrObj;
2479
- for (const part of parts) {
2480
- current = current[part];
2481
- if (current === undefined) return undefined;
2482
- }
2483
- return current;
2484
- }
2485
- // Accesso diretto tramite property
2486
- return bufOrObj[fieldName];
2487
- } else if (typeof bufOrObj === "object" && bufOrObj !== null) {
2488
- // Plain object (da toObject/create) - accesso diretto
2489
- if (fieldName.includes(".")) {
2490
- const parts = fieldName.split(".");
2491
- let current = bufOrObj;
2492
- for (const part of parts) {
2493
- current = current[part];
2494
- if (current === undefined) return undefined;
2495
- }
2496
- return current;
2497
- }
2498
- // Accesso diretto tramite property
2499
- return bufOrObj[fieldName];
2500
- } else {
2501
- throw new TypeError("Expected Buffer or struct instance");
2502
- }
2503
-
2504
- // Fast path: nome semplice senza dot notation
2505
- // Lookup with Map
2506
- let field = fieldMap.get(fieldName);
2507
-
2508
- if (field) {
2509
- // Bit field
2510
- if (field.isBitField) {
2511
- return _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
2512
- }
2513
-
2514
- // Nested struct (senza dot = ritorna intero oggetto)
2515
- if (field.isNested) {
2516
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2517
- // Proxy-based nested instance
2518
- return field.type.create
2519
- ? // Se ha create(), usa quello per creare il proxy
2520
- new Proxy(nestedBuf, {
2521
- get(target, prop, receiver) {
2522
- if (prop === "_buffer") return target;
2523
- if (prop === "toObject") return () => field.type.toObject(target);
2524
- if (prop === Symbol.toStringTag) return "NestedStructInstance";
2525
- if (prop === Symbol.iterator) return undefined;
2526
- // Handle Buffer/TypedArray properties
2527
- if (prop === "length" || prop === "byteLength" || prop === "byteOffset" || prop === "buffer" || prop === "BYTES_PER_ELEMENT") {
2528
- return target[prop];
2529
- }
2530
- if (typeof prop === "string") {
2531
- // Check direct fields
2532
- const nestedFieldMap = field.type.fields ? new Map(field.type.fields.map((f) => [f.name, f])) : new Map();
2533
- if (nestedFieldMap.has(prop)) {
2534
- return field.type.get(target, prop);
2535
- }
2536
- // Check anonymous fields
2537
- for (const sf of field.type.fields || []) {
2538
- if (sf.isAnonymous && sf.type && sf.type.fields) {
2539
- if (sf.type.fields.some((ssf) => ssf.name === prop)) {
2540
- return field.type.get(target, prop);
2541
- }
2542
- }
2543
- }
2544
- }
2545
- const value = target[prop];
2546
- if (typeof value === "function") return value.bind(target);
2547
- return value;
2548
- },
2549
- set(target, prop, value, receiver) {
2550
- if (typeof prop === "string") {
2551
- const nestedFieldMap = field.type.fields ? new Map(field.type.fields.map((f) => [f.name, f])) : new Map();
2552
- if (nestedFieldMap.has(prop)) {
2553
- field.type.set(target, prop, value);
2554
- return true;
2555
- }
2556
- // Check anonymous fields
2557
- for (const sf of field.type.fields || []) {
2558
- if (sf.isAnonymous && sf.type && sf.type.fields) {
2559
- if (sf.type.fields.some((ssf) => ssf.name === prop)) {
2560
- field.type.set(target, prop, value);
2561
- return true;
2562
- }
2563
- }
2564
- }
2565
- }
2566
- return Reflect.set(target, prop, value, receiver);
2567
- },
2568
- has(target, prop) {
2569
- if (prop === "_buffer" || prop === "toObject") return true;
2570
- if (typeof prop === "string" && field.type.fields) {
2571
- if (field.type.fields.some((f) => f.name === prop)) return true;
2572
- }
2573
- return Reflect.has(target, prop);
2574
- },
2575
- ownKeys(target) {
2576
- return (field.type.fields || []).map((f) => f.name);
2577
- },
2578
- getOwnPropertyDescriptor(target, prop) {
2579
- if (typeof prop === "string" && field.type.fields && field.type.fields.some((f) => f.name === prop)) {
2580
- return {
2581
- enumerable: true,
2582
- configurable: true,
2583
- writable: true,
2584
- };
2585
- }
2586
- return Reflect.getOwnPropertyDescriptor(target, prop);
2587
- },
2588
- })
2589
- : field.type.toObject(nestedBuf);
2590
- }
2591
-
2592
- // Array field
2593
- if (field.isArray) {
2594
- const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2595
- return field.type.wrap(arrayBuf);
2596
- }
2597
-
2598
- // Tipo base - fast path!
2599
- return readValue(buf, field.type, field.offset);
2600
- }
2601
-
2602
- // supporto per accesso nested 'outer.inner' o anonymous fields
2603
- const parts = fieldName.split(".");
2604
- const firstPart = parts[0];
2605
-
2606
- field = fieldMap.get(firstPart);
2607
-
2608
- // Se non trovato, cerca negli anonymous fields
2609
- if (!field) {
2610
- for (const f of fieldDefs) {
2611
- if (f.isAnonymous && f.type && f.type.fields) {
2612
- const hasField = f.type.fields.some((subField) => subField.name === firstPart);
2613
- if (hasField) {
2614
- const nestedBuf = buf.subarray(f.offset, f.offset + f.size);
2615
- return f.type.get(nestedBuf, fieldName);
2616
- }
2617
- }
2618
- }
2619
- throw new Error(`Unknown field: ${firstPart}`);
2620
- }
2621
-
2622
- // Nested struct con dot notation
2623
- if (field.isNested && parts.length > 1) {
2624
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2625
- return field.type.get(nestedBuf, parts.slice(1).join("."));
2626
- }
2627
-
2628
- throw new Error(`Unknown field: ${fieldName}`);
2629
- },
2630
-
2631
- /**
2632
- * Scrive un campo nella struttura
2633
- * @param {Buffer|Object} bufOrObj - Buffer della struttura O object wrapper
2634
- * @param {string} fieldName - Nome del campo (supporta 'outer.inner' per nested)
2635
- * @param {*} value - Valore da scrivere
2636
- */
2637
- set(bufOrObj, fieldName, value) {
2638
- // supporta sia buffer che object wrapper o plain object
2639
- let buf;
2640
- if (Buffer.isBuffer(bufOrObj)) {
2641
- buf = bufOrObj;
2642
- } else if (bufOrObj && bufOrObj._buffer) {
2643
- // Object wrapper - accesso diretto tramite property
2644
- bufOrObj[fieldName] = value;
2645
- return;
2646
- } else if (typeof bufOrObj === "object" && bufOrObj !== null) {
2647
- // Plain object (da toObject/create) - accesso diretto
2648
- bufOrObj[fieldName] = value;
2649
- return;
2650
- } else {
2651
- throw new TypeError("Expected Buffer or struct instance");
2652
- }
2653
-
2654
- // Fast path: nome semplice senza dot notation
2655
- // Lookup with Map
2656
- let field = fieldMap.get(fieldName);
2657
-
2658
- if (field) {
2659
- // Bit field
2660
- if (field.isBitField) {
2661
- _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
2662
- return;
2663
- }
2664
-
2665
- // Nested struct (senza dot = imposta da oggetto)
2666
- if (field.isNested) {
2667
- if (Buffer.isBuffer(value)) {
2668
- value.copy(buf, field.offset, 0, field.size);
2669
- } else if (typeof value === "object") {
2670
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2671
- for (const [k, v] of Object.entries(value)) {
2672
- field.type.set(nestedBuf, k, v);
2673
- }
2674
- }
2675
- return;
2676
- }
2677
-
2678
- // Array field
2679
- if (field.isArray) {
2680
- if (Buffer.isBuffer(value)) {
2681
- value.copy(buf, field.offset, 0, field.size);
2682
- } else if (Array.isArray(value)) {
2683
- const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2684
- const wrapped = field.type.wrap(arrayBuf);
2685
- for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
2686
- wrapped[i] = value[i];
2687
- }
2688
- }
2689
- return;
2690
- }
2691
-
2692
- // Tipo base - fast path!
2693
- writeValue(buf, field.type, value, field.offset);
2694
- return;
2695
- }
2696
-
2697
- // Slow path: supporto per accesso nested 'outer.inner' o anonymous fields
2698
- const parts = fieldName.split(".");
2699
- const firstPart = parts[0];
2700
-
2701
- field = fieldMap.get(firstPart);
2702
-
2703
- // Se non trovato, cerca negli anonymous fields
2704
- if (!field) {
2705
- for (const f of fieldDefs) {
2706
- if (f.isAnonymous && f.type && f.type.fields) {
2707
- // Verifica se il campo esiste nel tipo anonimo
2708
- const hasField = f.type.fields.some((subField) => subField.name === firstPart);
2709
- if (hasField) {
2710
- // Scrive nel campo dell'anonymous field
2711
- const nestedBuf = buf.subarray(f.offset, f.offset + f.size);
2712
- f.type.set(nestedBuf, fieldName, value);
2713
- return;
2714
- }
2715
- }
2716
- }
2717
- throw new Error(`Unknown field: ${firstPart}`);
2718
- }
2719
-
2720
- // Bit field
2721
- if (field.isBitField) {
2722
- _writeBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize, value);
2723
- return;
2724
- }
2725
-
2726
- // Nested struct
2727
- if (field.isNested) {
2728
- if (parts.length > 1) {
2729
- // Accesso a campo annidato singolo
2730
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2731
- field.type.set(nestedBuf, parts.slice(1).join("."), value);
2732
- } else if (Buffer.isBuffer(value)) {
2733
- // Copia buffer
2734
- value.copy(buf, field.offset, 0, field.size);
2735
- } else if (typeof value === "object") {
2736
- // Imposta campi da oggetto
2737
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2738
- for (const [k, v] of Object.entries(value)) {
2739
- field.type.set(nestedBuf, k, v);
2740
- }
2741
- }
2742
- return;
2743
- }
2744
-
2745
- // Array field
2746
- if (field.isArray) {
2747
- if (Buffer.isBuffer(value)) {
2748
- // Copia buffer array
2749
- value.copy(buf, field.offset, 0, field.size);
2750
- } else if (Array.isArray(value)) {
2751
- // Inizializza da array JavaScript
2752
- const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2753
- const wrapped = field.type.wrap(arrayBuf);
2754
- for (let i = 0; i < Math.min(value.length, field.type.length); i++) {
2755
- wrapped[i] = value[i];
2756
- }
2757
- }
2758
- return;
2759
- }
2760
-
2761
- // Tipo base
2762
- writeValue(buf, field.type, value, field.offset);
2763
- },
2764
-
2765
- /**
2766
- * Legge tutti i campi come oggetto (ricorsivo per nested)
2767
- * @param {Buffer} buf - Buffer della struttura
2768
- * @returns {Object} Oggetto con tutti i campi
2769
- */
2770
- /**
2771
- * Converte buffer in plain object (KOFFI EAGER APPROACH)
2772
- * Legge TUTTI i valori UNA VOLTA e crea PLAIN properties (no getters)
2773
- * Molto più veloce! Sincronizzazione lazy quando serve _buffer
2774
- */
2775
- toObject(bufOrObj) {
2776
- // Se è già un object (non buffer), controlla se ha _buffer
2777
- if (!Buffer.isBuffer(bufOrObj)) {
2778
- // Se ha _buffer, estrailo e ricarica i valori (utile dopo modifiche FFI)
2779
- if (bufOrObj && bufOrObj._buffer && Buffer.isBuffer(bufOrObj._buffer)) {
2780
- bufOrObj = bufOrObj._buffer; // Estrai buffer e procedi con il reload
2781
- } else if (typeof bufOrObj === "object" && bufOrObj !== null) {
2782
- // È già un plain object, restituiscilo così com'è
2783
- return bufOrObj;
2784
- } else {
2785
- throw new TypeError("Expected Buffer or struct instance");
2786
- }
2787
- }
2788
-
2789
- const buf = bufOrObj;
2790
- const result = {};
2791
-
2792
- // Leggi tutti i campi in una volta (eager approach)
2793
- for (const field of fieldDefs) {
2794
- if (field.isBitField) {
2795
- result[field.name] = _readBitField(buf, field.offset, field.type, field.bitOffset, field.bitSize);
2796
- } else if (field.isNested) {
2797
- const nestedBuf = buf.subarray(field.offset, field.offset + field.size);
2798
- const nestedObj = field.type.toObject(nestedBuf);
2799
- if (field.isAnonymous) {
2800
- // Anonymous fields: promote their fields to parent level
2801
- Object.assign(result, nestedObj);
2802
- } else {
2803
- result[field.name] = nestedObj;
2804
- }
2805
- } else if (field.isArray) {
2806
- const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
2807
- result[field.name] = field.type.wrap(arrayBuf);
2808
- } else {
2809
- // Tipo base - lettura diretta
2810
- result[field.name] = readValue(buf, field.type, field.offset);
2811
- }
2812
- }
2813
-
2814
- return result;
2815
- },
2816
-
2817
- /**
2818
- * Ottiene il buffer di una nested struct
2819
- * @param {Buffer} buf - Buffer della struttura parent
2820
- * @param {string} fieldName - Nome del campo nested
2821
- * @returns {Buffer} Slice del buffer per la nested struct
2822
- */
2823
- getNestedBuffer(buf, fieldName) {
2824
- // O(1) lookup con Map
2825
- const field = fieldMap.get(fieldName);
2826
- if (!field) throw new Error(`Unknown field: ${fieldName}`);
2827
- if (!field.isNested) throw new Error(`Field ${fieldName} is not a nested struct`);
2828
- return buf.subarray(field.offset, field.offset + field.size);
2829
- },
2830
- };
2831
-
2832
- // Crea un'istanza C++ StructType per le operazioni native
2833
- try {
2834
- const cppStructType = new StructType();
2835
- // Per ora non aggiungiamo campi - testiamo se ToObject funziona senza
2836
- structDef._cppStructType = cppStructType;
2837
- } catch (e) {
2838
- // Se fallisce, continua senza C++ (fallback a JS)
2839
- console.warn("Failed to create C++ StructType, using JavaScript fallback:", e.message);
2840
- }
2841
-
2842
- // fromObject: write plain object values into buffer
2843
- structDef.fromObject = function (buf, obj) {
2844
- for (const [key, value] of Object.entries(obj)) {
2845
- if (this.fields.some((f) => f.name === key)) {
2846
- this.set(buf, key, value);
2847
- }
2848
- }
2849
- };
2850
-
2851
- // createRaw: return plain object without synchronization
2852
- structDef.createRaw = function (values = {}) {
2853
- return this.toObject(this.create(values)._buffer);
2854
- };
2855
-
2856
- return structDef;
404
+ return _struct(fields, options, sizeof, alloc, readValue, writeValue, _isStruct, _isArrayType, _isBitField, Structure, Union, native);
2857
405
  }
2858
406
 
2859
407
  // ============================================================================
2860
408
  // Python-compatible Simple C Data Types (instantiable)
2861
409
  // Usage: const v = new c_uint64(42); v.value; byref(v);
2862
410
  // ============================================================================
411
+ // SimpleCData base class (created via factory function)
412
+ const SimpleCData = createSimpleCDataClass(alloc);
2863
413
 
2864
- /**
2865
- * Base class for simple C data types (Python ctypes compatible)
2866
- * Subclasses must define static _size, _type, _reader, _writer
2867
- */
2868
- class SimpleCData {
2869
- /**
2870
- * @param {number|bigint} [value] - Initial value (default: 0)
2871
- */
2872
- constructor(value) {
2873
- const Ctor = this.constructor;
2874
- this._buffer = alloc(Ctor._size);
2875
- if (value !== undefined && value !== null) {
2876
- this.value = value;
2877
- }
2878
- }
2879
-
2880
- /**
2881
- * Get the current value
2882
- */
2883
- get value() {
2884
- const Ctor = this.constructor;
2885
- return Ctor._reader(this._buffer, 0);
2886
- }
2887
-
2888
- /**
2889
- * Set the value
2890
- */
2891
- set value(v) {
2892
- const Ctor = this.constructor;
2893
- Ctor._writer(this._buffer, 0, v);
2894
- }
2895
-
2896
- /**
2897
- * Size of the type in bytes (instance property)
2898
- */
2899
- get size() {
2900
- return this.constructor._size;
2901
- }
2902
-
2903
- /**
2904
- * Create instance from existing buffer (Python ctypes compatible)
2905
- * WARNING: Returns a VIEW into the buffer, not a copy!
2906
- * @param {Buffer} buffer - Source buffer
2907
- * @param {number} [offset=0] - Offset in buffer
2908
- * @returns {SimpleCData} New instance wrapping the buffer slice
2909
- */
2910
- static from_buffer(buffer, offset = 0) {
2911
- if (!Buffer.isBuffer(buffer)) {
2912
- throw new TypeError("from_buffer requires a Buffer");
2913
- }
2914
- const inst = Object.create(this.prototype);
2915
- inst._buffer = buffer.subarray(offset, offset + this._size);
2916
- return inst;
2917
- }
2918
-
2919
- /**
2920
- * Create instance from a COPY of buffer data (Python ctypes compatible)
2921
- * @param {Buffer} buffer - Source buffer
2922
- * @param {number} [offset=0] - Offset in buffer
2923
- * @returns {SimpleCData} New instance with copied data
2924
- */
2925
- static from_buffer_copy(buffer, offset = 0) {
2926
- if (!Buffer.isBuffer(buffer)) {
2927
- throw new TypeError("from_buffer_copy requires a Buffer");
2928
- }
2929
- const inst = new this();
2930
- buffer.copy(inst._buffer, 0, offset, offset + this._size);
2931
- return inst;
2932
- }
2933
-
2934
- /**
2935
- * Size of the type in bytes (static property)
2936
- */
2937
- static get size() {
2938
- return this._size;
2939
- }
2940
-
2941
- toString() {
2942
- return `${this.constructor.name}(${this.value})`;
2943
- }
2944
-
2945
- toJSON() {
2946
- const v = this.value;
2947
- return typeof v === "bigint" ? v.toString() : v;
2948
- }
2949
-
2950
- valueOf() {
2951
- return this.value;
2952
- }
2953
- }
2954
-
2955
- SimpleCData._isSimpleCData = true;
2956
-
2957
- class c_void extends SimpleCData {
2958
- static _size = 0;
2959
- static _type = "void";
2960
- static _reader = (buf, off) => undefined;
2961
- static _writer = (buf, off, val) => {};
2962
- }
2963
-
2964
- // ============================================================================
2965
- // Integer types - signed
2966
- // ============================================================================
2967
-
2968
- class c_int8 extends SimpleCData {
2969
- static _size = 1;
2970
- static _type = "int8";
2971
- static _reader = (buf, off) => buf.readInt8(off);
2972
- static _writer = (buf, off, val) => buf.writeInt8(val, off);
2973
- }
2974
-
2975
- class c_int16 extends SimpleCData {
2976
- static _size = 2;
2977
- static _type = "int16";
2978
- static _reader = (buf, off) => buf.readInt16LE(off);
2979
- static _writer = (buf, off, val) => buf.writeInt16LE(val, off);
2980
- }
2981
-
2982
- class c_int32 extends SimpleCData {
2983
- static _size = 4;
2984
- static _type = "int32";
2985
- static _reader = (buf, off) => buf.readInt32LE(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
- };
2990
- }
2991
-
2992
- class c_int64 extends SimpleCData {
2993
- static _size = 8;
2994
- static _type = "int64";
2995
- static _reader = (buf, off) => buf.readBigInt64LE(off);
2996
- static _writer = (buf, off, val) => buf.writeBigInt64LE(BigInt(val), off);
2997
- }
2998
-
2999
- // ============================================================================
3000
- // Integer types - unsigned
3001
- // ============================================================================
3002
-
3003
- class c_uint8 extends SimpleCData {
3004
- static _size = 1;
3005
- static _type = "uint8";
3006
- static _reader = (buf, off) => buf.readUInt8(off);
3007
- static _writer = (buf, off, val) => buf.writeUInt8(val, off);
3008
- }
3009
-
3010
- class c_uint16 extends SimpleCData {
3011
- static _size = 2;
3012
- static _type = "uint16";
3013
- static _reader = (buf, off) => buf.readUInt16LE(off);
3014
- static _writer = (buf, off, val) => buf.writeUInt16LE(val, off);
3015
- }
3016
-
3017
- class c_uint32 extends SimpleCData {
3018
- static _size = 4;
3019
- static _type = "uint32";
3020
- static _reader = (buf, off) => buf.readUInt32LE(off);
3021
- static _writer = (buf, off, val) => buf.writeUInt32LE(val, off);
3022
- }
3023
-
3024
- class c_uint64 extends SimpleCData {
3025
- static _size = 8;
3026
- static _type = "uint64";
3027
- static _reader = (buf, off) => buf.readBigUInt64LE(off);
3028
- static _writer = (buf, off, val) => buf.writeBigUInt64LE(BigInt(val), off);
3029
- }
3030
-
3031
- // ============================================================================
3032
- // Floating point types
3033
- // ============================================================================
3034
-
3035
- class c_float extends SimpleCData {
3036
- static _size = 4;
3037
- static _type = "float";
3038
- static _reader = (buf, off) => buf.readFloatLE(off);
3039
- static _writer = (buf, off, val) => buf.writeFloatLE(val, off);
3040
- }
3041
-
3042
- class c_double extends SimpleCData {
3043
- static _size = 8;
3044
- static _type = "double";
3045
- static _reader = (buf, off) => buf.readDoubleLE(off);
3046
- static _writer = (buf, off, val) => buf.writeDoubleLE(val, off);
3047
- }
3048
-
3049
- // ============================================================================
3050
- // Boolean type
3051
- // ============================================================================
3052
-
3053
- class c_bool extends SimpleCData {
3054
- static _size = 1;
3055
- static _type = "bool";
3056
- static _reader = (buf, off) => buf.readUInt8(off) !== 0;
3057
- static _writer = (buf, off, val) => buf.writeUInt8(val ? 1 : 0, off);
3058
- }
3059
-
3060
- // ============================================================================
3061
- // Character types
3062
- // ============================================================================
3063
-
3064
- class c_char extends SimpleCData {
3065
- static _size = 1;
3066
- static _type = "char";
3067
- static _reader = (buf, off) => buf.readInt8(off);
3068
- static _writer = (buf, off, val) => {
3069
- if (typeof val === "string") {
3070
- buf.writeUInt8(val.charCodeAt(0), off);
3071
- } else {
3072
- buf.writeInt8(val, off);
3073
- }
3074
- };
3075
- }
3076
-
3077
- class c_wchar extends SimpleCData {
3078
- static _size = native.WCHAR_SIZE;
3079
- static _type = "wchar";
3080
- static _reader = (buf, off) => {
3081
- if (native.WCHAR_SIZE === 2) {
3082
- return String.fromCharCode(buf.readUInt16LE(off));
3083
- }
3084
- return String.fromCodePoint(buf.readUInt32LE(off));
3085
- };
3086
- static _writer = (buf, off, val) => {
3087
- const code = typeof val === "string" ? val.codePointAt(0) : val;
3088
- if (native.WCHAR_SIZE === 2) {
3089
- buf.writeUInt16LE(code, off);
3090
- } else {
3091
- buf.writeUInt32LE(code, off);
3092
- }
3093
- };
3094
- }
3095
-
3096
- // ============================================================================
3097
- // Pointer types
3098
- // ============================================================================
3099
-
3100
- class c_void_p extends SimpleCData {
3101
- static _size = native.POINTER_SIZE;
3102
- static _type = "pointer";
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
- };
3108
- static _writer = (buf, off, val) => {
3109
- // Accept Buffers (pointer to memory) and struct proxies with _buffer
3110
- if (Buffer.isBuffer(val)) {
3111
- const addr = addressOf(val);
3112
- const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
3113
- if (native.POINTER_SIZE === 8) buf.writeBigUInt64LE(v, off);
3114
- else buf.writeUInt32LE(Number(v), off);
3115
- return;
3116
- }
3117
- if (val && typeof val === "object" && val._buffer && Buffer.isBuffer(val._buffer)) {
3118
- const addr = addressOf(val._buffer);
3119
- const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
3120
- if (native.POINTER_SIZE === 8) buf.writeBigUInt64LE(v, off);
3121
- else buf.writeUInt32LE(Number(v), off);
3122
- return;
3123
- }
3124
-
3125
- // Fallback: accept BigInt or number
3126
- const v = typeof val === "bigint" ? val : BigInt(val || 0);
3127
- if (native.POINTER_SIZE === 8) {
3128
- buf.writeBigUInt64LE(v, off);
3129
- } else {
3130
- buf.writeUInt32LE(Number(v), off);
3131
- }
3132
- };
3133
- }
3134
-
3135
- class c_size_t extends SimpleCData {
3136
- static _size = native.POINTER_SIZE;
3137
- static _type = "size_t";
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
- };
3143
- static _writer = (buf, off, val) => {
3144
- const v = typeof val === "bigint" ? val : BigInt(val || 0);
3145
- if (native.POINTER_SIZE === 8) {
3146
- buf.writeBigUInt64LE(v, off);
3147
- } else {
3148
- buf.writeUInt32LE(Number(v), off);
3149
- }
3150
- };
3151
- }
3152
-
3153
- // ============================================================================
3154
- // Platform-dependent types
3155
- // ============================================================================
3156
-
3157
- const _longSize = native.sizeof ? native.sizeof("long") : 4;
3158
-
3159
- class c_long extends SimpleCData {
3160
- static _size = _longSize;
3161
- static _type = "long";
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);
3164
- }
3165
-
3166
- class c_ulong extends SimpleCData {
3167
- static _size = _longSize;
3168
- static _type = "ulong";
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);
3171
- }
3172
-
3173
- // ============================================================================
3174
- // String pointer types
3175
- // ============================================================================
3176
-
3177
- class c_char_p extends SimpleCData {
3178
- static _size = native.POINTER_SIZE;
3179
- static _type = "char_p";
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
- };
3185
- static _writer = (buf, off, val) => {
3186
- if (typeof val === "string") {
3187
- val = cstring(val);
3188
- }
3189
- if (Buffer.isBuffer(val)) {
3190
- const addr = addressOf(val);
3191
- const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
3192
- if (native.POINTER_SIZE === 8) {
3193
- buf.writeBigUInt64LE(v, off);
3194
- } else {
3195
- buf.writeUInt32LE(Number(v), off);
3196
- }
3197
- } else {
3198
- const v = typeof val === "bigint" ? val : BigInt(val || 0);
3199
- if (native.POINTER_SIZE === 8) {
3200
- buf.writeBigUInt64LE(v, off);
3201
- } else {
3202
- buf.writeUInt32LE(Number(v), off);
3203
- }
3204
- }
3205
- };
3206
- }
3207
-
3208
- class c_wchar_p extends SimpleCData {
3209
- static _size = native.POINTER_SIZE;
3210
- static _type = "wchar_p";
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
- };
3216
- static _writer = (buf, off, val) => {
3217
- if (typeof val === "string") {
3218
- val = wstring(val);
3219
- }
3220
- if (Buffer.isBuffer(val)) {
3221
- const addr = addressOf(val);
3222
- const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
3223
- if (native.POINTER_SIZE === 8) {
3224
- buf.writeBigUInt64LE(v, off);
3225
- } else {
3226
- buf.writeUInt32LE(Number(v), off);
3227
- }
3228
- } else {
3229
- const v = typeof val === "bigint" ? val : BigInt(val || 0);
3230
- if (native.POINTER_SIZE === 8) {
3231
- buf.writeBigUInt64LE(v, off);
3232
- } else {
3233
- buf.writeUInt32LE(Number(v), off);
3234
- }
3235
- }
3236
- };
3237
- }
414
+ // Create all primitive type classes
415
+ const primitiveTypes = createPrimitiveTypes(
416
+ SimpleCData,
417
+ native,
418
+ addressOf,
419
+ cstring,
420
+ wstring,
421
+ readCString,
422
+ readWString
423
+ );
424
+
425
+ // Destructure all types for easy access
426
+ const {
427
+ c_void,
428
+ c_int8, c_int16, c_int32, c_int64,
429
+ c_uint8, c_uint16, c_uint32, c_uint64,
430
+ c_float, c_double,
431
+ c_bool,
432
+ c_char, c_wchar,
433
+ c_void_p, c_char_p, c_wchar_p,
434
+ c_size_t, c_long, c_ulong,
435
+ c_byte, c_ubyte, c_short, c_ushort,
436
+ c_int, c_uint, c_longlong, c_ulonglong,
437
+ } = primitiveTypes;
3238
438
 
3239
439
  // ============================================================================
3240
- // Python-compatible aliases
440
+ // Alias Python-compatibili per Memory Management
3241
441
  // ============================================================================
3242
442
 
3243
- const c_byte = c_int8;
3244
- const c_ubyte = c_uint8;
3245
- const c_short = c_int16;
3246
- const c_ushort = c_uint16;
3247
- const c_int = c_int32;
3248
- const c_uint = c_uint32;
3249
- const c_longlong = c_int64;
3250
- const c_ulonglong = c_uint64;
3251
-
3252
443
  // ============================================================================
3253
- // Alias Python-compatibili per Memory Management
444
+ // Memory Management Functions
445
+ // Now imported from ./memory/buffer.js - see that file for implementation details
3254
446
  // ============================================================================
3255
447
 
3256
- /**
3257
- * Crea un buffer di stringa (compatibile con Python ctypes)
3258
- * @param {number|string|Buffer} init - Dimensione in bytes, stringa, o bytes
3259
- * @returns {Buffer} Buffer allocato
3260
- */
3261
448
  function create_string_buffer(init) {
3262
- if (typeof init === "number") {
3263
- // create_string_buffer(size) - alloca buffer vuoto
3264
- return alloc(init);
3265
- } else if (typeof init === "string") {
3266
- // create_string_buffer("string") - crea buffer con stringa
3267
- return cstring(init);
3268
- } else if (Buffer.isBuffer(init)) {
3269
- // create_string_buffer(bytes) - copia bytes
3270
- const buf = alloc(init.length + 1);
3271
- init.copy(buf);
3272
- buf[init.length] = 0; // null terminator
3273
- return buf;
3274
- }
3275
- throw new TypeError("create_string_buffer requires number, string, or Buffer");
449
+ return _create_string_buffer(init, native);
3276
450
  }
3277
451
 
3278
- /**
3279
- * Crea un buffer di stringa Unicode (compatibile con Python ctypes)
3280
- * @param {number|string} init - Dimensione in caratteri o stringa
3281
- * @returns {Buffer} Buffer allocato con stringa wide
3282
- */
3283
452
  function create_unicode_buffer(init) {
3284
- const wcharSize = native.WCHAR_SIZE; // 2 su Windows, 4 su Unix
3285
-
3286
- if (typeof init === "number") {
3287
- // create_unicode_buffer(size) - alloca buffer vuoto per N caratteri
3288
- return alloc(init * wcharSize);
3289
- } else if (typeof init === "string") {
3290
- // create_unicode_buffer("string") - crea buffer con stringa wide
3291
- return wstring(init);
3292
- }
3293
- throw new TypeError("create_unicode_buffer requires number or string");
453
+ return _create_unicode_buffer(init, native);
3294
454
  }
3295
455
 
3296
- /**
3297
- * Legge una stringa da un indirizzo di memoria (compatibile con Python ctypes)
3298
- * @param {Buffer|BigInt|number} address - Indirizzo di memoria
3299
- * @param {number} [size] - Numero di bytes da leggere (default: fino a null)
3300
- * @returns {string|null} Stringa letta
3301
- */
3302
456
  function string_at(address, size) {
3303
- return readCString(address, size);
457
+ return _string_at(address, size, native);
3304
458
  }
3305
459
 
3306
- /**
3307
- * Legge una stringa wide da un indirizzo di memoria (compatibile con Python ctypes)
3308
- * @param {Buffer|BigInt|number} address - Indirizzo di memoria
3309
- * @param {number} [size] - Numero di caratteri wide da leggere
3310
- * @returns {string|null} Stringa letta
3311
- */
3312
460
  function wstring_at(address, size) {
3313
- return readWString(address, size);
461
+ return _wstring_at(address, size, native);
3314
462
  }
3315
463
 
3316
464
  // Alias lowercase per compatibilità Python (addressof invece di addressOf)
3317
465
  const addressof = addressOf;
3318
466
 
3319
- /**
3320
- * Copia memoria da sorgente a destinazione (come memmove in C)
3321
- * @param {Buffer} dst - Buffer destinazione
3322
- * @param {Buffer|BigInt} src - Buffer o indirizzo sorgente
3323
- * @param {number} count - Numero di bytes da copiare
3324
- */
3325
467
  function memmove(dst, src, count) {
3326
- if (!Buffer.isBuffer(dst)) {
3327
- throw new TypeError("dst must be a Buffer");
3328
- }
3329
-
3330
- let srcBuf;
3331
- if (Buffer.isBuffer(src)) {
3332
- srcBuf = src;
3333
- } else {
3334
- srcBuf = ptrToBuffer(src, count);
3335
- }
3336
-
3337
- srcBuf.copy(dst, 0, 0, count);
468
+ return _memmove(dst, src, count, native);
3338
469
  }
3339
470
 
3340
- /**
3341
- * Riempie memoria con un valore (come memset in C)
3342
- * @param {Buffer} dst - Buffer destinazione
3343
- * @param {number} value - Valore byte (0-255)
3344
- * @param {number} count - Numero di bytes da riempire
3345
- */
3346
471
  function memset(dst, value, count) {
3347
- if (!Buffer.isBuffer(dst)) {
3348
- throw new TypeError("dst must be a Buffer");
3349
- }
3350
- dst.fill(value, 0, count);
472
+ return _memset(dst, value, count);
3351
473
  }
3352
474
 
3353
475
  // Export come ES module
@@ -3413,8 +535,7 @@ export {
3413
535
  // SimpleCData base class
3414
536
  SimpleCData,
3415
537
 
3416
- // Tipi (classes, now instantiable)
3417
- types,
538
+ // Python-compatible type classes
3418
539
  c_void,
3419
540
  c_int,
3420
541
  c_uint,
@@ -3446,7 +567,5 @@ export {
3446
567
  c_ulonglong,
3447
568
  };
3448
569
 
3449
- // Costanti esportate
3450
- export const POINTER_SIZE = native.POINTER_SIZE;
3451
- export const WCHAR_SIZE = native.WCHAR_SIZE;
3452
- export const NULL = null;
570
+ // Re-export constants from platform module
571
+ export { POINTER_SIZE, WCHAR_SIZE, NULL } from "./platform/constants.js";