node-ctypes 0.1.3

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