node-ctypes 0.1.4 → 0.1.5

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 CHANGED
@@ -94,7 +94,10 @@ print(p.x, p.y) # 10 20
94
94
  import { CDLL, c_int, Structure } from 'node-ctypes';
95
95
 
96
96
  // Traditional syntax (always available)
97
- const libc = new CDLL("libc.so.6");
97
+ const libc = new CDLL("libc.so.6"); // Linux
98
+ // const libc = new CDLL('msvcrt.dll'); // Windows
99
+ // const libc = new CDLL('libc.dylib'); // macOS
100
+
98
101
  const abs = libc.func("abs", c_int, [c_int]);
99
102
  console.log(abs(-42)); // 42
100
103
 
@@ -142,7 +145,10 @@ const strlen = libc.func('strlen', c_size_t, [c_char_p]);
142
145
  console.log(strlen('Hello')); // 5n (BigInt)
143
146
 
144
147
  // Load libm for math functions
145
- const libm = new CDLL('libm.so.6');
148
+ const libm = new CDLL('libm.so.6'); // Linux
149
+ // const libb = new CDLL('ucrtbase.dll'); // Windows
150
+ // const libm = new CDLL('libm.dylib'); // macOS
151
+
146
152
  const sqrt = libm.func('sqrt', c_double, [c_double]);
147
153
  console.log(sqrt(16.0)); // 4.0
148
154
  ```
@@ -271,7 +277,7 @@ const pkt = new Packet({
271
277
  data: new Array(256).fill(0)
272
278
  });
273
279
 
274
- console.log(pkt.header); // [1, 2, 3, 4, 5, 6, 7, 8]
280
+ console.log(pkt.header.toString()); // [1, 2, 3, 4, 5, 6, 7, 8]
275
281
  ```
276
282
 
277
283
  ### Complex Nested Structures
@@ -332,6 +338,7 @@ console.log(img.pixels[1].color.value); // -16711936 (0xFF00FF00 as signed)
332
338
 
333
339
  // Union nested in struct - direct property access!
334
340
  img.pixels[0].color.rgb.g = 128; // Works correctly!
341
+ console.log(img.pixels[0].color.rgb.g); // 128
335
342
  ```
336
343
 
337
344
  ### Callbacks - JavaScript Functions in C
@@ -349,7 +356,7 @@ const compare = callback(
349
356
  const bVal = readValue(b, c_int32);
350
357
  return aVal - bVal;
351
358
  },
352
- c_int, // return type
359
+ c_int32, // return type
353
360
  [c_void_p, c_void_p] // argument types: two pointers
354
361
  );
355
362
 
@@ -388,16 +395,16 @@ const sprintf = libc.func('sprintf', c_int, [c_void_p, c_char_p]);
388
395
  const buffer = Buffer.alloc(256);
389
396
 
390
397
  // Pass extra arguments - automatically handled as variadic
391
- sprintf(buffer, create_string_buffer('Hello %s!'), create_string_buffer('World'));
398
+ sprintf(buffer, 'Hello %s!', 'World');
392
399
  console.log(string_at(buffer)); // "Hello World!"
393
400
 
394
- sprintf(buffer, create_string_buffer('Number: %d'), 42);
401
+ sprintf(buffer, 'Number: %d', 42);
395
402
  console.log(string_at(buffer)); // "Number: 42"
396
403
 
397
- sprintf(buffer, create_string_buffer('%s: %d + %d = %d'), create_string_buffer('Sum'), 10, 20, 30);
404
+ sprintf(buffer, '%s: %d + %d = %d', 'Sum', 10, 20, 30);
398
405
  console.log(string_at(buffer)); // "Sum: 10 + 20 = 30"
399
406
 
400
- sprintf(buffer, create_string_buffer('Pi ≈ %.2f'), 3.14159);
407
+ sprintf(buffer, 'Pi ≈ %.2f', 3.14159);
401
408
  console.log(string_at(buffer)); // "Pi ≈ 3.14"
402
409
  ```
403
410
 
@@ -713,8 +720,8 @@ Base class for Python-like union definitions. Subclasses should define `static _
713
720
  | **Arrays** | `c_int * 5` | `array(c_int, 5)` |
714
721
  | **Bit fields** | `("flags", c_uint, 3)` | `bitfield(c_uint32, 3)` |
715
722
  | **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `callback(fn, c_int, [c_int])` |
716
- | **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")` |
717
- | **Pointers** | `POINTER(c_int)` | `c_void_p` or `"pointer"` |
723
+ | **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")`<br>**or**<br>`c_char_p(b"hello")` |
724
+ | **Pointers** | `POINTER(c_int)` | `c_void_p` |
718
725
  | **Variadic** | `sprintf(buf, b"%d", 42)` | `sprintf(buf, fmt, 42)` (auto) |
719
726
  | **Sizeof** | `sizeof(c_int)` | `sizeof(c_int)` |
720
727
 
Binary file
Binary file
package/lib/index.js CHANGED
@@ -454,8 +454,23 @@ function cstring(str) {
454
454
  * @returns {Buffer} Buffer con stringa wide
455
455
  */
456
456
  function wstring(str) {
457
- // Converti stringa JS a UTF-16LE (che è come wchar_t su Windows)
458
- const buf = Buffer.from(str + "\0", "utf16le");
457
+ // Crea un buffer contenente la stringa wide null-terminata
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
459
474
  return buf;
460
475
  }
461
476
 
@@ -825,12 +840,28 @@ function union(fields) {
825
840
  let size, alignment;
826
841
  let fieldDef = { name, type, offset: 0 };
827
842
 
828
- // Nested struct
843
+ // Nested struct (object form)
829
844
  if (_isStruct(type)) {
830
845
  size = type.size;
831
846
  alignment = type.alignment;
832
847
  fieldDef.isNested = true;
833
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
+ }
834
865
  // Array type
835
866
  else if (_isArrayType(type)) {
836
867
  size = type.getSize();
@@ -1520,24 +1551,31 @@ class Union extends Structure {
1520
1551
  * console.log(arr[2]); // 42
1521
1552
  */
1522
1553
  function array(elementType, count) {
1523
- // Validate elementType is a SimpleCData class
1524
- if (!(typeof elementType === "function" && elementType._isSimpleCData)) {
1525
- throw new TypeError("array elementType must be a SimpleCData class (e.g., c_int32, c_uint8)");
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");
1526
1561
  }
1527
1562
 
1528
- const elementSize = elementType._size;
1563
+ const elementSize = sizeof(elementType);
1529
1564
  let nativeArray;
1530
- if (typeof elementType === "object") {
1565
+ if (isNativeTypeObj) {
1531
1566
  nativeArray = new ArrayType(elementType, count);
1532
1567
  } else {
1533
- // For strings, create a mock
1568
+ // Provide a small JS-side shim implementing the minimal ArrayType API
1534
1569
  nativeArray = {
1535
1570
  getSize: () => count * elementSize,
1536
1571
  getLength: () => count,
1537
- getAlignment: () => elementSize, // Approximation
1572
+ getAlignment: () => elementSize,
1538
1573
  create: (values) => {
1539
- // Already handled in JS
1540
- throw new Error("Should not be called");
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;
1541
1579
  },
1542
1580
  };
1543
1581
  }
@@ -1567,7 +1605,13 @@ function array(elementType, count) {
1567
1605
  if (prop === Symbol.iterator) {
1568
1606
  return function* () {
1569
1607
  for (let i = 0; i < count; i++) {
1570
- yield elementType._reader(target, i * elementSize);
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
+ }
1571
1615
  }
1572
1616
  };
1573
1617
  }
@@ -1585,7 +1629,11 @@ function array(elementType, count) {
1585
1629
  const index = Number(prop);
1586
1630
  if (Number.isInteger(index) && !isNaN(index)) {
1587
1631
  if (index >= 0 && index < count) {
1588
- return readValue(target, elementType, index * elementSize);
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);
1589
1637
  }
1590
1638
  // Indice numerico fuori bounds -> undefined (comportamento JavaScript)
1591
1639
  return undefined;
@@ -2066,6 +2114,66 @@ function struct(fields, options = {}) {
2066
2114
  maxAlignment = alignment;
2067
2115
  }
2068
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
+ }
2069
2177
  // Caso 2: Nested struct
2070
2178
  else if (_isStruct(type)) {
2071
2179
  // Reset bit field state
@@ -2875,7 +2983,10 @@ class c_int32 extends SimpleCData {
2875
2983
  static _size = 4;
2876
2984
  static _type = "int32";
2877
2985
  static _reader = (buf, off) => buf.readInt32LE(off);
2878
- static _writer = (buf, off, val) => buf.writeInt32LE(val, 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
+ };
2879
2990
  }
2880
2991
 
2881
2992
  class c_int64 extends SimpleCData {
@@ -2989,7 +3100,11 @@ class c_wchar extends SimpleCData {
2989
3100
  class c_void_p extends SimpleCData {
2990
3101
  static _size = native.POINTER_SIZE;
2991
3102
  static _type = "pointer";
2992
- static _reader = (buf, off) => (native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off)));
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
+ };
2993
3108
  static _writer = (buf, off, val) => {
2994
3109
  // Accept Buffers (pointer to memory) and struct proxies with _buffer
2995
3110
  if (Buffer.isBuffer(val)) {
@@ -3020,7 +3135,11 @@ class c_void_p extends SimpleCData {
3020
3135
  class c_size_t extends SimpleCData {
3021
3136
  static _size = native.POINTER_SIZE;
3022
3137
  static _type = "size_t";
3023
- static _reader = (buf, off) => (native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off)));
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
+ };
3024
3143
  static _writer = (buf, off, val) => {
3025
3144
  const v = typeof val === "bigint" ? val : BigInt(val || 0);
3026
3145
  if (native.POINTER_SIZE === 8) {
@@ -3058,8 +3177,15 @@ class c_ulong extends SimpleCData {
3058
3177
  class c_char_p extends SimpleCData {
3059
3178
  static _size = native.POINTER_SIZE;
3060
3179
  static _type = "char_p";
3061
- static _reader = (buf, off) => (native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off)));
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
+ };
3062
3185
  static _writer = (buf, off, val) => {
3186
+ if (typeof val === "string") {
3187
+ val = cstring(val);
3188
+ }
3063
3189
  if (Buffer.isBuffer(val)) {
3064
3190
  const addr = addressOf(val);
3065
3191
  const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
@@ -3082,8 +3208,15 @@ class c_char_p extends SimpleCData {
3082
3208
  class c_wchar_p extends SimpleCData {
3083
3209
  static _size = native.POINTER_SIZE;
3084
3210
  static _type = "wchar_p";
3085
- static _reader = (buf, off) => (native.POINTER_SIZE === 8 ? buf.readBigUInt64LE(off) : BigInt(buf.readUInt32LE(off)));
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
+ };
3086
3216
  static _writer = (buf, off, val) => {
3217
+ if (typeof val === "string") {
3218
+ val = wstring(val);
3219
+ }
3087
3220
  if (Buffer.isBuffer(val)) {
3088
3221
  const addr = addressOf(val);
3089
3222
  const v = typeof addr === "bigint" ? addr : BigInt(addr || 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-ctypes",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Python ctypes-like FFI for Node.js using libffi",
5
5
  "author": "Damiano Mazzella",
6
6
  "license": "MIT",