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