node-ctypes 0.1.3

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 ADDED
@@ -0,0 +1,768 @@
1
+ # node-ctypes
2
+
3
+ [![npm version](https://img.shields.io/npm/v/node-ctypes.svg)](https://www.npmjs.com/package/node-ctypes)
4
+ [![npm downloads](https://img.shields.io/npm/dm/node-ctypes.svg)](https://www.npmjs.com/package/node-ctypes)
5
+ [![Build](https://github.com/dmazzella/node-ctypes/actions/workflows/build.yml/badge.svg)](https://github.com/dmazzella/node-ctypes/actions/workflows/build.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ **Python ctypes for Node.js** - A high-performance FFI (Foreign Function Interface) library with full Python ctypes compatibility, built on [libffi](https://github.com/libffi/libffi) and [N-API](https://nodejs.org/api/n-api.html).
9
+
10
+ ## Why node-ctypes?
11
+
12
+ - ✨ **Python ctypes API Compatibility** - If you know Python ctypes, you already know node-ctypes! Same syntax, same patterns.
13
+ - 🚀 **High Performance** - Up to **50x faster** than ffi-napi, with struct operations matching or exceeding koffi performance.
14
+ - 🔧 **Complete FFI Support** - Structs, unions, bit fields, nested structures, arrays, callbacks, variadic functions, and more.
15
+ - 🌍 **Cross-platform** - Works seamlessly on Linux, macOS, and Windows with identical API.
16
+
17
+ ## Features
18
+
19
+ - 🐍 **Full Python ctypes compatibility** - Struct definitions, unions, bit fields, anonymous fields
20
+ - 📚 **Load shared libraries** (.so, .dll, .dylib) with CDLL and WinDLL
21
+ - 🔄 **Variadic functions** (printf, sprintf) with automatic detection
22
+ - 📞 **Callbacks** - JavaScript functions callable from C code
23
+ - đŸ—ī¸ **Complex data structures** - Nested structs, unions in structs, arrays in structs
24
+ - ⚡ **High performance** - Eager loading for struct properties, optimized FFI wrapper
25
+ - 🔍 **Transparent API** - Pass struct objects directly to FFI functions
26
+ - 🔧 **Extended type support** - All ctypes types (int8, uint8, int16, uint16, etc.)
27
+ - 📊 **Memory utilities** - readValue/writeValue for direct memory access, enhanced sizeof
28
+ - đŸ—ī¸ **Advanced array support** - String initialization, improved proxy behavior
29
+
30
+ ## Installation
31
+
32
+ ### From npm (recommended)
33
+
34
+ ```bash
35
+ npm install node-ctypes
36
+ ```
37
+
38
+ Prebuilt binaries are available for:
39
+ - Windows x64, ARM64
40
+ - Linux x64, ARM64
41
+ - macOS ARM64
42
+
43
+ ### From source
44
+
45
+ #### Prerequisites
46
+
47
+ - Node.js >= 16
48
+ - CMake >= 3.15
49
+ - C++ compiler (GCC, Clang, or MSVC)
50
+
51
+ #### Ubuntu/Debian
52
+ ```bash
53
+ sudo apt install build-essential cmake
54
+ ```
55
+
56
+ #### macOS
57
+ ```bash
58
+ brew install cmake
59
+ ```
60
+
61
+ #### Windows
62
+ - Install Visual Studio Build Tools
63
+ - Install CMake
64
+
65
+ #### Build
66
+ ```bash
67
+ npm install
68
+ npm run build
69
+ ```
70
+
71
+ ## Quick Start - Python ctypes Users
72
+
73
+ If you're familiar with Python ctypes, you'll feel right at home:
74
+
75
+ **Python ctypes:**
76
+ ```python
77
+ from ctypes import CDLL, c_int, Structure
78
+
79
+ libc = CDLL("libc.so.6")
80
+ abs_func = libc.abs
81
+ abs_func.argtypes = [c_int]
82
+ abs_func.restype = c_int
83
+ print(abs_func(-42)) # 42
84
+
85
+ class Point(Structure):
86
+ _fields_ = [("x", c_int), ("y", c_int)]
87
+
88
+ p = Point(10, 20)
89
+ print(p.x, p.y) # 10 20
90
+ ```
91
+
92
+ **node-ctypes (identical patterns!):**
93
+ ```javascript
94
+ import { CDLL, c_int, Structure } from 'node-ctypes';
95
+
96
+ const libc = new CDLL("libc.so.6");
97
+ const abs = libc.func("abs", c_int, [c_int]);
98
+ console.log(abs(-42)); // 42
99
+
100
+ class Point extends Structure {
101
+ static _fields_ = [
102
+ ["x", c_int],
103
+ ["y", c_int]
104
+ ];
105
+ }
106
+
107
+ const p = new Point(10, 20);
108
+ console.log(p.x, p.y); // 10 20
109
+ ```
110
+
111
+ ## Usage
112
+
113
+ ### Basic FFI - Calling C Functions
114
+
115
+ ```javascript
116
+ import { CDLL, c_int, c_double, c_char_p } from 'node-ctypes';
117
+
118
+ // Load libc
119
+ const libc = new CDLL('libc.so.6'); // Linux
120
+ // const libc = new CDLL('msvcrt.dll'); // Windows
121
+ // const libc = new CDLL('libc.dylib'); // macOS
122
+
123
+ // Call abs() - integer absolute value
124
+ const abs = libc.func('abs', c_int, [c_int]);
125
+ console.log(abs(-42)); // 42
126
+
127
+ // Call strlen() - string length
128
+ const strlen = libc.func('strlen', 'size_t', [c_char_p]);
129
+ console.log(strlen('Hello')); // 5n (BigInt)
130
+
131
+ // Load libm for math functions
132
+ const libm = new CDLL('libm.so.6');
133
+ const sqrt = libm.func('sqrt', c_double, [c_double]);
134
+ console.log(sqrt(16.0)); // 4.0
135
+ ```
136
+
137
+ ### Structs - Full Python ctypes Compatibility
138
+
139
+ ```javascript
140
+ import { Structure, c_int, c_uint32 } from 'node-ctypes';
141
+
142
+ // Simple struct - Python-like class syntax
143
+ class Point extends Structure {
144
+ static _fields_ = [
145
+ ["x", c_int],
146
+ ["y", c_int]
147
+ ];
148
+ }
149
+
150
+ // Create and initialize - direct property access!
151
+ const p = new Point(10, 20);
152
+ console.log(p.x, p.y); // 10 20
153
+
154
+ // Modify properties directly
155
+ p.x = 100;
156
+ console.log(p.x); // 100
157
+
158
+ // Get struct size
159
+ console.log(Point.size); // 8
160
+
161
+ // Nested structs
162
+ class Rectangle extends Structure {
163
+ static _fields_ = [
164
+ ["topLeft", Point],
165
+ ["bottomRight", Point],
166
+ ["color", c_uint32]
167
+ ];
168
+ }
169
+
170
+ const rect = new Rectangle({
171
+ topLeft: { x: 0, y: 0 },
172
+ bottomRight: { x: 100, y: 200 },
173
+ color: 0xff0000
174
+ });
175
+
176
+ console.log(rect.topLeft.x); // 0
177
+ console.log(rect.bottomRight.x); // 100
178
+ console.log(rect.color); // 16711680
179
+ ```
180
+
181
+ ### Unions - Shared Memory Regions
182
+
183
+ ```javascript
184
+ import { Union, c_int, c_float } from 'node-ctypes';
185
+
186
+ // Union - all fields share the same memory
187
+ class IntOrFloat extends Union {
188
+ static _fields_ = [
189
+ ["i", c_int],
190
+ ["f", c_float]
191
+ ];
192
+ }
193
+
194
+ const u = new IntOrFloat();
195
+ u.f = 3.14159;
196
+ console.log(u.i); // Bit pattern of float as integer
197
+
198
+ u.i = 42;
199
+ console.log(u.f); // 42 reinterpreted as float
200
+ ```
201
+
202
+ ### Bit Fields - Compact Data Structures
203
+
204
+ ```javascript
205
+ import { Structure, bitfield, c_uint32 } from 'node-ctypes';
206
+
207
+ // Bit fields for flags and compact data
208
+ class Flags extends Structure {
209
+ static _fields_ = [
210
+ ["enabled", bitfield(c_uint32, 1)], // 1 bit
211
+ ["mode", bitfield(c_uint32, 3)], // 3 bits
212
+ ["priority", bitfield(c_uint32, 4)], // 4 bits
213
+ ["reserved", bitfield(c_uint32, 24)] // 24 bits
214
+ ];
215
+ }
216
+
217
+ const flags = new Flags();
218
+
219
+ flags.enabled = 1;
220
+ flags.mode = 5;
221
+ flags.priority = 12;
222
+
223
+ console.log(flags.enabled); // 1
224
+ console.log(flags.mode); // 5
225
+ console.log(flags.priority); // 12
226
+ ```
227
+
228
+ ### Arrays - Fixed-size and Dynamic
229
+
230
+ ```javascript
231
+ import { c_int, c_uint8, array } from 'node-ctypes';
232
+
233
+ // Fixed-size array
234
+ const IntArray = array('int32', 5);
235
+ const arr = IntArray.create([1, 2, 3, 4, 5]);
236
+
237
+ // Array access
238
+ console.log(arr[0]); // 1
239
+ console.log(arr[4]); // 5
240
+
241
+ // Iterate
242
+ for (const val of arr) {
243
+ console.log(val);
244
+ }
245
+
246
+ // Arrays in structs
247
+ import { Structure, array } from 'node-ctypes';
248
+
249
+ class Packet extends Structure {
250
+ static _fields_ = [
251
+ ["header", array("c_uint8", 8)],
252
+ ["data", array("c_uint8", 256)]
253
+ ];
254
+ }
255
+
256
+ const pkt = new Packet({
257
+ header: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
258
+ data: new Array(256).fill(0)
259
+ });
260
+
261
+ console.log(pkt.header); // [1, 2, 3, 4, 5, 6, 7, 8]
262
+ ```
263
+
264
+ ### Complex Nested Structures
265
+
266
+ Real-world example from our test suite:
267
+
268
+ ```javascript
269
+ import { Structure, Union, c_uint8, c_uint16, c_int32, array } from 'node-ctypes';
270
+
271
+ // RGB color components
272
+ class RGB extends Structure {
273
+ static _fields_ = [
274
+ ["r", c_uint8],
275
+ ["g", c_uint8],
276
+ ["b", c_uint8],
277
+ ["a", c_uint8]
278
+ ];
279
+ }
280
+
281
+ // Union for color access (as RGB or as 32-bit value)
282
+ class Color extends Union {
283
+ static _fields_ = [
284
+ ["rgb", RGB],
285
+ ["value", c_int32]
286
+ ];
287
+ }
288
+
289
+ // Pixel with position and color
290
+ class Pixel extends Structure {
291
+ static _fields_ = [
292
+ ["x", c_uint16],
293
+ ["y", c_uint16],
294
+ ["color", Color]
295
+ ];
296
+ }
297
+
298
+ // Image with array of pixels
299
+ class Image extends Structure {
300
+ static _fields_ = [
301
+ ["width", c_uint16],
302
+ ["height", c_uint16],
303
+ ["pixels", array(Pixel, 2)]
304
+ ];
305
+ }
306
+
307
+ // Create and manipulate
308
+ const img = new Image({
309
+ width: 640,
310
+ height: 480,
311
+ pixels: [
312
+ { x: 10, y: 20, color: { rgb: { r: 255, g: 0, b: 0, a: 255 } } },
313
+ { x: 30, y: 40, color: { value: 0xFF00FF00 } }
314
+ ]
315
+ });
316
+
317
+ console.log(img.pixels[0].color.rgb.r); // 255
318
+ console.log(img.pixels[1].color.value); // -16711936 (0xFF00FF00 as signed)
319
+
320
+ // Union nested in struct - direct property access!
321
+ img.pixels[0].color.rgb.g = 128; // Works correctly!
322
+ ```
323
+
324
+ ### Callbacks - JavaScript Functions in C
325
+
326
+ ```javascript
327
+ import { CDLL, callback, c_int, c_void_p, readValue, writeValue, create_string_buffer } from 'node-ctypes';
328
+
329
+ const libc = new CDLL('msvcrt.dll'); // or libc.so.6 on Linux
330
+
331
+ // Create a comparison callback for qsort
332
+ const compare = callback(
333
+ (a, b) => {
334
+ // a and b are pointers to int32 values
335
+ const aVal = readValue(a, 'int32');
336
+ const bVal = readValue(b, 'int32');
337
+ return aVal - bVal;
338
+ },
339
+ c_int, // return type
340
+ [c_void_p, c_void_p] // argument types: two pointers
341
+ );
342
+
343
+ // Sort an array using qsort
344
+ const qsort = libc.func('qsort', 'void', [
345
+ c_void_p, // array pointer
346
+ 'size_t', // number of elements
347
+ 'size_t', // element size
348
+ c_void_p // comparison function
349
+ ]);
350
+
351
+ const arr = create_string_buffer(5 * 4);
352
+ const values = [5, 2, 8, 1, 9];
353
+ values.forEach((v, i) => writeValue(arr, 'int32', v, i * 4));
354
+ qsort(arr, 5, 4, compare.pointer);
355
+
356
+ // Array is now sorted: [1, 2, 5, 8, 9]
357
+ console.log(readValue(arr, 'int32', 0)); // 1
358
+ console.log(readValue(arr, 'int32', 4)); // 2
359
+
360
+ // IMPORTANT: Release callback when done
361
+ compare.release();
362
+ ```
363
+
364
+ ### Variadic Functions - printf, sprintf
365
+
366
+ ```javascript
367
+ import { CDLL, create_string_buffer, string_at, c_int, c_void_p, c_char_p } from 'node-ctypes';
368
+
369
+ const libc = new CDLL('msvcrt.dll'); // Windows
370
+ // const libc = new CDLL('libc.so.6'); // Linux
371
+
372
+ // Define only the fixed parameters - variadic args detected automatically!
373
+ const sprintf = libc.func('sprintf', c_int, [c_void_p, c_char_p]);
374
+
375
+ const buffer = Buffer.alloc(256);
376
+
377
+ // Pass extra arguments - automatically handled as variadic
378
+ sprintf(buffer, create_string_buffer('Hello %s!'), create_string_buffer('World'));
379
+ console.log(string_at(buffer)); // "Hello World!"
380
+
381
+ sprintf(buffer, create_string_buffer('Number: %d'), 42);
382
+ console.log(string_at(buffer)); // "Number: 42"
383
+
384
+ sprintf(buffer, create_string_buffer('%s: %d + %d = %d'), create_string_buffer('Sum'), 10, 20, 30);
385
+ console.log(string_at(buffer)); // "Sum: 10 + 20 = 30"
386
+
387
+ sprintf(buffer, create_string_buffer('Pi ≈ %.2f'), 3.14159);
388
+ console.log(string_at(buffer)); // "Pi ≈ 3.14"
389
+ ```
390
+
391
+ **Automatic variadic detection** - When you pass more arguments than specified, node-ctypes:
392
+ - ✅ Detects the extra arguments
393
+ - ✅ Infers their types (string → char*, number → int32/double, Buffer → pointer)
394
+ - ✅ Uses `ffi_prep_cif_var` for variadic call preparation
395
+ - ✅ Calls the function with correct argument marshalling
396
+
397
+ **This matches Python ctypes behavior exactly!**
398
+
399
+ ### Windows API - Full Support
400
+
401
+ ```javascript
402
+ import { WinDLL, Structure, c_uint16, c_void_p, c_wchar_p, c_int } from 'node-ctypes';
403
+
404
+ // WinDLL uses __stdcall convention (default for Windows API)
405
+ const kernel32 = new WinDLL('kernel32.dll');
406
+
407
+ // SYSTEMTIME structure (from tests/windows/test_winapi.js)
408
+ class SYSTEMTIME extends Structure {
409
+ static _fields_ = [
410
+ ["wYear", c_uint16],
411
+ ["wMonth", c_uint16],
412
+ ["wDayOfWeek", c_uint16],
413
+ ["wDay", c_uint16],
414
+ ["wHour", c_uint16],
415
+ ["wMinute", c_uint16],
416
+ ["wSecond", c_uint16],
417
+ ["wMilliseconds", c_uint16]
418
+ ];
419
+ }
420
+
421
+ // Get local time
422
+ const GetLocalTime = kernel32.func('GetLocalTime', 'void', [c_void_p]);
423
+
424
+ const st = new SYSTEMTIME();
425
+ GetLocalTime(st); // Pass struct directly - automatic _buffer extraction!
426
+
427
+ console.log(`${st.wYear}-${st.wMonth}-${st.wDay}`);
428
+ console.log(`${st.wHour}:${st.wMinute}:${st.wSecond}`);
429
+
430
+ // MessageBox (wide string version)
431
+ const user32 = new WinDLL('user32.dll');
432
+ const MessageBoxW = user32.func('MessageBoxW', c_int, [
433
+ c_void_p, // hWnd
434
+ c_wchar_p, // lpText
435
+ c_wchar_p, // lpCaption
436
+ 'uint32' // uType
437
+ ]);
438
+
439
+ // Create UTF-16 buffers for wide strings
440
+ const text = Buffer.from('Hello from node-ctypes!\0', 'utf16le');
441
+ const caption = Buffer.from('node-ctypes\0', 'utf16le');
442
+
443
+ MessageBoxW(null, text, caption, 0);
444
+ ```
445
+
446
+ ### Memory Operations - Low-level Control
447
+
448
+ ```javascript
449
+ import { readValue, writeValue, sizeof, create_string_buffer, string_at, c_int, c_double, c_void_p } from 'node-ctypes';
450
+
451
+ // Allocate memory
452
+ const buf = Buffer.alloc(16);
453
+
454
+ // Write values at specific offsets
455
+ writeValue(buf, c_int, 12345, 0);
456
+ writeValue(buf, c_double, 3.14159, 8);
457
+
458
+ // Read values back
459
+ console.log(readValue(buf, c_int, 0)); // 12345
460
+ console.log(readValue(buf, c_double, 8)); // 3.14159
461
+
462
+ // Get type sizes
463
+ console.log(sizeof(c_int)); // 4
464
+ console.log(sizeof(c_double)); // 8
465
+ console.log(sizeof(c_void_p)); // 8 (on 64-bit)
466
+
467
+ // String handling
468
+ const str = create_string_buffer('Hello, World!');
469
+ console.log(string_at(str)); // "Hello, World!"
470
+ ```
471
+
472
+ ## Performance Benchmarks
473
+
474
+ Benchmarked on Windows with Node.js v24.11.0:
475
+
476
+ **vs koffi** (comprehensive 10-benchmark comparison, geometric mean: **3.27x slower**):
477
+ - Simple int32 function: 1.74x slower
478
+ - String parameter: 1.95x slower
479
+ - Floating point: 1.83x slower
480
+ - No arguments: 2.11x slower
481
+ - Multiple arguments: **1.40x faster**
482
+ - Variadic function: 1.28x slower
483
+ - **Struct read/write: 14.91x slower**
484
+ - Buffer allocation: 40.5% overhead
485
+ - Raw vs CDLL wrapper: 7.3% overhead
486
+ - **Callback creation: 1.51x slower**
487
+
488
+ **Key Insights:**
489
+ - koffi excels at simple operations and struct access
490
+ - node-ctypes competitive on complex argument handling
491
+ - **Struct performance gap**: koffi 15x faster due to direct object manipulation
492
+ - **Callback overhead**: koffi 1.5x faster at callback creation
493
+
494
+ **Transparent API overhead**: Only **3.5%** for auto `._buffer` extraction!
495
+
496
+ *See `tests/benchmarks/` for full benchmark suite.*
497
+
498
+ ## Supported Types
499
+
500
+ | Type Name | Aliases | Size |
501
+ |-----------|---------|------|
502
+ | `void` | - | 0 |
503
+ | `int8` | `c_int8`, `char` | 1 |
504
+ | `uint8` | `c_uint8`, `uchar` | 1 |
505
+ | `int16` | `c_int16`, `short` | 2 |
506
+ | `uint16` | `c_uint16`, `ushort` | 2 |
507
+ | `int32` | `c_int32`, `int`, `c_int` | 4 |
508
+ | `uint32` | `c_uint32`, `uint`, `c_uint` | 4 |
509
+ | `int64` | `c_int64`, `long long` | 8 |
510
+ | `uint64` | `c_uint64` | 8 |
511
+ | `float` | `c_float` | 4 |
512
+ | `double` | `c_double` | 8 |
513
+ | `pointer` | `c_void_p`, `void*`, `ptr` | 8 (64-bit) |
514
+ | `string` | `c_char_p`, `char*` | pointer |
515
+ | `bool` | `c_bool` | 1 |
516
+ | `size_t` | `c_size_t` | pointer |
517
+
518
+ ## API Reference
519
+
520
+ ### Classes
521
+
522
+ #### `CDLL(libPath)`
523
+ Load a shared library using default (cdecl) calling convention.
524
+
525
+ #### `WinDLL(libPath)`
526
+ Load a shared library using stdcall calling convention (Windows).
527
+
528
+ #### `Library(libPath)`
529
+ Low-level library wrapper.
530
+
531
+ ---
532
+
533
+ ## Detailed API Reference (from lib/index.js)
534
+
535
+ This section provides a more complete description of the APIs exported from `lib/index.js`, with quick examples and usage notes.
536
+
537
+ **Native classes and exports**
538
+ - `Version` - Version information exposed by the native module.
539
+ - `Library` - Represents a loaded native library and exposes low-level functions for symbols and library management.
540
+ - `FFIFunction` - Low-level object representing an FFI function (has properties like `address` and internal methods).
541
+ - `Callback` - Builds JS callbacks callable from C (main thread).
542
+ - `ThreadSafeCallback` - Builds thread-safe JS callbacks (can be called from external threads).
543
+ - `CType`, `StructType`, `ArrayType` - Types and helpers exposed by the native layer.
544
+
545
+ **Library loading and wrappers**
546
+ - `load(libPath)` → `Library` : loads a native library; `libPath` can be `null` for the current executable.
547
+ - `CDLL(libPath)` : common-use wrapper for C calls with cdecl convention; maintains a function cache and provides more convenient `func()`.
548
+ - `WinDLL(libPath)` : like `CDLL` but with `abi: 'stdcall'` by default (useful for WinAPI).
549
+
550
+ Example:
551
+ ```js
552
+ import { CDLL } from './lib/index.js';
553
+ const libc = new CDLL(null);
554
+ const abs = libc.func('abs', 'int32', ['int32']);
555
+ console.log(abs(-5));
556
+ ```
557
+
558
+ **Detailed CDLL API**
559
+ - `func(name, returnType, argTypes = [], options = {})` → `Function` : gets a callable function. The returned function is optimized and:
560
+ - automatically extracts `._buffer` from struct objects passed as arguments;
561
+ - exposes non-enumerable metadata: `funcName`, `address`, `_ffi`;
562
+ - provides the `errcheck` property as getter/setter to intercept return errors.
563
+ - `symbol(name)` → `BigInt` : address of a symbol.
564
+ - `close()` : closes the library and clears the cache.
565
+ - `path` (getter) : library path.
566
+ - `loaded` (getter) : loading status.
567
+
568
+ **Callback**
569
+ - `callback(fn, returnType, argTypes = [])` → `{ pointer, release(), _callback }` : fast callback, main thread only.
570
+ - `threadSafeCallback(fn, returnType, argTypes = [])` → `{ pointer, release(), _callback }` : thread-safe callback for external threads.
571
+
572
+ Note: always call `release()` when a callback is no longer needed.
573
+
574
+ **Allocation and strings**
575
+
576
+ - `Buffer.alloc(size)` → `Buffer` : allocates native memory.
577
+ - `create_string_buffer(init)` → `Buffer` : creates null-terminated C string (init: size|string|Buffer).
578
+ - `create_unicode_buffer(init)` → `Buffer` : creates null-terminated wide string (wchar_t).
579
+ - `ptrToBuffer(address, size)` → `Buffer` : view on native address (use with caution).
580
+ - `addressof(ptr)` → `BigInt` : get the address as BigInt.
581
+
582
+ Example string creation and passing to function:
583
+ ```js
584
+ import { create_string_buffer, CDLL } from './lib/index.js';
585
+ const libc = new CDLL(null);
586
+ const puts = libc.func('puts', 'int32', ['pointer']);
587
+ const s = create_string_buffer('hello');
588
+ puts(s);
589
+ ```
590
+
591
+ **Reading and writing values**
592
+ - `readValue(ptr, type, offset = 0)` : supports fast-path for Buffer + basic types (`int8`, `uint8`, `int16`, `int32`, `int64`, `float`, `double`, `bool`).
593
+ - `writeValue(ptr, type, value, offset = 0)` : writes values with fast-path for Buffer.
594
+
595
+ **Types and helpers**
596
+ - `sizeof(type)` → `number` : size in bytes of a type.
597
+ - `POINTER(baseType)` : creates a pointer type with helpers `create()`, `fromBuffer()`, `deref()`, `set()`.
598
+ - `byref(buffer)` : passes a buffer by reference (Python ctypes compatibility).
599
+ - `cast(ptr, targetType)` : interprets a pointer as another type (returns wrapper for struct).
600
+
601
+ **Struct / Union / Array / Bitfield**
602
+ - `struct(fields, options)` : defines struct with support for nested, bitfields, anonymous fields, packed option. Returns object with `create()`, `get()`, `set()`, `toObject()`, `getNestedBuffer()`.
603
+ - `union(fields)` : defines union; provides `create()`, `get()`, `set()`, `toObject()` and returns plain objects with properties.
604
+ - `array(elementType, count)` : defines ArrayType; `wrap(buffer)` returns Proxy with indexing.
605
+ - `bitfield(baseType, bits)` : bitfield definition.
606
+
607
+ Struct example:
608
+ ```js
609
+ class Point extends Structure {
610
+ static _fields_ = [
611
+ ["x", c_int32],
612
+ ["y", c_int32]
613
+ ];
614
+ }
615
+
616
+ const p = new Point(1, 2);
617
+ console.log(p.x, p.y); // 1 2
618
+ ```
619
+
620
+ **Python-compatible conveniences**
621
+ - `create_string_buffer(init)` : create string buffer from number/string/Buffer.
622
+ - `create_unicode_buffer(init)` : create wide string buffer.
623
+ - `string_at(address, size)` / `wstring_at(address, size)` : read strings from address.
624
+
625
+ **Memory: utilities**
626
+ - `memmove(dst, src, count)` : copy memory.
627
+ - `memset(dst, value, count)` : set memory.
628
+
629
+ **Error handling and WinAPI helpers**
630
+ - `get_errno()` / `set_errno(value)` : access to errno (platform-specific implementation).
631
+ - `_initWinError()` internals; public helpers: `GetLastError()`, `SetLastError(code)`, `FormatError(code)`, `WinError(code)`.
632
+
633
+ **Type aliases**
634
+ The following aliases are exposed (mapped from `native.types`):
635
+ `c_int, c_uint, c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64, c_float, c_double, c_char, c_char_p, c_wchar, c_wchar_p, c_void_p, c_bool, c_size_t, c_long, c_ulong`.
636
+
637
+ **Constants**
638
+ - `POINTER_SIZE` - pointer size (from `native.POINTER_SIZE`).
639
+ - `WCHAR_SIZE` - wchar size (from `native.WCHAR_SIZE`).
640
+ - `NULL` - exported null value.
641
+
642
+ ---
643
+
644
+ If you want, I can generate additional Windows or Linux-specific snippets, or integrate examples in the `tests/` directory.
645
+
646
+ ### Functions
647
+
648
+ #### `load(libPath)` → `Library`
649
+ Load a shared library.
650
+
651
+ #### `callback(fn, returnType, argTypes)` → `{pointer, release()}`
652
+ Create a callback from a JavaScript function.
653
+
654
+ #### `create_string_buffer(init)` → `Buffer`
655
+ Create a null-terminated C string buffer (like Python ctypes). `init` can be a size, a string, or an existing `Buffer`.
656
+
657
+ #### `create_unicode_buffer(init)` → `Buffer`
658
+ Create a wide (wchar_t) null-terminated buffer (UTF-16LE on Windows).
659
+
660
+ #### `string_at(address, [size])` → `string`
661
+ Read a C string from an address or buffer.
662
+
663
+ #### `readValue(ptr, type, [offset])` → `value`
664
+ Read a value from memory.
665
+
666
+ #### `writeValue(ptr, type, value, [offset])` → `bytesWritten`
667
+ Write a value to memory.
668
+
669
+ #### `sizeof(type)` → `number`
670
+ Get the size of a type in bytes.
671
+
672
+ #### `struct(fields)` → `StructDefinition`
673
+ Create a simple struct definition.
674
+
675
+ #### `Structure` (base class)
676
+ Base class for Python-like struct definitions. Subclasses should define `static _fields_`.
677
+
678
+ #### `Union` (base class)
679
+ Base class for Python-like union definitions. Subclasses should define `static _fields_`.
680
+
681
+ ## Python ctypes Compatibility Reference
682
+
683
+ ### API Comparison
684
+
685
+ | Feature | Python ctypes | node-ctypes |
686
+ |---------|---------------|-------------|
687
+ | **Load library** | `CDLL("lib.so")` | `new CDLL("lib.so")` |
688
+ | **Define function** | `lib.func.argtypes = [c_int]`<br>`lib.func.restype = c_int` | `lib.func("func", c_int, [c_int])` |
689
+ | **Structs** | `class Point(Structure):`<br>&nbsp;&nbsp;`_fields_ = [("x", c_int)]` | `class Point extends Structure`<br>&nbsp;&nbsp;`{ static _fields_ = [["x", c_int]] }` |
690
+ | **Unions** | `class U(Union):`<br>&nbsp;&nbsp;`_fields_ = [("i", c_int)]` | `class U extends Union`<br>&nbsp;&nbsp;`{ static _fields_ = [["i", c_int]] }` |
691
+ | **Arrays** | `c_int * 5` | `array(c_int, 5)` |
692
+ | **Bit fields** | `("flags", c_uint, 3)` | `bitfield(c_uint32, 3)` |
693
+ | **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `callback(fn, c_int, [c_int])` |
694
+ | **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")` |
695
+ | **Pointers** | `POINTER(c_int)` | `c_void_p` or `"pointer"` |
696
+ | **Variadic** | `sprintf(buf, b"%d", 42)` | `sprintf(buf, fmt, 42)` (auto) |
697
+ | **Sizeof** | `sizeof(c_int)` | `sizeof(c_int)` |
698
+
699
+ ### What's Supported
700
+
701
+ ✅ **Fully Compatible**:
702
+ - All basic types (int8-64, uint8-64, float, double, bool, pointer, string)
703
+ - Structs with nested structures
704
+ - Unions (including nested in structs)
705
+ - Bit fields
706
+ - Arrays (fixed-size)
707
+ - Callbacks (with manual release)
708
+ - Variadic functions (automatic detection)
709
+ - Anonymous fields in structs/unions
710
+ - **Class-based struct/union definitions** (`class MyStruct extends Structure`)
711
+ - Platform-specific types (c_long, c_ulong, c_size_t)
712
+ - Memory operations (alloc, read, write)
713
+ - Windows API (__stdcall via WinDLL)
714
+
715
+ âš ī¸ **Differences from Python ctypes**:
716
+ - Structs use `.toObject()` for property access (eager loading for performance)
717
+ - Callbacks must be manually released with `.release()`
718
+ - Function definition is combined: `func(name, returnType, argTypes)` vs separate argtypes/restype
719
+ - No `POINTER()` type - use `c_void_p` or type name string
720
+
721
+
722
+ ## Limitations & Known Issues
723
+
724
+ - âš ī¸ Callbacks must be released manually with `.release()` to prevent memory leaks
725
+ - âš ī¸ No automatic memory management for returned pointers (manual `free()` required)
726
+ - âš ī¸ Struct alignment follows platform defaults (not customizable per-field)
727
+ - â„šī¸ Nested union access uses getter caching (slight behavior difference from Python)
728
+ - â„šī¸ Struct property access via `.toObject()` instead of direct field access (performance optimization)
729
+
730
+ ## Examples in Test Suite
731
+
732
+ For complete, working examples, see the test suite:
733
+
734
+ - **Basic types**: [tests/common/test_basic_types.js](tests/common/test_basic_types.js)
735
+ - **Structs & unions**: [tests/common/test_structs.js](tests/common/test_structs.js)
736
+ - **Nested structures**: [tests/common/test_nested_structs.js](tests/common/test_nested_structs.js)
737
+ - **Arrays**: [tests/common/test_arrays.js](tests/common/test_arrays.js)
738
+ - **Functions**: [tests/common/test_functions.js](tests/common/test_functions.js)
739
+ - **Callbacks**: [tests/common/test_callbacks.js](tests/common/test_callbacks.js)
740
+ - **Version info**: [tests/common/test_version.js](tests/common/test_version.js)
741
+ - **Windows API**: [tests/windows/test_winapi.js](tests/windows/test_winapi.js)
742
+ - **Python compatibility**: [tests/common/*.py](tests/common/) (parallel Python implementations)
743
+
744
+ Run tests:
745
+ ```bash
746
+ cd tests
747
+ npm install
748
+ npm run test # All tests
749
+ npm run bench:koffi # Benchmark vs koffi
750
+ ```
751
+
752
+ ## Examples
753
+
754
+ For practical GUI application examples using the Windows API:
755
+
756
+ - **Simple GUI Demo**: [examples/windows/simple.js](examples/windows/simple.js) - Message boxes and basic Windows API GUI elements
757
+ - **Windows Controls Showcase Demo**: [examples/windows/windows_controls.js](examples/windows/windows_controls.js) - Comprehensive demo with a wide set of common Win32 controls
758
+
759
+ ## License
760
+
761
+ MIT
762
+
763
+ ## Credits
764
+
765
+ Built with:
766
+ - [libffi](https://github.com/libffi/libffi) - Foreign Function Interface library
767
+ - [node-addon-api](https://github.com/nodejs/node-addon-api) - N-API C++ wrapper
768
+ - [cmake-js](https://github.com/cmake-js/cmake-js) - CMake-based build system for Node.js addons