node-ctypes 1.1.0 → 1.3.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.

@@ -56,8 +56,7 @@ export function addressOf(ptr, alloc, native) {
56
56
  if (Buffer.isBuffer(ptr)) {
57
57
  // Get the buffer's memory address
58
58
  const tempBuf = alloc(native.POINTER_SIZE);
59
- // Use native.writeValue with string 'pointer' to avoid JS-level recursion
60
- native.writeValue(tempBuf, "pointer", ptr, 0);
59
+ native.writeValue(tempBuf, native.CType.POINTER, ptr, 0);
61
60
  if (native.POINTER_SIZE === 8) return tempBuf.readBigUInt64LE(0);
62
61
  return BigInt(tempBuf.readUInt32LE(0));
63
62
  }
@@ -290,54 +289,225 @@ export function POINTER(baseType, sizeof, alloc, readValue, writeValue, c_void_p
290
289
  const baseSize = typeof baseType === "object" ? baseType.size : sizeof(baseType);
291
290
  const typeName = typeof baseType === "object" ? "struct" : baseType._type || "unknown";
292
291
 
293
- return {
292
+ /**
293
+ * Helper to get buffer's memory address using native writeValue
294
+ */
295
+ function getBufferAddress(buf) {
296
+ const tempBuf = alloc(native.POINTER_SIZE);
297
+ native.writeValue(tempBuf, native.CType.POINTER, buf, 0);
298
+ if (native.POINTER_SIZE === 8) return tempBuf.readBigUInt64LE(0);
299
+ return BigInt(tempBuf.readUInt32LE(0));
300
+ }
301
+
302
+ /**
303
+ * Creates a pointer instance with Python ctypes-compatible API.
304
+ * Supports .contents property and [index] access for pointer arithmetic.
305
+ */
306
+ function createPointerInstance(addressOrBuffer) {
307
+ let _address = 0n;
308
+ let _buffer = null;
309
+
310
+ if (addressOrBuffer === undefined || addressOrBuffer === null) {
311
+ _address = 0n;
312
+ } else if (typeof addressOrBuffer === "bigint") {
313
+ _address = addressOrBuffer;
314
+ } else if (typeof addressOrBuffer === "number") {
315
+ _address = BigInt(addressOrBuffer);
316
+ } else if (Buffer.isBuffer(addressOrBuffer)) {
317
+ // This is a buffer we're pointing TO
318
+ _buffer = addressOrBuffer;
319
+ _address = getBufferAddress(addressOrBuffer);
320
+ }
321
+
322
+ const instance = {
323
+ _pointerType: PointerType,
324
+ _baseType: baseType,
325
+ _baseSize: baseSize,
326
+
327
+ /**
328
+ * Get the raw address as BigInt
329
+ */
330
+ get address() {
331
+ return _address;
332
+ },
333
+
334
+ /**
335
+ * Python ctypes compatible: get/set the value at the pointed location.
336
+ * In Python: p.contents returns the dereferenced value.
337
+ */
338
+ get contents() {
339
+ if (_address === 0n && !_buffer) {
340
+ throw new Error("NULL pointer access");
341
+ }
342
+ if (_buffer) {
343
+ // We have the actual buffer, read from it
344
+ return readValue(_buffer, baseType, 0);
345
+ }
346
+ // We only have address - need native support to read
347
+ const buf = native.ptrToBuffer(_address, baseSize);
348
+ return readValue(buf, baseType, 0);
349
+ },
350
+
351
+ set contents(value) {
352
+ if (_address === 0n && !_buffer) {
353
+ throw new Error("NULL pointer access");
354
+ }
355
+ if (_buffer) {
356
+ writeValue(_buffer, baseType, value, 0);
357
+ } else {
358
+ const buf = native.ptrToBuffer(_address, baseSize);
359
+ writeValue(buf, baseType, value, 0);
360
+ }
361
+ },
362
+
363
+ /**
364
+ * Returns the buffer this pointer points to (if available)
365
+ */
366
+ get _buffer() {
367
+ return _buffer;
368
+ },
369
+
370
+ /**
371
+ * Dereference - alias for contents getter
372
+ */
373
+ deref() {
374
+ return this.contents;
375
+ },
376
+
377
+ /**
378
+ * Set pointer to point to a new address/buffer
379
+ */
380
+ set(value) {
381
+ if (Buffer.isBuffer(value)) {
382
+ _buffer = value;
383
+ _address = getBufferAddress(value);
384
+ } else if (typeof value === "bigint") {
385
+ _address = value;
386
+ _buffer = null;
387
+ } else if (typeof value === "number") {
388
+ _address = BigInt(value);
389
+ _buffer = null;
390
+ }
391
+ },
392
+
393
+ /**
394
+ * Check if pointer is NULL
395
+ */
396
+ get isNull() {
397
+ return _address === 0n && !_buffer;
398
+ },
399
+
400
+ /**
401
+ * For compatibility - get pointer as buffer (8 bytes containing address)
402
+ */
403
+ toBuffer() {
404
+ const buf = alloc(native.POINTER_SIZE);
405
+ writeValue(buf, c_void_p, _address);
406
+ return buf;
407
+ },
408
+
409
+ toString() {
410
+ return `<${typeName} pointer at 0x${_address.toString(16)}>`;
411
+ },
412
+ };
413
+
414
+ // Use Proxy for [index] access (pointer arithmetic)
415
+ return new Proxy(instance, {
416
+ get(target, prop, receiver) {
417
+ // Numeric index access: p[0], p[1], etc.
418
+ if (typeof prop === "string" && /^\d+$/.test(prop)) {
419
+ const index = parseInt(prop, 10);
420
+ if (target.isNull) {
421
+ throw new Error("NULL pointer access");
422
+ }
423
+ const offset = index * baseSize;
424
+ if (_buffer && offset + baseSize <= _buffer.length) {
425
+ return readValue(_buffer, baseType, offset);
426
+ }
427
+ // Use native to read at address + offset
428
+ const addr = _address + BigInt(offset);
429
+ const buf = native.ptrToBuffer(addr, baseSize);
430
+ return readValue(buf, baseType, 0);
431
+ }
432
+ return Reflect.get(target, prop, receiver);
433
+ },
434
+
435
+ set(target, prop, value, receiver) {
436
+ // Numeric index access: p[0] = value, p[1] = value, etc.
437
+ if (typeof prop === "string" && /^\d+$/.test(prop)) {
438
+ const index = parseInt(prop, 10);
439
+ if (target.isNull) {
440
+ throw new Error("NULL pointer access");
441
+ }
442
+ const offset = index * baseSize;
443
+ if (_buffer && offset + baseSize <= _buffer.length) {
444
+ writeValue(_buffer, baseType, value, offset);
445
+ return true;
446
+ }
447
+ // Use native to write at address + offset
448
+ const addr = _address + BigInt(offset);
449
+ const buf = native.ptrToBuffer(addr, baseSize);
450
+ writeValue(buf, baseType, value, 0);
451
+ return true;
452
+ }
453
+ return Reflect.set(target, prop, value, receiver);
454
+ },
455
+ });
456
+ }
457
+
458
+ /**
459
+ * Pointer type factory - creates pointer instances
460
+ */
461
+ const PointerType = {
294
462
  _pointerTo: baseType,
295
463
  _baseSize: baseSize,
296
464
  size: native.POINTER_SIZE,
297
465
 
298
466
  /**
299
- * Creates a NULL pointer.
300
- * @returns {Buffer} Buffer containing NULL pointer
467
+ * Creates a new pointer instance.
468
+ * @param {Buffer|bigint|number} [value] - Initial value (buffer to point to, or address)
469
+ * @returns {Object} Pointer instance with .contents and [index] access
301
470
  */
302
- create() {
303
- const buf = alloc(native.POINTER_SIZE);
304
- writeValue(buf, c_void_p, 0n);
305
- return buf;
471
+ create(value) {
472
+ return createPointerInstance(value);
306
473
  },
307
474
 
308
475
  /**
309
- * Creates a pointer to an existing buffer.
310
- * @param {Buffer} targetBuf - Target buffer to point to
311
- * @returns {Buffer} Buffer containing pointer to targetBuf
476
+ * Creates a pointer from an existing buffer (points to that buffer).
477
+ * @param {Buffer} targetBuf - Buffer to point to
478
+ * @returns {Object} Pointer instance
312
479
  */
313
480
  fromBuffer(targetBuf) {
314
- const buf = alloc(native.POINTER_SIZE);
315
- writeValue(buf, c_void_p, targetBuf);
316
- return buf;
481
+ return createPointerInstance(targetBuf);
482
+ },
483
+
484
+ /**
485
+ * Creates a pointer from a raw address.
486
+ * @param {bigint|number} address - Memory address
487
+ * @returns {Object} Pointer instance
488
+ */
489
+ fromAddress(address) {
490
+ return createPointerInstance(typeof address === "bigint" ? address : BigInt(address));
317
491
  },
318
492
 
319
493
  /**
320
- * Dereferences the pointer (reads the address it points to).
321
- * @param {Buffer} ptrBuf - Buffer containing the pointer
322
- * @returns {bigint|null} Address value, or null if NULL pointer
494
+ * Legacy API: Dereferences a pointer buffer.
495
+ * @deprecated Use pointer instance .contents instead
323
496
  */
324
497
  deref(ptrBuf) {
325
498
  const addr = readValue(ptrBuf, c_void_p);
326
499
  if (addr === 0n || addr === null) {
327
500
  return null;
328
501
  }
329
- // For struct types, return address (can't safely create buffer without native support)
330
502
  if (typeof baseType === "object" && baseType.toObject) {
331
503
  return addr;
332
504
  }
333
- // For primitive types, return address
334
505
  return addr;
335
506
  },
336
507
 
337
508
  /**
338
- * Sets the pointer to point to a value or buffer.
339
- * @param {Buffer} ptrBuf - Buffer containing the pointer
340
- * @param {Buffer|bigint|number} value - Value to set (buffer or address)
509
+ * Legacy API: Sets pointer buffer value.
510
+ * @deprecated Use pointer instance .set() instead
341
511
  */
342
512
  set(ptrBuf, value) {
343
513
  if (Buffer.isBuffer(value)) {
@@ -347,12 +517,59 @@ export function POINTER(baseType, sizeof, alloc, readValue, writeValue, c_void_p
347
517
  }
348
518
  },
349
519
 
350
- /**
351
- * Returns string representation of pointer type.
352
- * @returns {string} Type name
353
- */
354
520
  toString() {
355
521
  return `POINTER(${typeName})`;
356
522
  },
357
523
  };
524
+
525
+ return PointerType;
526
+ }
527
+
528
+ /**
529
+ * Creates a pointer to an existing ctypes object.
530
+ * Python ctypes compatible: pointer(obj) returns a pointer to obj.
531
+ *
532
+ * @param {Object} obj - Object to create pointer to (must have ._buffer or be a Buffer)
533
+ * @param {Function} POINTER_fn - POINTER function reference
534
+ * @param {Function} sizeof - sizeof function reference
535
+ * @param {Function} alloc - alloc function reference
536
+ * @param {Function} readValue - readValue function reference
537
+ * @param {Function} writeValue - writeValue function reference
538
+ * @param {Function} c_void_p - c_void_p type reference
539
+ * @param {Object} native - Native module reference
540
+ * @returns {Object} Pointer instance pointing to obj
541
+ *
542
+ * @example
543
+ * ```javascript
544
+ * const x = new c_int32(42);
545
+ * const p = pointer(x);
546
+ * console.log(p.contents); // 42
547
+ * p.contents = 100;
548
+ * console.log(x.value); // 100
549
+ * ```
550
+ */
551
+ export function pointer(obj, POINTER_fn, sizeof, alloc, readValue, writeValue, c_void_p, native) {
552
+ let targetBuffer;
553
+ let baseType;
554
+
555
+ if (Buffer.isBuffer(obj)) {
556
+ targetBuffer = obj;
557
+ baseType = c_void_p; // Generic pointer
558
+ } else if (obj && obj._buffer) {
559
+ // Structure/Union instance or SimpleCData with _buffer
560
+ targetBuffer = obj._buffer;
561
+ // Get the base type - use constructor for SimpleCData, _structDef for structures
562
+ if (obj.constructor && obj.constructor._type) {
563
+ // SimpleCData instance - use the class itself
564
+ baseType = obj.constructor;
565
+ } else {
566
+ // Structure/Union instance
567
+ baseType = obj._structDef || obj.__structDef || c_void_p;
568
+ }
569
+ } else {
570
+ throw new TypeError("pointer() argument must be a ctypes instance or Buffer");
571
+ }
572
+
573
+ const PtrType = POINTER_fn(baseType, sizeof, alloc, readValue, writeValue, c_void_p, native);
574
+ return PtrType.fromBuffer(targetBuffer);
358
575
  }