node-ctypes 1.7.0 → 1.8.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.
Binary file
Binary file
package/lib/index.js CHANGED
@@ -208,6 +208,15 @@ function sizeof(type) {
208
208
  }
209
209
 
210
210
  function alignment(type) {
211
+ // Instance of Structure/Union — use _structDef/_unionDef (Python: alignment(instance) works)
212
+ if (type instanceof Structure || type instanceof Union) {
213
+ const def = type._structDef || type._unionDef;
214
+ return def ? def.alignment : 1;
215
+ }
216
+ // SimpleCData instance — delegate to class (Python: alignment(c_int32(42)) works)
217
+ if (typeof type === "object" && type !== null && type.constructor && type.constructor._isSimpleCData) {
218
+ return alignment(type.constructor);
219
+ }
211
220
  // Structure/Union class
212
221
  if (typeof type === "function" && (type.prototype instanceof Structure || type.prototype instanceof Union)) {
213
222
  const def = type._structDef || type._unionDef || type._buildStruct?.() || type._buildUnion?.();
@@ -242,7 +251,7 @@ function byref(obj) {
242
251
  }
243
252
 
244
253
  function cast(ptr, targetType) {
245
- return _cast(ptr, targetType, readValue, sizeof, ptrToBuffer);
254
+ return _cast(ptr, targetType, readValue, sizeof, ptrToBuffer, native);
246
255
  }
247
256
 
248
257
  function ptrToBuffer(address, size) {
@@ -119,6 +119,12 @@ export function readValue(ptr, type, offset = 0, sizeof, ptrToBuffer, Structure,
119
119
  return def.toObject(tempBuf);
120
120
  }
121
121
 
122
+ // Struct/Union def object (has _isStructType and toObject)
123
+ if (typeof type === "object" && type !== null && type._isStructType && typeof type.toObject === "function") {
124
+ const tempBuf = ptr.subarray(offset, offset + type.size);
125
+ return type.toObject(tempBuf);
126
+ }
127
+
122
128
  throw new TypeError(`readValue: unsupported type ${type}`);
123
129
  }
124
130
 
@@ -211,6 +217,19 @@ export function writeValue(ptr, type, value, offset = 0, sizeof, Structure, Unio
211
217
  return def.size;
212
218
  }
213
219
 
220
+ // Struct/Union def object (has _isStructType and set/fromObject)
221
+ if (typeof type === "object" && type !== null && type._isStructType && typeof type.set === "function") {
222
+ const tempBuf = ptr.subarray(offset, offset + type.size);
223
+ if (Buffer.isBuffer(value)) {
224
+ value.copy(tempBuf, 0, 0, type.size);
225
+ } else if (typeof value === "object" && value !== null) {
226
+ for (const [k, v] of Object.entries(value)) {
227
+ type.set(tempBuf, k, v);
228
+ }
229
+ }
230
+ return type.size;
231
+ }
232
+
214
233
  throw new TypeError(`writeValue: unsupported type ${type}`);
215
234
  }
216
235
 
@@ -53,6 +53,10 @@
53
53
  * ```
54
54
  */
55
55
  export function addressOf(ptr, alloc, native) {
56
+ // If it's an object with _buffer (SimpleCData, Structure, Union, array proxy), use its buffer
57
+ if (!Buffer.isBuffer(ptr) && ptr && ptr._buffer && Buffer.isBuffer(ptr._buffer)) {
58
+ ptr = ptr._buffer;
59
+ }
56
60
  if (Buffer.isBuffer(ptr)) {
57
61
  // Get the buffer's memory address
58
62
  const tempBuf = alloc(native.POINTER_SIZE);
@@ -164,7 +168,7 @@ export function byref(obj) {
164
168
  * console.log(header.contents); // { magic: 23117, version: 1 }
165
169
  * ```
166
170
  */
167
- export function cast(ptr, targetType, readValue, sizeof, ptrToBuffer) {
171
+ export function cast(ptr, targetType, readValue, sizeof, ptrToBuffer, native) {
168
172
  // Validate inputs
169
173
  if (!ptr) {
170
174
  throw new TypeError("cast() requires a non-null pointer");
@@ -173,9 +177,42 @@ export function cast(ptr, targetType, readValue, sizeof, ptrToBuffer) {
173
177
  throw new TypeError("cast() requires a target type");
174
178
  }
175
179
 
176
- // PointerTypeDef — return a PointerInstance via fromAddress()
177
- // Python equivalent: cast(c_void_p(addr), POINTER(MyStruct))
180
+ // PointerTypeDef — return a PointerInstance via fromAddress() or fromBuffer()
181
+ // Python equivalent: cast(c_void_p(addr), POINTER(c_int32)) — read stored address
182
+ // Python equivalent: cast(arr, POINTER(c_int32)) — pointer to arr's memory
183
+ // Python equivalent: cast(p, POINTER(c_double)) — pointer to same memory as p
178
184
  if (typeof targetType === "object" && targetType._pointerTo !== undefined) {
185
+ // PointerInstance — use its address/buffer (Python: cast(p, POINTER(OtherType)))
186
+ if (ptr && typeof ptr === "object" && typeof ptr.address === "bigint") {
187
+ if (ptr._buffer) {
188
+ return targetType.fromBuffer(ptr._buffer);
189
+ }
190
+ return targetType.fromAddress(ptr.address);
191
+ }
192
+
193
+ // ctypes object with _buffer — distinguish data objects from pointer-value objects
194
+ // Note: array proxies are Buffer.isBuffer()==true but also have ._buffer, so don't exclude Buffers
195
+ if (ptr && typeof ptr === "object" && ptr._buffer && Buffer.isBuffer(ptr._buffer)) {
196
+ // SimpleCData storing a pointer/address value (c_void_p, c_char_p, etc.)
197
+ // → read the stored address value from the buffer
198
+ if (ptr.constructor && ptr.constructor._isSimpleCData && ptr.constructor._type === native.CType.POINTER) {
199
+ // This is a pointer-valued SimpleCData — read address from buffer
200
+ const buf = ptr._buffer;
201
+ let addr;
202
+ if (buf.length >= 8) {
203
+ addr = buf.readBigUInt64LE(0);
204
+ } else if (buf.length >= 4) {
205
+ addr = BigInt(buf.readUInt32LE(0));
206
+ } else {
207
+ throw new TypeError("cast(): buffer too small to contain a pointer");
208
+ }
209
+ return targetType.fromAddress(addr);
210
+ }
211
+ // Array, Structure, Union — pointer to their data buffer
212
+ // Python: cast(arr, POINTER(c_int32)) points to arr's memory
213
+ return targetType.fromBuffer(ptr._buffer);
214
+ }
215
+
179
216
  let addr;
180
217
  if (typeof ptr === "bigint") {
181
218
  addr = ptr;
@@ -190,18 +227,8 @@ export function cast(ptr, targetType, readValue, sizeof, ptrToBuffer) {
190
227
  } else {
191
228
  throw new TypeError("cast(): buffer too small to contain a pointer");
192
229
  }
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
230
  } else {
204
- throw new TypeError("cast(): ptr must be a BigInt, number, Buffer, or ctypes instance");
231
+ throw new TypeError("cast(): ptr must be a BigInt, number, Buffer, ctypes instance, or PointerInstance");
205
232
  }
206
233
  return targetType.fromAddress(addr);
207
234
  }
@@ -320,7 +347,7 @@ export function ptrToBuffer(address, size, native) {
320
347
  */
321
348
  export function POINTER(baseType, sizeof, alloc, readValue, writeValue, c_void_p, native) {
322
349
  const baseSize = typeof baseType === "object" ? baseType.size : sizeof(baseType);
323
- const typeName = typeof baseType === "object" ? "struct" : baseType._type || "unknown";
350
+ const typeName = typeof baseType === "object" ? "struct" : baseType._type || baseType.name || "unknown";
324
351
 
325
352
  /**
326
353
  * Helper to get buffer's memory address using native writeValue
@@ -591,13 +618,16 @@ export function pointer(obj, POINTER_fn, sizeof, alloc, readValue, writeValue, c
591
618
  } else if (obj && obj._buffer) {
592
619
  // Structure/Union instance or SimpleCData with _buffer
593
620
  targetBuffer = obj._buffer;
594
- // Get the base type - use constructor for SimpleCData, _structDef for structures
621
+ // Get the base type for POINTER creation
595
622
  if (obj.constructor && obj.constructor._type) {
596
- // SimpleCData instance - use the class itself
623
+ // SimpleCData instance - use the class itself (c_int32, c_double, etc.)
597
624
  baseType = obj.constructor;
625
+ } else if (obj._structDef) {
626
+ // Structure/Union instance - use the struct def object
627
+ // (obj.constructor is a bound function from Proxy, so we use _structDef directly)
628
+ baseType = obj._structDef;
598
629
  } else {
599
- // Structure/Union instance
600
- baseType = obj._structDef || obj.__structDef || c_void_p;
630
+ baseType = c_void_p;
601
631
  }
602
632
  } else {
603
633
  throw new TypeError("pointer() argument must be a ctypes instance or Buffer");
@@ -320,6 +320,12 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
320
320
  return field.type.wrap(buf.subarray(field.offset, field.offset + field.size));
321
321
  }
322
322
 
323
+ // POINTER() field — return PointerInstance
324
+ if (field.isPointer) {
325
+ const addr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(field.offset) : BigInt(buf.readUInt32LE(field.offset));
326
+ return field.type.fromAddress(addr);
327
+ }
328
+
323
329
  // Tipo base
324
330
  return readValue(buf, field.type, field.offset);
325
331
  },
@@ -376,6 +382,28 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
376
382
  return;
377
383
  }
378
384
 
385
+ // POINTER() field — write address
386
+ if (field.isPointer) {
387
+ let addr;
388
+ if (typeof value === "bigint") {
389
+ addr = value;
390
+ } else if (typeof value === "number") {
391
+ addr = BigInt(value);
392
+ } else if (value && typeof value.address === "bigint") {
393
+ addr = value.address;
394
+ } else if (Buffer.isBuffer(value)) {
395
+ addr = native.POINTER_SIZE === 8 ? value.readBigUInt64LE(0) : BigInt(value.readUInt32LE(0));
396
+ } else {
397
+ addr = 0n;
398
+ }
399
+ if (native.POINTER_SIZE === 8) {
400
+ buf.writeBigUInt64LE(addr, field.offset);
401
+ } else {
402
+ buf.writeUInt32LE(Number(addr), field.offset);
403
+ }
404
+ return;
405
+ }
406
+
379
407
  // Tipo base
380
408
  writeValue(buf, field.type, value, field.offset);
381
409
  },
@@ -429,6 +457,9 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
429
457
  } else if (field.isArray) {
430
458
  const wrapped = field.type.wrap(buf.subarray(0, field.size));
431
459
  return [...wrapped];
460
+ } else if (field.isPointer) {
461
+ const addr = native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(0) : BigInt(buf.readUInt32LE(0));
462
+ return field.type.fromAddress(addr);
432
463
  } else {
433
464
  return readValue(buf, field.type, 0);
434
465
  }
@@ -436,6 +467,24 @@ export function union(fields, sizeof, alloc, readValue, writeValue, _isStruct, _
436
467
  set(value) {
437
468
  if (field.isBitField) {
438
469
  _writeBitField(buf, 0, field.type, field.bitOffset, field.bitSize, value, sizeof);
470
+ } else if (field.isPointer) {
471
+ let addr;
472
+ if (typeof value === "bigint") {
473
+ addr = value;
474
+ } else if (typeof value === "number") {
475
+ addr = BigInt(value);
476
+ } else if (value && typeof value.address === "bigint") {
477
+ addr = value.address;
478
+ } else if (Buffer.isBuffer(value)) {
479
+ addr = native.POINTER_SIZE === 8 ? value.readBigUInt64LE(0) : BigInt(value.readUInt32LE(0));
480
+ } else {
481
+ addr = 0n;
482
+ }
483
+ if (native.POINTER_SIZE === 8) {
484
+ buf.writeBigUInt64LE(addr, 0);
485
+ } else {
486
+ buf.writeUInt32LE(Number(addr), 0);
487
+ }
439
488
  } else {
440
489
  writeValue(buf, field.type, value, 0);
441
490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-ctypes",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Python ctypes-like FFI for Node.js using libffi",
5
5
  "author": "Damiano Mazzella",
6
6
  "license": "MIT",