koffi 3.0.1 → 3.1.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/CHANGELOG.md +32 -3
- package/cnoke.cjs +2 -2
- package/doc/benchmarks.md +1 -1
- package/doc/callbacks.md +7 -26
- package/doc/{input.md → composites.md} +161 -147
- package/doc/contribute.md +3 -2
- package/doc/index.md +0 -14
- package/doc/{functions.md → load.md} +54 -113
- package/doc/migration.md +4 -7
- package/doc/misc.md +0 -103
- package/doc/output.md +5 -11
- package/doc/pointers.md +76 -17
- package/doc/primitives.md +151 -0
- package/doc/start.md +3 -13
- package/doc/types.md +88 -0
- package/doc/unions.md +0 -186
- package/doc/values.md +134 -0
- package/index.d.ts +375 -308
- package/lib/native/base/base.cc +66 -24
- package/lib/native/base/base.hh +55 -153
- package/package.json +16 -16
- package/src/koffi/CMakeLists.txt +20 -17
- package/src/koffi/index.cjs +30 -111
- package/src/koffi/index.js +22 -96
- package/src/koffi/indirect.cjs +30 -111
- package/src/koffi/indirect.js +24 -24
- package/src/koffi/src/abi/arm64.cc +48 -62
- package/src/koffi/src/abi/riscv64.cc +39 -57
- package/src/koffi/src/abi/x64sysv.cc +39 -57
- package/src/koffi/src/abi/x64win.cc +48 -65
- package/src/koffi/src/abi/x86.cc +47 -59
- package/src/koffi/src/call.cc +426 -209
- package/src/koffi/src/call.hh +7 -11
- package/src/koffi/src/ffi.cc +534 -303
- package/src/koffi/src/ffi.hh +71 -15
- package/src/koffi/src/parser.cc +5 -3
- package/src/koffi/src/parser.hh +2 -2
- package/src/koffi/src/static.cjs +122 -0
- package/src/koffi/src/static.js +125 -0
- package/src/koffi/src/type.cc +725 -0
- package/src/koffi/src/type.hh +71 -0
- package/src/koffi/src/util.cc +117 -1202
- package/src/koffi/src/util.hh +158 -156
- package/src/koffi/src/uv.cc +17 -11
- package/src/koffi/src/uv.hh +2 -1
- package/vendor/node-addon-api/README.md +1 -1
- package/vendor/node-addon-api/napi-inl.h +213 -35
- package/vendor/node-addon-api/napi.h +118 -7
- package/doc/variables.md +0 -102
- package/indirect.d.ts +0 -322
package/CHANGELOG.md
CHANGED
|
@@ -5,8 +5,37 @@
|
|
|
5
5
|
|
|
6
6
|
## Koffi 3
|
|
7
7
|
|
|
8
|
+
### Koffi 3.1
|
|
9
|
+
|
|
10
|
+
#### Koffi 3.1.0
|
|
11
|
+
|
|
12
|
+
*Released on 2026-06-29*
|
|
13
|
+
|
|
14
|
+
- Add endian-sensitive integer decoding functions
|
|
15
|
+
- Drop required but useless type specifier from `lib.symbol()`
|
|
16
|
+
- Add `wstring` type alias for wide strings
|
|
17
|
+
- Add platform-dependent `koffi.decode.wstring()` function
|
|
18
|
+
- Improve performance of various functions and value conversions
|
|
19
|
+
- Optimize Windows build flags
|
|
20
|
+
- Fix static native loading from the koffi ESM entry point ([@yibe](https://codeberg.org/yibe))
|
|
21
|
+
- Fix minor memory and reference leaks on exit
|
|
22
|
+
- Fix length argument types in TypeScript definition
|
|
23
|
+
- Reorganize documentation pages
|
|
24
|
+
|
|
8
25
|
### Koffi 3.0
|
|
9
26
|
|
|
27
|
+
#### Koffi 3.0.2
|
|
28
|
+
|
|
29
|
+
*Released on 2026-05-26*
|
|
30
|
+
|
|
31
|
+
- Fix unexpected type error when passing array of pointers (see [Koromix/koffi#269](https://github.com/Koromix/koffi/issues/269))
|
|
32
|
+
- Search more native addon directories under `process.resourcesPath`
|
|
33
|
+
- Rearrange Koffi loading to use `require()` for static packages
|
|
34
|
+
- Avoid declare module syntax in TypeScript definition file
|
|
35
|
+
- Fix error when using *String* array hint with UTF-32 string types (`const char32_t *`)
|
|
36
|
+
- Add *Buffer* array hint to convert arrays and pointers to Node Buffer objects
|
|
37
|
+
- Reduce overhead of various Koffi functions
|
|
38
|
+
|
|
10
39
|
#### Koffi 3.0.1
|
|
11
40
|
|
|
12
41
|
*Released on 2026-05-20*
|
|
@@ -400,7 +429,7 @@ Consult the [migration guide](migration) for more information.
|
|
|
400
429
|
*Released on 2023-12-05*
|
|
401
430
|
|
|
402
431
|
- Speed up resolving simple and often used type names
|
|
403
|
-
- Fix use of optional length argument with [koffi.encode()](
|
|
432
|
+
- Fix use of optional length argument with [koffi.encode()](values#encode-to-c-memory)
|
|
404
433
|
|
|
405
434
|
#### Koffi 2.6.10
|
|
406
435
|
|
|
@@ -461,8 +490,8 @@ Consult the [migration guide](migration) for more information.
|
|
|
461
490
|
|
|
462
491
|
**New features:**
|
|
463
492
|
|
|
464
|
-
- Use [koffi.symbol()](
|
|
465
|
-
- Use [koffi.encode()](
|
|
493
|
+
- Use [koffi.symbol()](values#variable-definitions) to make pointers to exported variables (or other symbols)
|
|
494
|
+
- Use [koffi.encode()](values#encode-to-c-memory) to explictly encode data from JS to C memory
|
|
466
495
|
- Use shared library [lazy-loading](functions#loading-options) (RTLD_LAZY) on POSIX platforms
|
|
467
496
|
|
|
468
497
|
**Other changes:**
|
package/cnoke.cjs
CHANGED
|
@@ -317,8 +317,8 @@ endif()
|
|
|
317
317
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
318
318
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(amd64|x86_64)")
|
|
319
319
|
foreach(lang C CXX)
|
|
320
|
-
set(CMAKE_\${lang}_FLAGS_RELEASE "\${CMAKE_\${lang}_FLAGS_RELEASE} -
|
|
321
|
-
set(CMAKE_\${lang}_FLAGS_RELWITHDEBINFO "\${CMAKE_\${lang}_FLAGS_RELWITHDEBINFO} -
|
|
320
|
+
set(CMAKE_\${lang}_FLAGS_RELEASE "\${CMAKE_\${lang}_FLAGS_RELEASE} -march=x86-64-v2")
|
|
321
|
+
set(CMAKE_\${lang}_FLAGS_RELWITHDEBINFO "\${CMAKE_\${lang}_FLAGS_RELWITHDEBINFO} -march=x86-64-v2")
|
|
322
322
|
endforeach()
|
|
323
323
|
endif()
|
|
324
324
|
endif()
|
package/doc/benchmarks.md
CHANGED
|
@@ -25,7 +25,7 @@ This test is based around repeated calls to the standard C function `memset`. Al
|
|
|
25
25
|
|
|
26
26
|
# macOS ARM64
|
|
27
27
|
|
|
28
|
-
The results presented below were measured on an Apple Mac mini
|
|
28
|
+
The results presented below were measured on an Apple Mac mini M1 hosted by Scaleway.
|
|
29
29
|
|
|
30
30
|
<div class="benchmark chart" data-platform="darwin_arm64"></div>
|
|
31
31
|
|
package/doc/callbacks.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Callback types
|
|
2
2
|
|
|
3
|
-
*Changed in Koffi 2.7*
|
|
4
|
-
|
|
5
3
|
In order to pass a JS function to a C function expecting a callback, you must first create a callback type with the expected return type and parameters. The syntax is similar to the one used to load functions from a shared library.
|
|
6
4
|
|
|
7
5
|
```js
|
|
@@ -14,10 +12,8 @@ const ExampleCallback = koffi.proto('ExampleCallback', 'void', ['int']);
|
|
|
14
12
|
// With the prototype parser, this callback expects a double and float, and returns the sum as a double
|
|
15
13
|
const AddDoubleFloat = koffi.proto('double AddDoubleFloat(double d, float f)');
|
|
16
14
|
```
|
|
17
|
-
> [!NOTE]
|
|
18
|
-
> The function `koffi.proto()` was introduced in Koffi 2.4, it was called `koffi.callback()` in earlier versions.
|
|
19
15
|
|
|
20
|
-
For alternative [calling conventions](
|
|
16
|
+
For alternative [calling conventions](load#calling-conventions) (such as `stdcall` on Windows x86 32-bit), you can specify as the first argument with the classic syntax, or after the return type in prototype strings, like this:
|
|
21
17
|
|
|
22
18
|
```js
|
|
23
19
|
const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
|
|
@@ -31,30 +27,19 @@ const EnumWindowsProc = koffi.proto('__stdcall', 'EnumWindowsProc', 'bool', ['HW
|
|
|
31
27
|
> [!WARNING]
|
|
32
28
|
> You have to make sure you **get the calling convention right** (such as specifying __stdcall for a Windows API callback), or your code will crash on Windows 32-bit.
|
|
33
29
|
>
|
|
34
|
-
>
|
|
30
|
+
> Only *cdecl* and *stdcall* callbacks are supported.
|
|
35
31
|
|
|
36
32
|
Once your callback type is declared, you can use a pointer to it in struct definitions, as function parameters and/or return types, or to call/decode function pointers.
|
|
37
33
|
|
|
38
|
-
> [!NOTE]
|
|
39
|
-
> Callbacks **have changed in version 2.0**.
|
|
40
|
-
>
|
|
41
|
-
> In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
|
|
42
|
-
>
|
|
43
|
-
> Now, you must use them through a pointer: `void CallIt(CallbackType func)` in Koffi 1.x becomes `void CallIt(CallbackType *func)` in version 2.0 and newer.
|
|
44
|
-
>
|
|
45
|
-
> Consult the [migration guide](migration) for more information.
|
|
46
|
-
|
|
47
34
|
# Transient and registered callbacks
|
|
48
35
|
|
|
49
36
|
Koffi only uses predefined static trampolines, and does not need to generate code at runtime, which makes it compatible with platforms with hardened W^X migitations (such as PaX mprotect). However, this imposes some restrictions on the maximum number of callbacks, and their duration.
|
|
50
37
|
|
|
51
38
|
Thus, Koffi distinguishes two callback modes:
|
|
52
39
|
|
|
53
|
-
- [Transient callbacks](#transient-callbacks) can only be called while the C function they are passed to is running, and are invalidated when it returns. If the C function calls the callback later, the behavior is undefined, though Koffi tries to detect such cases. If it does, an exception will be thrown, but this is
|
|
40
|
+
- [Transient callbacks](#transient-callbacks) can only be called while the C function they are passed to is running, and are invalidated when it returns. If the C function calls the callback later, the behavior is undefined, though Koffi tries to detect such cases. If it does, an exception will be thrown, but this is not guaranteed. However, they are simple to use, and don't require any special handling.
|
|
54
41
|
- [Registered callbacks](#registered-callbacks) can be called at any time, but they must be manually registered and unregistered. A limited number of registered callbacks can exist at the same time.
|
|
55
42
|
|
|
56
|
-
You need to specify the correct [calling convention](functions#calling-conventions) on x86 platforms, or the behavior is undefined (Node will probably crash). Only *cdecl* and *stdcall* callbacks are supported.
|
|
57
|
-
|
|
58
43
|
## Transient callbacks
|
|
59
44
|
|
|
60
45
|
Use transient callbacks when the native C function only needs to call them while it runs (e.g. qsort, progress callback, `sqlite3_exec`). Here is a small example with the C part and the JS part.
|
|
@@ -95,7 +80,7 @@ console.log(ret);
|
|
|
95
80
|
|
|
96
81
|
## Registered callbacks
|
|
97
82
|
|
|
98
|
-
*
|
|
83
|
+
*Changed in Koffi 3.0 (see note below)*
|
|
99
84
|
|
|
100
85
|
Use registered callbacks when the function needs to be called at a later time (e.g. log handler, event handler, `fopencookie/funopen`). Call `koffi.register(func, type)` to register a callback function, with two arguments: the JS function, and the callback type.
|
|
101
86
|
|
|
@@ -143,7 +128,7 @@ koffi.unregister(cb2);
|
|
|
143
128
|
```
|
|
144
129
|
|
|
145
130
|
> [!NOTE]
|
|
146
|
-
> In Koffi 2.
|
|
131
|
+
> In Koffi 2.2, you could bind a specific `this` value to the callback function, by giving an object as the first argument: `koffi.register(object, func)`.
|
|
147
132
|
>
|
|
148
133
|
> This feature has been deprecated in Koffi 3 and will eventually be removed. Replace with an explicit call to `function.bind()` instead.
|
|
149
134
|
|
|
@@ -151,11 +136,9 @@ koffi.unregister(cb2);
|
|
|
151
136
|
|
|
152
137
|
## Decoding pointer arguments
|
|
153
138
|
|
|
154
|
-
|
|
139
|
+
Koffi does not have enough information to convert callback pointer arguments to an appropriate JS value. In this case, your JS function will receive a *BigInt* value with the pointer address (or *null* is the pointer is null).
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
You can pass this value through to another C function that expects a pointer of the same type, or you can use [koffi.decode()](variables#decode-to-js-values) function to decode pointer arguments.
|
|
141
|
+
You can pass this value through to another C function that expects a pointer of the same type, or you can use [koffi.decode()](values#decode-to-js-values) function to decode pointer arguments.
|
|
159
142
|
|
|
160
143
|
The following examples uses it to sort an array of strings in-place with the standard C function `qsort()`:
|
|
161
144
|
|
|
@@ -182,8 +165,6 @@ console.log(array); // Prints ['123', 'bar', 'foo', 'foobar']
|
|
|
182
165
|
|
|
183
166
|
## Asynchronous callbacks
|
|
184
167
|
|
|
185
|
-
*New in Koffi 2.2.2*
|
|
186
|
-
|
|
187
168
|
JS execution is inherently single-threaded, so JS callbacks must run on the main thread. There are two ways you may want to call a callback function from another thread:
|
|
188
169
|
|
|
189
170
|
- Call the callback from an asynchronous FFI call (e.g. `waitpid.async`)
|
|
@@ -1,83 +1,3 @@
|
|
|
1
|
-
# Primitive types
|
|
2
|
-
|
|
3
|
-
## Standard types
|
|
4
|
-
|
|
5
|
-
While the C standard allows for variation in the size of most integer types, Koffi enforces the same definition for most primitive types, listed below:
|
|
6
|
-
|
|
7
|
-
C type | JS type | Bytes | Signedness | Note
|
|
8
|
-
----------------------------- | ---------------- | ----- | ---------- | ---------------------------
|
|
9
|
-
void | Undefined | 0 | | Only valid as a return type
|
|
10
|
-
int8, int8_t | Number (integer) | 1 | Signed |
|
|
11
|
-
uint8, uint8_t | Number (integer) | 1 | Unsigned |
|
|
12
|
-
char | Number (integer) | 1 | Signed |
|
|
13
|
-
uchar, unsigned char | Number (integer) | 1 | Unsigned |
|
|
14
|
-
char16, char16_t | Number (integer) | 2 | Signed |
|
|
15
|
-
int16, int16_t | Number (integer) | 2 | Signed |
|
|
16
|
-
uint16, uint16_t | Number (integer) | 2 | Unsigned |
|
|
17
|
-
short | Number (integer) | 2 | Signed |
|
|
18
|
-
ushort, unsigned short | Number (integer) | 2 | Unsigned |
|
|
19
|
-
char32, char32_t | Number (integer) | 4 | Signed |
|
|
20
|
-
int32, int32_t | Number (integer) | 4 | Signed |
|
|
21
|
-
uint32, uint32_t | Number (integer) | 4 | Unsigned |
|
|
22
|
-
int | Number (integer) | 4 | Signed |
|
|
23
|
-
uint, unsigned int | Number (integer) | 4 | Unsigned |
|
|
24
|
-
int64, int64_t | Number (integer) | 8 | Signed |
|
|
25
|
-
uint64, uint64_t | Number (integer) | 8 | Unsigned |
|
|
26
|
-
longlong, long long | Number (integer) | 8 | Signed |
|
|
27
|
-
ulonglong, unsigned long long | Number (integer) | 8 | Unsigned |
|
|
28
|
-
float32 | Number (float) | 4 | |
|
|
29
|
-
float64 | Number (float) | 8 | |
|
|
30
|
-
float | Number (float) | 4 | |
|
|
31
|
-
double | Number (float) | 8 | |
|
|
32
|
-
|
|
33
|
-
Koffi also accepts BigInt values when converting from JS to C integers. If the value exceeds the range of the C type, Koffi will convert the number to an undefined value. In the reverse direction, BigInt values are automatically used when needed for big 64-bit integers.
|
|
34
|
-
|
|
35
|
-
Koffi defines a few more types that can change size depending on the OS and the architecture:
|
|
36
|
-
|
|
37
|
-
C type | JS type | Signedness | Note
|
|
38
|
-
---------------- | ---------------- | ----------- | ------------------------------------------------
|
|
39
|
-
bool | Boolean | | Usually one byte
|
|
40
|
-
long | Number (integer) | Signed | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
41
|
-
ulong | Number (integer) | Unsigned | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
42
|
-
unsigned long | Number (integer) | Unsigned | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
43
|
-
intptr | Number (integer) | Signed | 4 or 8 bytes depending on register width
|
|
44
|
-
intptr_t | Number (integer) | Signed | 4 or 8 bytes depending on register width
|
|
45
|
-
uintptr | Number (integer) | Unsigned | 4 or 8 bytes depending on register width
|
|
46
|
-
uintptr_t | Number (integer) | Unsigned | 4 or 8 bytes depending on register width
|
|
47
|
-
wchar_t | Number (integer) | *Undefined* | 2 bytes on Windows, 4 bytes Linux, macOS and BSD
|
|
48
|
-
str, string | String | | JS strings are converted to and from UTF-8
|
|
49
|
-
str16, string16 | String | | JS strings are converted to and from UTF-16 (LE)
|
|
50
|
-
str32, string32 | String | | JS strings are converted to and from UTF-32 (LE)
|
|
51
|
-
|
|
52
|
-
Primitive types can be specified by name (in a string) or through `koffi.types`:
|
|
53
|
-
|
|
54
|
-
```js
|
|
55
|
-
// These two lines do the same:
|
|
56
|
-
let struct1 = koffi.struct({ dummy: 'long' });
|
|
57
|
-
let struct2 = koffi.struct({ dummy: koffi.types.long });
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Endian-sensitive integers
|
|
61
|
-
|
|
62
|
-
*New in Koffi 2.1*
|
|
63
|
-
|
|
64
|
-
Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
|
|
65
|
-
|
|
66
|
-
C type | Bytes | Signedness | Endianness
|
|
67
|
-
---------------------- | ----- | ---------- | -------------
|
|
68
|
-
int16_le, int16_le_t | 2 | Signed | Little Endian
|
|
69
|
-
int16_be, int16_be_t | 2 | Signed | Big Endian
|
|
70
|
-
uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
|
|
71
|
-
uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
|
|
72
|
-
int32_le, int32_le_t | 4 | Signed | Little Endian
|
|
73
|
-
int32_be, int32_be_t | 4 | Signed | Big Endian
|
|
74
|
-
uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
|
|
75
|
-
uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
|
|
76
|
-
int64_le, int64_le_t | 8 | Signed | Little Endian
|
|
77
|
-
int64_be, int64_be_t | 8 | Signed | Big Endian
|
|
78
|
-
uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
|
|
79
|
-
uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
|
|
80
|
-
|
|
81
1
|
# Struct types
|
|
82
2
|
|
|
83
3
|
## Struct definition
|
|
@@ -145,17 +65,6 @@ Many C libraries use some kind of object-oriented API, with a pair of functions
|
|
|
145
65
|
|
|
146
66
|
In Koffi, you can manage this with opaque types. Declare the opaque type with `koffi.opaque(name)`, and use a pointer to this type either as a return type or some kind of [output parameter](output) (with a double pointer).
|
|
147
67
|
|
|
148
|
-
> [!NOTE]
|
|
149
|
-
> Opaque types **have changed in version 2.0, and again in version 2.1**.
|
|
150
|
-
>
|
|
151
|
-
> In Koffi 1.x, opaque handles were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
|
|
152
|
-
>
|
|
153
|
-
> Now, you must use them through a pointer, and use an array for output parameters. This is shown in the example below (look for the call to `ConcatNewOut` in the JS part), and is described in the section on [output parameters](output).
|
|
154
|
-
>
|
|
155
|
-
> In addition to this, you should use `koffi.opaque()` (introduced in Koffi 2.1) instead of `koffi.handle()` which is deprecated, and will be removed eventually in Koffi 3.0.
|
|
156
|
-
>
|
|
157
|
-
> Consult the [migration guide](migration) for more information.
|
|
158
|
-
|
|
159
68
|
The full example below implements an iterative string builder (concatenator) in C, and uses it from Javascript to output a mix of Hello World and FizzBuzz. The builder is hidden behind an opaque type, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
|
|
160
69
|
|
|
161
70
|
```c
|
|
@@ -336,8 +245,6 @@ try {
|
|
|
336
245
|
|
|
337
246
|
## Fixed-size C arrays
|
|
338
247
|
|
|
339
|
-
*Changed in Koffi 2.7.1*
|
|
340
|
-
|
|
341
248
|
Fixed-size arrays are declared with `koffi.array(type, length)`. Just like in C, they cannot be passed as functions parameters (they degenerate to pointers), or returned by value. You can however embed them in struct types.
|
|
342
249
|
|
|
343
250
|
Koffi applies the following conversion rules when passing arrays to/from C:
|
|
@@ -378,28 +285,18 @@ const StructType = koffi.struct('StructType', {
|
|
|
378
285
|
});
|
|
379
286
|
```
|
|
380
287
|
|
|
381
|
-
> [!NOTE]
|
|
382
|
-
> The short C-like syntax was introduced in Koffi 2.7.1, use `koffi.array()` for older versions.
|
|
383
|
-
|
|
384
288
|
## Fixed-size string buffers
|
|
385
289
|
|
|
386
|
-
*Changed in Koffi 2.9.0*
|
|
387
|
-
|
|
388
290
|
Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
|
389
291
|
|
|
390
292
|
- **char arrays** are filled with the UTF-8 encoded string, truncated if needed. The buffer is always NUL-terminated.
|
|
391
293
|
- **char16 (or char16_t) arrays** are filled with the UTF-16 encoded string, truncated if needed. The buffer is always NUL-terminated.
|
|
392
294
|
- **char32 (or char32_t) arrays** are filled with the UTF-32 encoded string, truncated if needed. The buffer is always NUL-terminated.
|
|
393
295
|
|
|
394
|
-
> [!NOTE]
|
|
395
|
-
> Support for UTF-32 and wchar_t (wide) strings was introduced in Koffi 2.9.0.
|
|
396
|
-
|
|
397
296
|
The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS string. This happens by default for char, char16_t and char32_t arrays, but you can also explicitly ask for this with the `String` array hint (e.g. `koffi.array('char', 8, 'String')`).
|
|
398
297
|
|
|
399
298
|
## Flexible arrays
|
|
400
299
|
|
|
401
|
-
*Added in Koffi 2.14.0*
|
|
402
|
-
|
|
403
300
|
C structs ending with a flexible array member are often used for variable-sized structs, and are generally paired with dynamic memory allocation. In many cases, the number of elements is described by another struct member.
|
|
404
301
|
|
|
405
302
|
Use `koffi.array(type, countedBy, maxLen)` to make a flexible array type, for which the array length is determined by the struct member indicated by the `countedBy` parameter. Flexible array types can only be used as the last member of a struct, as shown below:
|
|
@@ -466,70 +363,187 @@ console.log(array); // Prints { count: 8, numbers: [1, 2, 3, 4, 5, 10, 12, 14] }
|
|
|
466
363
|
|
|
467
364
|
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers#dynamic-arrays) in the relevant section.
|
|
468
365
|
|
|
469
|
-
#
|
|
366
|
+
# Unions
|
|
470
367
|
|
|
471
|
-
|
|
368
|
+
You can declare unions with a syntax similar to structs, but with the `koffi.union()` function. This function takes two arguments: the first one is the name of the type, and the second one is an object containing the union member names and types. You can omit the first argument to declare an anonymous union.
|
|
472
369
|
|
|
473
|
-
|
|
370
|
+
The following example illustrates how to declare the same union in C and in JS with Koffi:
|
|
474
371
|
|
|
475
|
-
|
|
372
|
+
```c
|
|
373
|
+
typedef union IntOrDouble {
|
|
374
|
+
int64_t i;
|
|
375
|
+
double d;
|
|
376
|
+
} IntOrDouble;
|
|
377
|
+
```
|
|
476
378
|
|
|
477
|
-
|
|
478
|
-
|
|
379
|
+
```js
|
|
380
|
+
const IntOrDouble = koffi.union('IntOrDouble', {
|
|
381
|
+
i: 'int64_t',
|
|
382
|
+
d: 'double'
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Input unions
|
|
387
|
+
|
|
388
|
+
### Passing union values to C
|
|
479
389
|
|
|
480
|
-
|
|
390
|
+
You can instantiate an union object with `koffi.Union(type)`. This will create a special object that contains at most one active member.
|
|
391
|
+
|
|
392
|
+
Once you have created an instance of your union, you can simply set the member with the dot operator as you would with a basic object. Then, simply pass your union value to the C function you wish.
|
|
481
393
|
|
|
482
394
|
```js
|
|
483
|
-
|
|
484
|
-
const OpenResult = koffi.enumeration('OpenResult', {
|
|
485
|
-
Success: 0,
|
|
486
|
-
MissingFile: 1,
|
|
487
|
-
AccessDenied: 2,
|
|
488
|
-
OtherError: 3
|
|
489
|
-
});
|
|
395
|
+
const U = koffi.union('U', { i: 'int', str: 'char *' });
|
|
490
396
|
|
|
491
|
-
|
|
492
|
-
const RelativePosition = koffi.enumeration('RelativePosition', {
|
|
493
|
-
Left: -1,
|
|
494
|
-
Center: 0,
|
|
495
|
-
Right: 1
|
|
496
|
-
});
|
|
397
|
+
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
|
|
497
398
|
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
399
|
+
const u1 = new koffi.Union('U'); u1.i = 42;
|
|
400
|
+
const u2 = new koffi.Union('U'); u2.str = 'Hello!';
|
|
401
|
+
|
|
402
|
+
DoSomething('int', u1);
|
|
403
|
+
DoSomething('string', u2);
|
|
503
404
|
```
|
|
504
405
|
|
|
505
|
-
|
|
506
|
-
|
|
406
|
+
For simplicity, Koffi also accepts object literals with one property (no more, no less) setting the corresponding union member. The example belows uses this to simplify the code shown above:
|
|
407
|
+
|
|
408
|
+
```js
|
|
409
|
+
const U = koffi.union('U', { i: 'int', str: 'char *' });
|
|
507
410
|
|
|
508
|
-
|
|
509
|
-
- Windows: MSVC (and Clang) always use `int` even if some values do not fit, which matches what Koffi does... unless the compiler flag `/Zc:enumTypes` is set, maybe.
|
|
411
|
+
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
|
|
510
412
|
|
|
511
|
-
|
|
413
|
+
DoSomething('int', { i: 42 });
|
|
414
|
+
DoSomething('string', { str: 'Hello!' });
|
|
512
415
|
```
|
|
513
416
|
|
|
514
|
-
|
|
417
|
+
### Win32 example
|
|
418
|
+
|
|
419
|
+
The following example uses the [SendInput](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput) Win32 API to emit the Win+D shortcut and hide windows (show the desktop).
|
|
515
420
|
|
|
516
421
|
```js
|
|
517
|
-
|
|
518
|
-
|
|
422
|
+
import koffi from 'koffi';
|
|
423
|
+
// CJS: const koffi = require('koffi');
|
|
424
|
+
|
|
425
|
+
// Win32 type and functions
|
|
426
|
+
|
|
427
|
+
const user32 = koffi.load('user32.dll');
|
|
428
|
+
|
|
429
|
+
const INPUT_MOUSE = 0;
|
|
430
|
+
const INPUT_KEYBOARD = 1;
|
|
431
|
+
const INPUT_HARDWARE = 2;
|
|
432
|
+
|
|
433
|
+
const KEYEVENTF_KEYUP = 0x2;
|
|
434
|
+
const KEYEVENTF_SCANCODE = 0x8;
|
|
435
|
+
|
|
436
|
+
const VK_LWIN = 0x5B;
|
|
437
|
+
const VK_D = 0x44;
|
|
438
|
+
|
|
439
|
+
const MOUSEINPUT = koffi.struct('MOUSEINPUT', {
|
|
440
|
+
dx: 'long',
|
|
441
|
+
dy: 'long',
|
|
442
|
+
mouseData: 'uint32_t',
|
|
443
|
+
dwFlags: 'uint32_t',
|
|
444
|
+
time: 'uint32_t',
|
|
445
|
+
dwExtraInfo: 'uintptr_t'
|
|
446
|
+
});
|
|
447
|
+
const KEYBDINPUT = koffi.struct('KEYBDINPUT', {
|
|
448
|
+
wVk: 'uint16_t',
|
|
449
|
+
wScan: 'uint16_t',
|
|
450
|
+
dwFlags: 'uint32_t',
|
|
451
|
+
time: 'uint32_t',
|
|
452
|
+
dwExtraInfo: 'uintptr_t'
|
|
453
|
+
});
|
|
454
|
+
const HARDWAREINPUT = koffi.struct('HARDWAREINPUT', {
|
|
455
|
+
uMsg: 'uint32_t',
|
|
456
|
+
wParamL: 'uint16_t',
|
|
457
|
+
wParamH: 'uint16_t'
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const INPUT = koffi.struct('INPUT', {
|
|
461
|
+
type: 'uint32_t',
|
|
462
|
+
u: koffi.union({
|
|
463
|
+
mi: MOUSEINPUT,
|
|
464
|
+
ki: KEYBDINPUT,
|
|
465
|
+
hi: HARDWAREINPUT
|
|
466
|
+
})
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const SendInput = user32.func('unsigned int __stdcall SendInput(unsigned int cInputs, INPUT *pInputs, int cbSize)');
|
|
470
|
+
|
|
471
|
+
// Show/hide desktop with Win+D shortcut
|
|
472
|
+
|
|
473
|
+
let events = [
|
|
474
|
+
make_keyboard_event(VK_LWIN, true),
|
|
475
|
+
make_keyboard_event(VK_D, true),
|
|
476
|
+
make_keyboard_event(VK_D, false),
|
|
477
|
+
make_keyboard_event(VK_LWIN, false)
|
|
478
|
+
];
|
|
479
|
+
|
|
480
|
+
SendInput(events.length, events, koffi.sizeof(INPUT));
|
|
481
|
+
|
|
482
|
+
// Utility
|
|
483
|
+
|
|
484
|
+
function make_keyboard_event(vk, down) {
|
|
485
|
+
let event = {
|
|
486
|
+
type: INPUT_KEYBOARD,
|
|
487
|
+
u: {
|
|
488
|
+
ki: {
|
|
489
|
+
wVk: vk,
|
|
490
|
+
wScan: 0,
|
|
491
|
+
dwFlags: down ? 0 : KEYEVENTF_KEYUP,
|
|
492
|
+
time: 0,
|
|
493
|
+
dwExtraInfo: 0
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
return event;
|
|
499
|
+
}
|
|
519
500
|
```
|
|
520
501
|
|
|
521
|
-
|
|
502
|
+
## Output unions
|
|
522
503
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
504
|
+
Unlike structs, Koffi does not know which union member is valid, and it cannot decode it automatically. You can however use special `koffi.Union` objects for output parameters, and decode the memory after the call.
|
|
505
|
+
|
|
506
|
+
To decode an output union pointer parameter, create a placeholder object with `new koffi.Union(type)` and pass the resulting object to the function.
|
|
507
|
+
|
|
508
|
+
After the call, you can dereference the member value you want on this object and Koffi will decode it at this moment.
|
|
509
|
+
|
|
510
|
+
The following example illustrates the use of `koffi.Union()` to decode output unions after the call.
|
|
511
|
+
|
|
512
|
+
```c
|
|
513
|
+
#include <stdint.h>
|
|
514
|
+
|
|
515
|
+
typedef union IntOrDouble {
|
|
516
|
+
int64_t i;
|
|
517
|
+
double d;
|
|
518
|
+
} IntOrDouble;
|
|
519
|
+
|
|
520
|
+
void SetUnionInt(int64_t i, IntOrDouble *out)
|
|
521
|
+
{
|
|
522
|
+
out->i = i;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
void SetUnionDouble(double d, IntOrDouble *out)
|
|
526
|
+
{
|
|
527
|
+
out->d = d;
|
|
528
|
+
}
|
|
531
529
|
```
|
|
532
530
|
|
|
533
|
-
|
|
531
|
+
```js
|
|
532
|
+
const IntOrDouble = koffi.union('IntOrDouble', {
|
|
533
|
+
i: 'int64_t',
|
|
534
|
+
d: 'double',
|
|
535
|
+
raw: koffi.array('uint8_t', 8)
|
|
536
|
+
});
|
|
534
537
|
|
|
535
|
-
|
|
538
|
+
const SetUnionInt = lib.func('void SetUnionInt(int64_t i, _Out_ IntOrDouble *out)');
|
|
539
|
+
const SetUnionDouble = lib.func('void SetUnionDouble(double d, _Out_ IntOrDouble *out)');
|
|
540
|
+
|
|
541
|
+
let u1 = new koffi.Union('IntOrDouble');
|
|
542
|
+
let u2 = new koffi.Union('IntOrDouble');
|
|
543
|
+
|
|
544
|
+
SetUnionInt(123, u1);
|
|
545
|
+
SetUnionDouble(123, u2);
|
|
546
|
+
|
|
547
|
+
console.log(u1.i, '---', u1.raw); // Prints 123 --- Uint8Array(8) [123, 0, 0, 0, 0, 0, 0, 0]
|
|
548
|
+
console.log(u2.d, '---', u2.raw); // Prints 123 --- Uint8Array(8) [0, 0, 0, 0, 0, 0, 69, 64]
|
|
549
|
+
```
|
package/doc/contribute.md
CHANGED
|
@@ -131,11 +131,12 @@ node tools/brew.js info debian_x64
|
|
|
131
131
|
|
|
132
132
|
# Making a release
|
|
133
133
|
|
|
134
|
-
First, you must update the code in
|
|
134
|
+
First, you must update the code in four steps:
|
|
135
135
|
|
|
136
136
|
- Change the version number in `package.json`
|
|
137
137
|
- Add an entry to `CHANGELOG` to summarize the changes since last release
|
|
138
138
|
- Commit theses changes with the message *Bump Koffi to X.Y.Z*
|
|
139
|
+
- Create a signed tag on this commit with `git tag -s koffi/X.Y.Z -m 'Koffi X.Y.Z'`
|
|
139
140
|
|
|
140
141
|
Once this is done, you can publish a new release with the following commands:
|
|
141
142
|
|
|
@@ -145,7 +146,7 @@ cd src/koffi
|
|
|
145
146
|
node tools/brew.js test # If not done before
|
|
146
147
|
node tools/brew.js build
|
|
147
148
|
|
|
148
|
-
cd ../../bin/
|
|
149
|
+
cd ../../bin/Koffi/packages
|
|
149
150
|
|
|
150
151
|
./prepare.sh
|
|
151
152
|
./publish.sh
|
package/doc/index.md
CHANGED
|
@@ -35,20 +35,6 @@ LoongArch64 | ⬜️ | ✅ | 🟨 | ⬜️ |
|
|
|
35
35
|
|
|
36
36
|
For all fully supported platforms (green check marks), a prebuilt binary is included in the NPM package which means you can install Koffi without a C++ compiler.
|
|
37
37
|
|
|
38
|
-
# Source code
|
|
39
|
-
|
|
40
|
-
The source code is available here: https://github.com/Koromix/rygel/ (in the *src/koffi* subdirectory).
|
|
41
|
-
|
|
42
|
-
> [!NOTE]
|
|
43
|
-
> Most of my projects live in a single repository (or monorepo), which have two killer features for me:
|
|
44
|
-
>
|
|
45
|
-
> - Cross-project refactoring
|
|
46
|
-
> - Simplified dependency management
|
|
47
|
-
>
|
|
48
|
-
> You can find a more detailed rationale here: https://danluu.com/monorepo/
|
|
49
|
-
|
|
50
|
-
New releases are frequent, look at the [changelog](changelog) for more information.
|
|
51
|
-
|
|
52
38
|
# License
|
|
53
39
|
|
|
54
40
|
This program is free software: you can redistribute it and/or modify it under the terms of the **MIT License**.
|