koffi 1.3.9 → 1.3.12
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 +24 -0
- package/build/qemu/1.3.12/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_x64.tar.gz +0 -0
- package/doc/benchmarks.md +2 -2
- package/doc/conf.py +1 -1
- package/doc/contribute.md +1 -1
- package/doc/dist/doctrees/benchmarks.doctree +0 -0
- package/doc/dist/doctrees/changes.doctree +0 -0
- package/doc/dist/doctrees/contribute.doctree +0 -0
- package/doc/dist/doctrees/environment.pickle +0 -0
- package/doc/dist/doctrees/functions.doctree +0 -0
- package/doc/dist/doctrees/index.doctree +0 -0
- package/doc/dist/doctrees/memory.doctree +0 -0
- package/doc/dist/doctrees/types.doctree +0 -0
- package/doc/dist/html/.buildinfo +1 -1
- package/doc/dist/html/_sources/benchmarks.md.txt +2 -2
- package/doc/dist/html/_sources/contribute.md.txt +1 -2
- package/doc/dist/html/_sources/functions.md.txt +137 -15
- package/doc/dist/html/_sources/index.rst.txt +2 -0
- package/doc/dist/html/_sources/memory.md.txt +1 -1
- package/doc/dist/html/_sources/types.md.txt +27 -11
- package/doc/dist/html/_static/basic.css +14 -12
- package/doc/dist/html/_static/pygments.css +54 -54
- package/doc/dist/html/benchmarks.html +3 -3
- package/doc/dist/html/changes.html +1 -1
- package/doc/dist/html/contribute.html +2 -3
- package/doc/dist/html/functions.html +206 -86
- package/doc/dist/html/genindex.html +1 -1
- package/doc/dist/html/index.html +6 -7
- package/doc/dist/html/memory.html +4 -4
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +2 -2
- package/doc/dist/html/search.html +1 -1
- package/doc/dist/html/searchindex.js +1 -1
- package/doc/dist/html/start.html +55 -55
- package/doc/dist/html/types.html +172 -159
- package/doc/functions.md +9 -7
- package/doc/index.rst +2 -0
- package/doc/memory.md +1 -1
- package/doc/poetry.lock +692 -0
- package/doc/pyproject.toml +18 -0
- package/doc/{_static → static}/bench_linux.png +0 -0
- package/doc/{_static → static}/bench_windows.png +0 -0
- package/doc/{_static → static}/custom.css +0 -0
- package/doc/{_static → static}/perf_linux_20220623.png +0 -0
- package/doc/{_static → static}/perf_linux_20220623_2.png +0 -0
- package/doc/{_static → static}/perf_linux_20220627.png +0 -0
- package/doc/{_static → static}/perf_linux_20220628.png +0 -0
- package/doc/{_static → static}/perf_windows_20220623.png +0 -0
- package/doc/{_static → static}/perf_windows_20220623_2.png +0 -0
- package/doc/{_static → static}/perf_windows_20220627.png +0 -0
- package/doc/{_static → static}/perf_windows_20220628.png +0 -0
- package/doc/types.md +1 -1
- package/package.json +9 -7
- package/qemu/qemu.js +3 -3
- package/src/abi_arm32.cc +3 -3
- package/src/abi_riscv64.cc +1 -1
- package/src/abi_x64_sysv.cc +1 -1
- package/src/abi_x64_win.cc +1 -1
- package/src/abi_x64_win_fwd.asm +2 -2
- package/src/abi_x86.cc +5 -5
- package/src/parser.cc +2 -1
- package/test/misc.c +1 -1
- package/test/sync.js +2 -2
- package/build/qemu/1.3.9/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.3.9/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Koffi 1.3.12
|
|
4
|
+
|
|
5
|
+
**Main fixes:**
|
|
6
|
+
|
|
7
|
+
- Fix support for Yarn package manager
|
|
8
|
+
|
|
9
|
+
## Koffi 1.3.11
|
|
10
|
+
|
|
11
|
+
**Main fixes:**
|
|
12
|
+
|
|
13
|
+
- Fix broken parsing of `void *` when used for first parameter
|
|
14
|
+
|
|
15
|
+
## Koffi 1.3.10
|
|
16
|
+
|
|
17
|
+
**Main fixes:**
|
|
18
|
+
|
|
19
|
+
- Fix support for callbacks with more than 4 parameters on Windows x64
|
|
20
|
+
- Fix support for callbacks with multiple floating-point arguments on ARM32 platforms
|
|
21
|
+
- Fix possibly incorrect conversion for uint32_t callback parameters
|
|
22
|
+
|
|
23
|
+
**Other changes:**
|
|
24
|
+
|
|
25
|
+
- Various documentation fixes and improvements
|
|
26
|
+
|
|
3
27
|
## Koffi 1.3.9
|
|
4
28
|
|
|
5
29
|
**Main fixes:**
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/benchmarks.md
CHANGED
|
@@ -10,8 +10,8 @@ Here is a quick overview of the execution time of Koffi calls on three benchmark
|
|
|
10
10
|
|
|
11
11
|
<table style="margin: 0 auto;">
|
|
12
12
|
<tr>
|
|
13
|
-
<td><a href="_static/perf_linux_20220628.png" target="_blank"><img src="_static/perf_linux_20220628.png" alt="Linux performance" style="width: 350px;"/></a></td>
|
|
14
|
-
<td><a href="_static/perf_windows_20220628.png" target="_blank"><img src="_static/perf_windows_20220628.png" alt="Windows performance" style="width: 350px;"/></a></td>
|
|
13
|
+
<td><a href="_static/perf_linux_20220628.png" target="_blank"><img src="_static/perf_linux_20220628.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
|
|
14
|
+
<td><a href="_static/perf_windows_20220628.png" target="_blank"><img src="_static/perf_windows_20220628.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
|
|
15
15
|
</tr>
|
|
16
16
|
</table>
|
|
17
17
|
|
package/doc/conf.py
CHANGED
package/doc/contribute.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Use the official repository (named Luigi, because this is a monorepo containing multiple projects) for bugs, ideas and features requests.
|
|
6
6
|
|
|
7
|
-
Go here: https://github.com/Koromix/luigi/
|
|
7
|
+
Go here: https://github.com/Koromix/luigi/
|
|
8
8
|
|
|
9
9
|
## Build from source
|
|
10
10
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/dist/html/.buildinfo
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# Sphinx build info version 1
|
|
2
2
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
|
3
|
-
config:
|
|
3
|
+
config: 3958eced2fda3c6c0751ec1a88d416ca
|
|
4
4
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
|
@@ -10,8 +10,8 @@ Here is a quick overview of the execution time of Koffi calls on three benchmark
|
|
|
10
10
|
|
|
11
11
|
<table style="margin: 0 auto;">
|
|
12
12
|
<tr>
|
|
13
|
-
<td><a href="
|
|
14
|
-
<td><a href="
|
|
13
|
+
<td><a href="static/perf_linux_20220628.png" target="_blank"><img src="static/perf_linux_20220628.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
|
|
14
|
+
<td><a href="static/perf_windows_20220628.png" target="_blank"><img src="static/perf_windows_20220628.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
|
|
15
15
|
</tr>
|
|
16
16
|
</table>
|
|
17
17
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Use the official repository (named Luigi, because this is a monorepo containing multiple projects) for bugs, ideas and features requests.
|
|
6
6
|
|
|
7
|
-
Go here: https://github.com/Koromix/luigi/
|
|
7
|
+
Go here: https://github.com/Koromix/luigi/
|
|
8
8
|
|
|
9
9
|
## Build from source
|
|
10
10
|
|
|
@@ -111,7 +111,6 @@ node qemu.js info debian_x64
|
|
|
111
111
|
|
|
112
112
|
The following features and improvements are planned, not necessarily in that order:
|
|
113
113
|
|
|
114
|
-
- Provide better ways to automatically deal with caller/heap-allocated memory (strings, etc.)
|
|
115
114
|
- Optimize passing of structs and arrays (avoid setting named properties one by one? separate HFA-specific helper functions?)
|
|
116
115
|
- Automate Windows/AArch64 (qemu) and macOS/AArch64 (how? ... thanks Apple) tests
|
|
117
116
|
- Create a real-world example, using several libraries (Raylib, SQLite, libsodium) to illustrate various C API styles
|
|
@@ -23,7 +23,7 @@ const printf = lib.func('printf', 'int', ['str', '...']);
|
|
|
23
23
|
const atoi = lib.func('atoi', 'int', ['str']);
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Koffi automatically tries mangled names for non-standard x86 calling conventions. See the section
|
|
26
|
+
Koffi automatically tries mangled names for non-standard x86 calling conventions. See the section on [calling conventions](#calling-conventions) for more information on this subject.
|
|
27
27
|
|
|
28
28
|
### C-like prototypes
|
|
29
29
|
|
|
@@ -36,7 +36,9 @@ const atoi = lib.func('int atoi(str)'); // The parameter name is not used by Kof
|
|
|
36
36
|
|
|
37
37
|
You can use `()` or `(void)` for functions that take no argument.
|
|
38
38
|
|
|
39
|
-
##
|
|
39
|
+
## Function calls
|
|
40
|
+
|
|
41
|
+
### Calling conventions
|
|
40
42
|
|
|
41
43
|
By default, calling a C function happens synchronously.
|
|
42
44
|
|
|
@@ -57,12 +59,12 @@ Below you can find a small example showing how to use a non-default calling conv
|
|
|
57
59
|
const koffi = require('koffi');
|
|
58
60
|
const lib = koffi.load('user32.dll');
|
|
59
61
|
|
|
60
|
-
// The following two declarations are equivalent, and use
|
|
62
|
+
// The following two declarations are equivalent, and use stdcall on x86 (and the default ABI on other platforms)
|
|
61
63
|
const MessageBoxA_1 = lib.stdcall('MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
|
|
62
64
|
const MessageBoxA_2 = lib.func('int __stdcall MessageBoxA(void *hwnd, str text, str caption, uint type)');
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
### Asynchronous calls
|
|
66
68
|
|
|
67
69
|
You can issue asynchronous calls by calling the function through its async member. In this case, you need to provide a callback function as the last argument, with `(err, res)` parameters.
|
|
68
70
|
|
|
@@ -88,7 +90,7 @@ You can easily convert this callback-style async function to a promise-based ver
|
|
|
88
90
|
|
|
89
91
|
Variadic functions cannot be called asynchronously.
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
### Variadic functions
|
|
92
94
|
|
|
93
95
|
Variadic functions are declared with an ellipsis as the last argument.
|
|
94
96
|
|
|
@@ -103,7 +105,9 @@ printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END
|
|
|
103
105
|
|
|
104
106
|
On x86 platforms, only the Cdecl convention can be used for variadic functions.
|
|
105
107
|
|
|
106
|
-
##
|
|
108
|
+
## C to JS conversion gotchas
|
|
109
|
+
|
|
110
|
+
### Output parameters
|
|
107
111
|
|
|
108
112
|
By default, Koffi will only forward arguments from Javascript to C. However, many C functions use pointer arguments for output values, or input/output values.
|
|
109
113
|
|
|
@@ -122,7 +126,7 @@ The same can be done when declaring a function with a C-like prototype string, w
|
|
|
122
126
|
- `_Out_` for output parameters
|
|
123
127
|
- `_Inout_` for dual input/output parameters
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
#### Struct example
|
|
126
130
|
|
|
127
131
|
This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
|
|
128
132
|
|
|
@@ -148,7 +152,7 @@ gettimeofday(tv, null);
|
|
|
148
152
|
console.log(tv);
|
|
149
153
|
```
|
|
150
154
|
|
|
151
|
-
|
|
155
|
+
#### Opaque handle example
|
|
152
156
|
|
|
153
157
|
This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
|
|
154
158
|
|
|
@@ -158,9 +162,9 @@ const lib = koffi.load('sqlite.so');
|
|
|
158
162
|
|
|
159
163
|
const sqlite3_db = koffi.handle('sqlite3_db');
|
|
160
164
|
|
|
161
|
-
// Use koffi.out() on a pointer to copy out (from C to JS) after the call
|
|
162
|
-
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'str']);
|
|
163
|
-
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
|
|
165
|
+
// Use koffi.out() on a double pointer to copy out (from C to JS) after the call
|
|
166
|
+
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db, 2)), 'int', 'str']);
|
|
167
|
+
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3_db)]);
|
|
164
168
|
|
|
165
169
|
const SQLITE_OPEN_READWRITE = 0x2;
|
|
166
170
|
const SQLITE_OPEN_CREATE = 0x4;
|
|
@@ -171,6 +175,55 @@ if (sqlite3_open_v2(':memory:', db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
|
|
171
175
|
sqlite3_close_v2(db);
|
|
172
176
|
```
|
|
173
177
|
|
|
178
|
+
### Heap-allocated values
|
|
179
|
+
|
|
180
|
+
Some C functions return heap-allocated values directly or through output parameters. While Koffi automatically converts values from C to JS (to a string or an object), it does not know when something needs to be freed, or how.
|
|
181
|
+
|
|
182
|
+
For opaque handles, such as FILE, this does not matter because you will explicitly call `fclose()` on them. But some values (such as strings) get implicitly converted by Koffi, and you lose access to the original pointer. This creates a leak if the string is heap-allocated.
|
|
183
|
+
|
|
184
|
+
To avoid this, you can instruct Koffi to call a function on the original pointer once the conversion is done, by creating a disposable type with `koffi.dispose(name, type, func)`. This creates a type derived from another type, the only difference being that *func* gets called with the original pointer once the value has been converted and is not needed anymore.
|
|
185
|
+
|
|
186
|
+
The *name* can be omitted to create an anonymous disposable type. If *func* is omitted or is null, Koffi will use `koffi.free(ptr)` (which calls the standard C library *free* function under the hood).
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
const AnonHeapStr = koffi.disposable('str'); // Anonymous type (cannot be used in function prototypes)
|
|
190
|
+
const NamedHeapStr = koffi.disposable('HeapStr', 'str'); // Same thing, but named so usable in function prototypes
|
|
191
|
+
const ExplicitFree = koffi.disposable('HeapStr16', 'str16', koffi.free); // You can specify any other JS function
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The following example illustrates the use of a disposable type derived from *str*.
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
const koffi = require('koffi');
|
|
198
|
+
const lib = koffi.load('libc.so.6');
|
|
199
|
+
|
|
200
|
+
// You can also use: const strdup = lib.func('const char *! asprintf(const char *str)')
|
|
201
|
+
const HeapStr = koffi.disposable('str');
|
|
202
|
+
const strdup = lib.cdecl('strdup', HeapStr, ['str']);
|
|
203
|
+
|
|
204
|
+
let copy = strdup('Hello!');
|
|
205
|
+
console.log(copy); // Prints Hello!
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
When you declare functions with the [prototype-like syntax](#c-like-prototypes), you can either use named disposables types or use the '!' shortcut qualifier with compatibles types, as shown in the example below. This qualifier creates an anonymous disposable type that calls `koffi.free(ptr)`.
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
const koffi = require('koffi');
|
|
212
|
+
const lib = koffi.load('libc.so.6');
|
|
213
|
+
|
|
214
|
+
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
215
|
+
const strdup = lib.func('str! strdup(const char *str)');
|
|
216
|
+
|
|
217
|
+
let copy = strdup('World!');
|
|
218
|
+
console.log(copy); // Prints World!
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Disposable types can only be created from pointer or string types.
|
|
222
|
+
|
|
223
|
+
```{warning}
|
|
224
|
+
Be careful on Windows: if your shared library uses a different CRT (such as msvcrt), the memory could have been allocated by a different malloc/free implementation or heap, resulting in undefined behavior if you use `koffi.free()`.
|
|
225
|
+
```
|
|
226
|
+
|
|
174
227
|
## Javascript callbacks
|
|
175
228
|
|
|
176
229
|
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.
|
|
@@ -185,9 +238,28 @@ const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
|
|
|
185
238
|
const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
|
|
186
239
|
```
|
|
187
240
|
|
|
188
|
-
Once your callback type is declared, you can use it in struct definitions, or as function
|
|
241
|
+
Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
|
|
242
|
+
|
|
243
|
+
```{note}
|
|
244
|
+
Callbacks **have changed in version 2.0**.
|
|
245
|
+
|
|
246
|
+
In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
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.
|
|
252
|
+
|
|
253
|
+
Thus, Koffi distinguishes two callback modes:
|
|
189
254
|
|
|
190
|
-
|
|
255
|
+
- [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 no guaranteed. However, they are simple to use, and don't require any special handling.
|
|
256
|
+
- [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.
|
|
257
|
+
|
|
258
|
+
You need to specify the correct [calling convention](#calling-conventions) on x86 platforms, or the behavior is undefined (Node will probably crash). Only *cdecl* and *stdcall* callbacks are supported.
|
|
259
|
+
|
|
260
|
+
### Transient callbacks
|
|
261
|
+
|
|
262
|
+
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.
|
|
191
263
|
|
|
192
264
|
```c
|
|
193
265
|
#include <string.h>
|
|
@@ -202,10 +274,11 @@ int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
|
|
|
202
274
|
|
|
203
275
|
```js
|
|
204
276
|
const koffi = require('koffi');
|
|
277
|
+
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
205
278
|
|
|
206
279
|
const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
|
|
207
280
|
|
|
208
|
-
const TransferToJS = lib.func('
|
|
281
|
+
const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
|
|
209
282
|
|
|
210
283
|
let ret = TransferToJS('Niels', 27, (str, age) => {
|
|
211
284
|
console.log(str);
|
|
@@ -220,7 +293,56 @@ console.log(ret);
|
|
|
220
293
|
// 42
|
|
221
294
|
```
|
|
222
295
|
|
|
223
|
-
|
|
296
|
+
### Registered callbacks
|
|
297
|
+
|
|
298
|
+
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.
|
|
299
|
+
|
|
300
|
+
When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of 16 registered callbacks can exist at the same time. Failure to do so will leak the slot, and subsequent registrations may fail (with an exception) once all slots are used.
|
|
301
|
+
|
|
302
|
+
The example below shows how to register and unregister delayed callbacks.
|
|
303
|
+
|
|
304
|
+
```c
|
|
305
|
+
static const char *(*g_cb1)(const char *name);
|
|
306
|
+
static void (*g_cb2)(const char *str);
|
|
307
|
+
|
|
308
|
+
void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
|
|
309
|
+
{
|
|
310
|
+
g_cb1 = cb1;
|
|
311
|
+
g_cb2 = cb2;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
void SayIt(const char *name)
|
|
315
|
+
{
|
|
316
|
+
const char *str = g_cb1(name);
|
|
317
|
+
g_cb2(str);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
const koffi = require('koffi');
|
|
323
|
+
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
324
|
+
|
|
325
|
+
const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
|
|
326
|
+
const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
|
|
327
|
+
|
|
328
|
+
const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
|
|
329
|
+
const SayIt = lib.func('void SayIt(const char *name)');
|
|
330
|
+
|
|
331
|
+
let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
|
|
332
|
+
let cb2 = koffi.register(console.log, 'PrintCallback *');
|
|
333
|
+
|
|
334
|
+
RegisterFunctions(cb1, cb2);
|
|
335
|
+
SayIt('Kyoto'); // Prints Hello Kyoto!
|
|
336
|
+
|
|
337
|
+
koffi.unregister(cb1);
|
|
338
|
+
koffi.unregister(cb2);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Handling of exceptions
|
|
342
|
+
|
|
343
|
+
If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
|
|
344
|
+
|
|
345
|
+
Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
|
|
224
346
|
|
|
225
347
|
## Thread safety
|
|
226
348
|
|
|
@@ -14,6 +14,8 @@ Koffi is a **fast and easy-to-use C FFI module for Node.js**, featuring:
|
|
|
14
14
|
|
|
15
15
|
Koffi requires a recent `Node.js <https://nodejs.org/>`_ version with N-API version 8 support, see :ref:`this page<Node.js>` for more information.
|
|
16
16
|
|
|
17
|
+
The source code is available here: https://github.com/Koromix/luigi/ (in the *koffi* subdirectory).
|
|
18
|
+
|
|
17
19
|
Table of contents
|
|
18
20
|
-----------------
|
|
19
21
|
|
|
@@ -7,7 +7,7 @@ For synchronous/normal calls, Koffi uses two preallocated memory blocks:
|
|
|
7
7
|
- One to construct the C stack and assign registers, subsequently used by the platform-specific assembly code (1 MiB by default)
|
|
8
8
|
- One to allocate strings and big objects/structs (2 MiB by default)
|
|
9
9
|
|
|
10
|
-
Unless very big strings or objects (at least more than one page of memory) are used,
|
|
10
|
+
Unless very big strings or objects (at least more than one page of memory) are used, Koffi does not directly allocate any extra memory during calls or callbacks. However, please note that the JS engine (V8) might.
|
|
11
11
|
|
|
12
12
|
The size (in bytes) of these preallocated blocks can be changed. Use `koffi.config()` to get an object with the settings, and `koffi.config(obj)` to apply new settings.
|
|
13
13
|
|
|
@@ -63,7 +63,7 @@ Primitive types can be specified by name (in a string) or through `koffi.types`:
|
|
|
63
63
|
|
|
64
64
|
```js
|
|
65
65
|
// These two lines do the same:
|
|
66
|
-
let struct1 = koffi.struct({ dummy: '
|
|
66
|
+
let struct1 = koffi.struct({ dummy: 'long' });
|
|
67
67
|
let struct2 = koffi.struct({ dummy: koffi.types.long });
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -91,7 +91,7 @@ typedef struct A {
|
|
|
91
91
|
const A = koffi.struct('A', {
|
|
92
92
|
a: 'int',
|
|
93
93
|
b: 'char',
|
|
94
|
-
c: '
|
|
94
|
+
c: 'const char *', // Koffi does not care about const, it is ignored
|
|
95
95
|
d: koffi.struct({
|
|
96
96
|
d1: 'double',
|
|
97
97
|
d2: 'double'
|
|
@@ -146,7 +146,15 @@ console.log(pos);
|
|
|
146
146
|
|
|
147
147
|
Many C libraries use some kind of object-oriented API, with a pair of functions dedicated to create and delete objects. An obvious example of this can be found in stdio.h, with the opaque `FILE *` pointer. You can open and close files with `fopen()` and `fclose()`, and manipule the handle with other functions such as `fread()` or `ftell()`.
|
|
148
148
|
|
|
149
|
-
In Koffi, you can manage this with opaque handles. Declare the handle type with `koffi.handle(name)`, and use this type either as a return type or some kind of [output parameter](functions.md#output-parameters) (with a pointer
|
|
149
|
+
In Koffi, you can manage this with opaque handles. Declare the handle type with `koffi.handle(name)`, and use a pointer to this type either as a return type or some kind of [output parameter](functions.md#output-parameters) (with a double pointer).
|
|
150
|
+
|
|
151
|
+
```{note}
|
|
152
|
+
Opaque handles **have changed in version 2.0**.
|
|
153
|
+
|
|
154
|
+
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.
|
|
155
|
+
|
|
156
|
+
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](functions.md#output-parameters).
|
|
157
|
+
```
|
|
150
158
|
|
|
151
159
|
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 handle, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
|
|
152
160
|
|
|
@@ -274,17 +282,19 @@ const koffi = require('koffi');
|
|
|
274
282
|
const lib = koffi.load('./handles.so');
|
|
275
283
|
|
|
276
284
|
const Concat = koffi.handle('Concat');
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const
|
|
285
|
+
const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat **out)');
|
|
286
|
+
const ConcatNew = lib.func('Concat *ConcatNew()');
|
|
287
|
+
const ConcatFree = lib.func('void ConcatFree(Concat *c)');
|
|
288
|
+
const ConcatAppend = lib.func('bool ConcatAppend(Concat *c, const char *frag)');
|
|
289
|
+
const ConcatBuild = lib.func('const char *ConcatBuild(Concat *c)');
|
|
282
290
|
|
|
283
291
|
let c = ConcatNew();
|
|
284
292
|
if (!c) {
|
|
285
293
|
// This is stupid, it does the same, but try both versions (return value, output parameter)
|
|
286
|
-
|
|
294
|
+
let ptr = [null];
|
|
295
|
+
if (!ConcatNewOut(ptr))
|
|
287
296
|
throw new Error('Allocation failure');
|
|
297
|
+
c = ptr[0];
|
|
288
298
|
}
|
|
289
299
|
|
|
290
300
|
try {
|
|
@@ -368,7 +378,7 @@ Here is an example based on the Win32 API, listing files in the current director
|
|
|
368
378
|
const koffi = require('koffi');
|
|
369
379
|
const lib = koffi.load('kernel32.dll');
|
|
370
380
|
|
|
371
|
-
const HANDLE = koffi.
|
|
381
|
+
const HANDLE = koffi.pointer('HANDLE', koffi.handle());
|
|
372
382
|
const FILETIME = koffi.struct('FILETIME', {
|
|
373
383
|
dwLowDateTime: 'uint',
|
|
374
384
|
dwHighDateTime: 'uint'
|
|
@@ -509,7 +519,13 @@ Koffi exposes three functions to explore type information:
|
|
|
509
519
|
|
|
510
520
|
- `koffi.sizeof(type)` to get the size of a type
|
|
511
521
|
- `koffi.alignof(type)` to get the alignment of a type
|
|
512
|
-
- `koffi.introspect(type)` to get the definition of a type
|
|
522
|
+
- `koffi.introspect(type)` to get the definition of a type in an object containing: name, primitive, size, alignment, members (structs), reference (array, pointer) and length (array)
|
|
523
|
+
|
|
524
|
+
```{note}
|
|
525
|
+
The value returned by `introspect()` has **changed in version 2.0**.
|
|
526
|
+
|
|
527
|
+
In Koffi 1.x, it could only be used with struct types and returned the object passed to koffi.struct() with the member names and types.
|
|
528
|
+
```
|
|
513
529
|
|
|
514
530
|
Just like before, you can refer to primitive types by their name or through `koffi.types`:
|
|
515
531
|
|
|
@@ -237,6 +237,16 @@ a.headerlink {
|
|
|
237
237
|
visibility: hidden;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
a.brackets:before,
|
|
241
|
+
span.brackets > a:before{
|
|
242
|
+
content: "[";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
a.brackets:after,
|
|
246
|
+
span.brackets > a:after {
|
|
247
|
+
content: "]";
|
|
248
|
+
}
|
|
249
|
+
|
|
240
250
|
h1:hover > a.headerlink,
|
|
241
251
|
h2:hover > a.headerlink,
|
|
242
252
|
h3:hover > a.headerlink,
|
|
@@ -324,18 +334,14 @@ aside.sidebar {
|
|
|
324
334
|
p.sidebar-title {
|
|
325
335
|
font-weight: bold;
|
|
326
336
|
}
|
|
327
|
-
nav.contents,
|
|
328
|
-
aside.topic,
|
|
329
337
|
|
|
330
|
-
div.admonition, div.topic, blockquote {
|
|
338
|
+
div.admonition, div.topic, aside.topic, blockquote {
|
|
331
339
|
clear: left;
|
|
332
340
|
}
|
|
333
341
|
|
|
334
342
|
/* -- topics ---------------------------------------------------------------- */
|
|
335
|
-
nav.contents,
|
|
336
|
-
aside.topic,
|
|
337
343
|
|
|
338
|
-
div.topic {
|
|
344
|
+
div.topic, aside.topic {
|
|
339
345
|
border: 1px solid #ccc;
|
|
340
346
|
padding: 7px;
|
|
341
347
|
margin: 10px 0 10px 0;
|
|
@@ -373,20 +379,16 @@ div.body p.centered {
|
|
|
373
379
|
|
|
374
380
|
div.sidebar > :last-child,
|
|
375
381
|
aside.sidebar > :last-child,
|
|
376
|
-
nav.contents > :last-child,
|
|
377
|
-
aside.topic > :last-child,
|
|
378
|
-
|
|
379
382
|
div.topic > :last-child,
|
|
383
|
+
aside.topic > :last-child,
|
|
380
384
|
div.admonition > :last-child {
|
|
381
385
|
margin-bottom: 0;
|
|
382
386
|
}
|
|
383
387
|
|
|
384
388
|
div.sidebar::after,
|
|
385
389
|
aside.sidebar::after,
|
|
386
|
-
nav.contents::after,
|
|
387
|
-
aside.topic::after,
|
|
388
|
-
|
|
389
390
|
div.topic::after,
|
|
391
|
+
aside.topic::after,
|
|
390
392
|
div.admonition::after,
|
|
391
393
|
blockquote::after {
|
|
392
394
|
display: block;
|