node-ctypes 1.5.0 → 1.7.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.
- package/README.md +149 -777
- 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/index.d.ts +1140 -58
- package/lib/index.js +24 -0
- package/lib/memory/pointer.js +33 -0
- package/lib/structures/helpers/struct.js +88 -0
- package/lib/structures/helpers/union.js +7 -0
- package/lib/types/primitives.js +16 -0
- package/package.json +7 -3
package/lib/index.js
CHANGED
|
@@ -207,6 +207,27 @@ function sizeof(type) {
|
|
|
207
207
|
return _sizeof(type, Structure, Union);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
function alignment(type) {
|
|
211
|
+
// Structure/Union class
|
|
212
|
+
if (typeof type === "function" && (type.prototype instanceof Structure || type.prototype instanceof Union)) {
|
|
213
|
+
const def = type._structDef || type._unionDef || type._buildStruct?.() || type._buildUnion?.();
|
|
214
|
+
return def ? def.alignment : 1;
|
|
215
|
+
}
|
|
216
|
+
// StructDef / UnionDef object
|
|
217
|
+
if (typeof type === "object" && type !== null && type._isStructType && type.alignment !== undefined) {
|
|
218
|
+
return type.alignment;
|
|
219
|
+
}
|
|
220
|
+
// ArrayTypeDef
|
|
221
|
+
if (typeof type === "object" && type !== null && type._isArrayType) {
|
|
222
|
+
return type.getAlignment();
|
|
223
|
+
}
|
|
224
|
+
// SimpleCData class — natural alignment = min(size, POINTER_SIZE)
|
|
225
|
+
if (typeof type === "function" && type._isSimpleCData) {
|
|
226
|
+
return Math.min(type._size, native.POINTER_SIZE);
|
|
227
|
+
}
|
|
228
|
+
throw new TypeError("alignment() argument must be a ctypes type");
|
|
229
|
+
}
|
|
230
|
+
|
|
210
231
|
// ============================================================================
|
|
211
232
|
// Pointer Operations
|
|
212
233
|
// Now imported from ./memory/pointer.js - see that file for implementation details
|
|
@@ -425,6 +446,7 @@ const {
|
|
|
425
446
|
c_char_p,
|
|
426
447
|
c_wchar_p,
|
|
427
448
|
c_size_t,
|
|
449
|
+
c_ssize_t,
|
|
428
450
|
c_long,
|
|
429
451
|
c_ulong,
|
|
430
452
|
c_byte,
|
|
@@ -505,6 +527,7 @@ export {
|
|
|
505
527
|
readValue,
|
|
506
528
|
writeValue,
|
|
507
529
|
sizeof,
|
|
530
|
+
alignment,
|
|
508
531
|
ptrToBuffer,
|
|
509
532
|
|
|
510
533
|
// Strutture
|
|
@@ -560,6 +583,7 @@ export {
|
|
|
560
583
|
c_void_p,
|
|
561
584
|
c_bool,
|
|
562
585
|
c_size_t,
|
|
586
|
+
c_ssize_t,
|
|
563
587
|
c_long,
|
|
564
588
|
c_ulong,
|
|
565
589
|
// Python-compatible aliases
|
package/lib/memory/pointer.js
CHANGED
|
@@ -173,6 +173,39 @@ export function cast(ptr, targetType, readValue, sizeof, ptrToBuffer) {
|
|
|
173
173
|
throw new TypeError("cast() requires a target type");
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
// PointerTypeDef — return a PointerInstance via fromAddress()
|
|
177
|
+
// Python equivalent: cast(c_void_p(addr), POINTER(MyStruct))
|
|
178
|
+
if (typeof targetType === "object" && targetType._pointerTo !== undefined) {
|
|
179
|
+
let addr;
|
|
180
|
+
if (typeof ptr === "bigint") {
|
|
181
|
+
addr = ptr;
|
|
182
|
+
} else if (typeof ptr === "number") {
|
|
183
|
+
addr = BigInt(ptr);
|
|
184
|
+
} else if (Buffer.isBuffer(ptr)) {
|
|
185
|
+
// Read the pointer value from the buffer
|
|
186
|
+
if (ptr.length >= 8) {
|
|
187
|
+
addr = ptr.readBigUInt64LE(0);
|
|
188
|
+
} else if (ptr.length >= 4) {
|
|
189
|
+
addr = BigInt(ptr.readUInt32LE(0));
|
|
190
|
+
} else {
|
|
191
|
+
throw new TypeError("cast(): buffer too small to contain a pointer");
|
|
192
|
+
}
|
|
193
|
+
} else if (ptr && ptr._buffer) {
|
|
194
|
+
// SimpleCData instance (e.g., c_void_p)
|
|
195
|
+
const buf = ptr._buffer;
|
|
196
|
+
if (buf.length >= 8) {
|
|
197
|
+
addr = buf.readBigUInt64LE(0);
|
|
198
|
+
} else if (buf.length >= 4) {
|
|
199
|
+
addr = BigInt(buf.readUInt32LE(0));
|
|
200
|
+
} else {
|
|
201
|
+
throw new TypeError("cast(): buffer too small to contain a pointer");
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
throw new TypeError("cast(): ptr must be a BigInt, number, Buffer, or ctypes instance");
|
|
205
|
+
}
|
|
206
|
+
return targetType.fromAddress(addr);
|
|
207
|
+
}
|
|
208
|
+
|
|
176
209
|
// Struct type - return wrapper object
|
|
177
210
|
if (typeof targetType === "object" && targetType.size !== undefined) {
|
|
178
211
|
const buf = Buffer.isBuffer(ptr) ? ptr : ptrToBuffer(ptr, targetType.size);
|
|
@@ -307,6 +307,34 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
307
307
|
maxAlignment = alignment;
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
|
+
// Caso 4b: POINTER() type — stored as pointer-sized field with typed access
|
|
311
|
+
else if (typeof type === "object" && type !== null && type._pointerTo !== undefined) {
|
|
312
|
+
// Reset bit field state
|
|
313
|
+
currentBitFieldBase = null;
|
|
314
|
+
currentBitOffset = 0;
|
|
315
|
+
|
|
316
|
+
const size = native.POINTER_SIZE;
|
|
317
|
+
const alignment = packed ? 1 : Math.min(size, native.POINTER_SIZE);
|
|
318
|
+
|
|
319
|
+
if (!packed && totalSize % alignment !== 0) {
|
|
320
|
+
totalSize += alignment - (totalSize % alignment);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
fieldDef = {
|
|
324
|
+
name,
|
|
325
|
+
type,
|
|
326
|
+
offset: totalSize,
|
|
327
|
+
size,
|
|
328
|
+
alignment,
|
|
329
|
+
isPointer: true,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
totalSize += size;
|
|
333
|
+
|
|
334
|
+
if (alignment > maxAlignment) {
|
|
335
|
+
maxAlignment = alignment;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
310
338
|
// Caso 4: Tipo base (SimpleCData)
|
|
311
339
|
else {
|
|
312
340
|
// Reset bit field state
|
|
@@ -369,6 +397,37 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
369
397
|
continue;
|
|
370
398
|
}
|
|
371
399
|
|
|
400
|
+
// POINTER() type field — read returns PointerInstance, write accepts BigInt/PointerInstance
|
|
401
|
+
if (field.isPointer) {
|
|
402
|
+
const fieldOffset = offset;
|
|
403
|
+
const ptrType = field.type;
|
|
404
|
+
fieldReaders.set(name, (buf) => {
|
|
405
|
+
const addr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(fieldOffset) : BigInt(buf.readUInt32LE(fieldOffset));
|
|
406
|
+
return ptrType.fromAddress(addr);
|
|
407
|
+
});
|
|
408
|
+
fieldWriters.set(name, (buf, val) => {
|
|
409
|
+
let addr;
|
|
410
|
+
if (typeof val === "bigint") {
|
|
411
|
+
addr = val;
|
|
412
|
+
} else if (typeof val === "number") {
|
|
413
|
+
addr = BigInt(val);
|
|
414
|
+
} else if (val && typeof val.address === "bigint") {
|
|
415
|
+
// PointerInstance
|
|
416
|
+
addr = val.address;
|
|
417
|
+
} else if (Buffer.isBuffer(val)) {
|
|
418
|
+
addr = native.POINTER_SIZE === 8 ? val.readBigUInt64LE(0) : BigInt(val.readUInt32LE(0));
|
|
419
|
+
} else {
|
|
420
|
+
addr = 0n;
|
|
421
|
+
}
|
|
422
|
+
if (native.POINTER_SIZE === 8) {
|
|
423
|
+
buf.writeBigUInt64LE(addr, fieldOffset);
|
|
424
|
+
} else {
|
|
425
|
+
buf.writeUInt32LE(Number(addr), fieldOffset);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
|
|
372
431
|
// Pre-compile based on SimpleCData type
|
|
373
432
|
// Use the type's _reader and _writer directly for optimization
|
|
374
433
|
const fieldType = field.type;
|
|
@@ -662,6 +721,12 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
662
721
|
: field.type.toObject(nestedBuf);
|
|
663
722
|
}
|
|
664
723
|
|
|
724
|
+
// POINTER() field — return PointerInstance
|
|
725
|
+
if (field.isPointer) {
|
|
726
|
+
const addr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(field.offset) : BigInt(buf.readUInt32LE(field.offset));
|
|
727
|
+
return field.type.fromAddress(addr);
|
|
728
|
+
}
|
|
729
|
+
|
|
665
730
|
// Array field
|
|
666
731
|
if (field.isArray) {
|
|
667
732
|
const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
|
|
@@ -748,6 +813,26 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
748
813
|
return;
|
|
749
814
|
}
|
|
750
815
|
|
|
816
|
+
// POINTER() field — write address
|
|
817
|
+
if (field.isPointer) {
|
|
818
|
+
let addr;
|
|
819
|
+
if (typeof value === "bigint") {
|
|
820
|
+
addr = value;
|
|
821
|
+
} else if (typeof value === "number") {
|
|
822
|
+
addr = BigInt(value);
|
|
823
|
+
} else if (value && typeof value.address === "bigint") {
|
|
824
|
+
addr = value.address;
|
|
825
|
+
} else {
|
|
826
|
+
addr = 0n;
|
|
827
|
+
}
|
|
828
|
+
if (native.POINTER_SIZE === 8) {
|
|
829
|
+
buf.writeBigUInt64LE(addr, field.offset);
|
|
830
|
+
} else {
|
|
831
|
+
buf.writeUInt32LE(Number(addr), field.offset);
|
|
832
|
+
}
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
|
|
751
836
|
// Array field
|
|
752
837
|
if (field.isArray) {
|
|
753
838
|
if (Buffer.isBuffer(value)) {
|
|
@@ -875,6 +960,9 @@ export function struct(fields, options = {}, sizeof, alloc, readValue, writeValu
|
|
|
875
960
|
} else {
|
|
876
961
|
result[field.name] = nestedObj;
|
|
877
962
|
}
|
|
963
|
+
} else if (field.isPointer) {
|
|
964
|
+
const addr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(field.offset) : BigInt(buf.readUInt32LE(field.offset));
|
|
965
|
+
result[field.name] = field.type.fromAddress(addr);
|
|
878
966
|
} else if (field.isArray) {
|
|
879
967
|
const arrayBuf = buf.subarray(field.offset, field.offset + field.size);
|
|
880
968
|
result[field.name] = field.type.wrap(arrayBuf);
|
|
@@ -123,6 +123,13 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
|
|
|
123
123
|
fieldDef.baseSize = type.baseSize;
|
|
124
124
|
fieldDef.type = type.baseType; // Usa il tipo base per lettura/scrittura
|
|
125
125
|
}
|
|
126
|
+
// POINTER() type
|
|
127
|
+
else if (typeof type === "object" && type !== null && type._pointerTo !== undefined) {
|
|
128
|
+
size = native.POINTER_SIZE;
|
|
129
|
+
alignment = Math.min(size, native.POINTER_SIZE);
|
|
130
|
+
fieldDef.type = type;
|
|
131
|
+
fieldDef.isPointer = true;
|
|
132
|
+
}
|
|
126
133
|
// Tipo base (SimpleCData)
|
|
127
134
|
else {
|
|
128
135
|
// Validate type is a SimpleCData class
|
package/lib/types/primitives.js
CHANGED
|
@@ -284,6 +284,21 @@ export function createPrimitiveTypes(SimpleCData, native, addressOf, cstring, ws
|
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
class c_ssize_t extends SimpleCData {
|
|
288
|
+
static _size = native.POINTER_SIZE;
|
|
289
|
+
static _type = CType.SSIZE_T;
|
|
290
|
+
static _reader = (buf, off) => {
|
|
291
|
+
return native.POINTER_SIZE === 8 ? buf.readBigInt64LE(off) : buf.readInt32LE(off);
|
|
292
|
+
};
|
|
293
|
+
static _writer = (buf, off, val) => {
|
|
294
|
+
if (native.POINTER_SIZE === 8) {
|
|
295
|
+
buf.writeBigInt64LE(BigInt(val), off);
|
|
296
|
+
} else {
|
|
297
|
+
buf.writeInt32LE(Number(val), off);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
287
302
|
// Get actual long size from native module (4 on Windows, 4 or 8 on Unix depending on arch)
|
|
288
303
|
const _longSize = native.sizeof ? native.sizeof(CType.LONG) : 4;
|
|
289
304
|
|
|
@@ -408,6 +423,7 @@ export function createPrimitiveTypes(SimpleCData, native, addressOf, cstring, ws
|
|
|
408
423
|
c_wchar_p,
|
|
409
424
|
// Platform-specific
|
|
410
425
|
c_size_t,
|
|
426
|
+
c_ssize_t,
|
|
411
427
|
c_long,
|
|
412
428
|
c_ulong,
|
|
413
429
|
// Python-compatible aliases
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-ctypes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Python ctypes-like FFI for Node.js using libffi",
|
|
5
5
|
"author": "Damiano Mazzella",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,11 +29,15 @@
|
|
|
29
29
|
"build:debug": "cmake-js compile --target ctypes --debug",
|
|
30
30
|
"rebuild": "cmake-js rebuild --target ctypes",
|
|
31
31
|
"clean": "cmake-js clean",
|
|
32
|
-
"generate-ffi-headers": "node third-party/libffi_cmake/generate-headers.js"
|
|
32
|
+
"generate-ffi-headers": "node third-party/libffi_cmake/generate-headers.js",
|
|
33
|
+
"docs": "typedoc",
|
|
34
|
+
"docs:serve": "typedoc && npx serve docs"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
37
|
+
"@types/node": "^25.3.3",
|
|
35
38
|
"cmake-js": "^8.0.0",
|
|
36
|
-
"node-addon-api": "^8.
|
|
39
|
+
"node-addon-api": "^8.6.0",
|
|
40
|
+
"typedoc": "^0.28.17"
|
|
37
41
|
},
|
|
38
42
|
"engines": {
|
|
39
43
|
"node": ">=16.0.0"
|