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/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
@@ -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
@@ -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.5.0",
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.5.0"
39
+ "node-addon-api": "^8.6.0",
40
+ "typedoc": "^0.28.17"
37
41
  },
38
42
  "engines": {
39
43
  "node": ">=16.0.0"