koffi 2.1.4 → 2.2.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/{src/koffi/ChangeLog.md → ChangeLog.md} +21 -1
- package/{src/koffi/LICENSE.txt → LICENSE.txt} +0 -0
- package/{src/koffi/README.md → README.md} +0 -0
- package/{src/koffi/doc → doc}/Makefile +1 -1
- package/{src/koffi/doc → doc}/benchmarks.md +0 -0
- package/{src/koffi/doc → doc}/benchmarks.xlsx +0 -0
- package/doc/callbacks.md +175 -0
- package/{src/koffi/doc → doc}/changes.md +2 -3
- package/{src/koffi/doc → doc}/conf.py +29 -6
- package/{src/koffi/doc → doc}/contribute.md +0 -0
- package/{src/koffi/doc → doc}/functions.md +39 -124
- package/{src/koffi/doc → doc}/index.rst +1 -0
- package/{src/koffi/doc → doc}/make.bat +1 -1
- package/{src/koffi/doc → doc}/memory.md +0 -0
- package/{src/koffi/doc → doc}/platforms.md +0 -0
- package/{src/koffi/doc → doc}/poetry.lock +0 -0
- package/{src/koffi/doc → doc}/pyproject.toml +0 -0
- package/{src/koffi/doc → doc}/start.md +0 -0
- package/{src/koffi/doc → doc}/static/bench_linux.png +0 -0
- package/{src/koffi/doc → doc}/static/bench_windows.png +0 -0
- package/{src/koffi/doc → doc}/static/custom.css +0 -0
- package/{src/koffi/doc → doc}/static/perf_linux_20220623.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_linux_20220623_2.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_linux_20220627.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_linux_20220628.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_linux_20220812.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_windows_20220623.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_windows_20220623_2.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_windows_20220627.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_windows_20220628.png +0 -0
- package/{src/koffi/doc → doc}/static/perf_windows_20220812.png +0 -0
- package/{src/koffi/doc → doc}/templates/badges.html +0 -0
- package/{src/koffi/doc → doc}/types.md +36 -9
- package/package.json +2 -2
- package/src/core/libcc/libcc.cc +89 -27
- package/src/core/libcc/libcc.hh +74 -39
- package/src/koffi/build/2.2.0/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_x64.tar.gz +0 -0
- package/src/koffi/qemu/qemu.js +17 -7
- package/src/koffi/src/abi_arm32.cc +25 -23
- package/src/koffi/src/abi_arm64.cc +24 -22
- package/src/koffi/src/abi_riscv64.cc +20 -18
- package/src/koffi/src/abi_x64_sysv.cc +19 -17
- package/src/koffi/src/abi_x64_win.cc +18 -16
- package/src/koffi/src/abi_x86.cc +22 -20
- package/src/koffi/src/call.cc +220 -607
- package/src/koffi/src/call.hh +7 -11
- package/src/koffi/src/ffi.cc +229 -29
- package/src/koffi/src/ffi.hh +6 -2
- package/src/koffi/src/parser.cc +3 -9
- package/src/koffi/src/util.cc +546 -8
- package/src/koffi/src/util.hh +8 -2
- package/src/koffi/test/CMakeLists.txt +3 -3
- package/src/koffi/test/callbacks.js +70 -0
- package/src/koffi/test/misc.c +67 -0
- package/src/koffi/test/raylib.js +2 -2
- package/src/koffi/test/sqlite.js +1 -1
- package/src/koffi/test/sync.js +28 -6
- package/vendor/brotli/c/common/platform.h +2 -0
- package/vendor/sqlite3mc/sqlite3.c +243532 -0
- package/vendor/sqlite3mc/sqlite3.h +12887 -0
- package/src/koffi/build/2.1.4/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.1.4/koffi_win32_x64.tar.gz +0 -0
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## History
|
|
4
4
|
|
|
5
|
+
### Koffi 2.2.0
|
|
6
|
+
|
|
7
|
+
**New features:**
|
|
8
|
+
|
|
9
|
+
- Add [koffi.decode()](callbacks.md#pointer-arguments) for callback pointer arguments
|
|
10
|
+
- Support transparent [output string parameters](functions.md#output-parameters)
|
|
11
|
+
- Add `koffi.offsetof()` utility function
|
|
12
|
+
- Support optional *this* binding in `koffi.register()`
|
|
13
|
+
|
|
14
|
+
**Other fixes:**
|
|
15
|
+
|
|
16
|
+
- Correctly validate output parameter types
|
|
17
|
+
- Fix assertion with `void *` parameters
|
|
18
|
+
|
|
19
|
+
### Koffi 2.1.5
|
|
20
|
+
|
|
21
|
+
**Main fixes:**
|
|
22
|
+
|
|
23
|
+
- Add missing README.md file to NPM package
|
|
24
|
+
|
|
5
25
|
### Koffi 2.1.4
|
|
6
26
|
|
|
7
27
|
**Main changes:**
|
|
@@ -60,7 +80,7 @@
|
|
|
60
80
|
**Major new features:**
|
|
61
81
|
|
|
62
82
|
- Add [disposable types](functions.md#heap-allocated-values) for automatic disposal of C values (such as heap-allocated strings)
|
|
63
|
-
- Add support for [registered callbacks](
|
|
83
|
+
- Add support for [registered callbacks](callbacks.md#registered-callbacks), that can be called after the initial FFI call
|
|
64
84
|
- Support named pointer types
|
|
65
85
|
- Support complex type specifications outside of prototype parser
|
|
66
86
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/doc/callbacks.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Callbacks
|
|
2
|
+
|
|
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.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
const koffi = require('koffi');
|
|
7
|
+
|
|
8
|
+
// With the classic syntax, this callback expects an integer and returns nothing
|
|
9
|
+
const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
|
|
10
|
+
|
|
11
|
+
// With the prototype parser, this callback expects a double and float, and returns the sum as a double
|
|
12
|
+
const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
|
|
16
|
+
|
|
17
|
+
```{note}
|
|
18
|
+
Callbacks **have changed in version 2.0**.
|
|
19
|
+
|
|
20
|
+
In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
Consult the [migration guide](changes.md) for more information.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Callback types
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
Thus, Koffi distinguishes two callback modes:
|
|
32
|
+
|
|
33
|
+
- [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.
|
|
34
|
+
- [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.
|
|
35
|
+
|
|
36
|
+
You need to specify the correct [calling convention](functions.md#calling-conventions) on x86 platforms, or the behavior is undefined (Node will probably crash). Only *cdecl* and *stdcall* callbacks are supported.
|
|
37
|
+
|
|
38
|
+
### Transient callbacks
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
```c
|
|
43
|
+
#include <string.h>
|
|
44
|
+
|
|
45
|
+
int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
|
|
46
|
+
{
|
|
47
|
+
char buf[64];
|
|
48
|
+
snprintf(buf, sizeof(buf), "Hello %s!", str);
|
|
49
|
+
return cb(buf, age);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const koffi = require('koffi');
|
|
55
|
+
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
56
|
+
|
|
57
|
+
const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
|
|
58
|
+
|
|
59
|
+
const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
|
|
60
|
+
|
|
61
|
+
let ret = TransferToJS('Niels', 27, (str, age) => {
|
|
62
|
+
console.log(str);
|
|
63
|
+
console.log('Your age is:', age);
|
|
64
|
+
return 42;
|
|
65
|
+
});
|
|
66
|
+
console.log(ret);
|
|
67
|
+
|
|
68
|
+
// This example prints:
|
|
69
|
+
// Hello Niels!
|
|
70
|
+
// Your age is: 27
|
|
71
|
+
// 42
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Registered callbacks
|
|
75
|
+
|
|
76
|
+
*New in Koffi 2.0 (explicit this binding in Koffi 2.2)*
|
|
77
|
+
|
|
78
|
+
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.
|
|
79
|
+
|
|
80
|
+
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.
|
|
81
|
+
|
|
82
|
+
The example below shows how to register and unregister delayed callbacks.
|
|
83
|
+
|
|
84
|
+
```c
|
|
85
|
+
static const char *(*g_cb1)(const char *name);
|
|
86
|
+
static void (*g_cb2)(const char *str);
|
|
87
|
+
|
|
88
|
+
void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
|
|
89
|
+
{
|
|
90
|
+
g_cb1 = cb1;
|
|
91
|
+
g_cb2 = cb2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void SayIt(const char *name)
|
|
95
|
+
{
|
|
96
|
+
const char *str = g_cb1(name);
|
|
97
|
+
g_cb2(str);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const koffi = require('koffi');
|
|
103
|
+
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
104
|
+
|
|
105
|
+
const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
|
|
106
|
+
const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
|
|
107
|
+
|
|
108
|
+
const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
|
|
109
|
+
const SayIt = lib.func('void SayIt(const char *name)');
|
|
110
|
+
|
|
111
|
+
let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
|
|
112
|
+
let cb2 = koffi.register(console.log, 'PrintCallback *');
|
|
113
|
+
|
|
114
|
+
RegisterFunctions(cb1, cb2);
|
|
115
|
+
SayIt('Kyoto'); // Prints Hello Kyoto!
|
|
116
|
+
|
|
117
|
+
koffi.unregister(cb1);
|
|
118
|
+
koffi.unregister(cb2);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Starting *with Koffi 2.2*, you can optionally specify the `this` value for the function as the first argument.
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
class ValueStore {
|
|
125
|
+
constructor(value) { this.value = value; }
|
|
126
|
+
get() { return this.value; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let store = new ValueStore(42);
|
|
130
|
+
|
|
131
|
+
let cb1 = koffi.register(store.get, 'IntCallback *'); // If a C function calls cb1 it will fail because this will be undefined
|
|
132
|
+
let cb2 = koffi.register(store, store.get, 'IntCallback *'); // However in this case, this will match the store object
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Pointer arguments
|
|
136
|
+
|
|
137
|
+
*New in Koffi 2.2*
|
|
138
|
+
|
|
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 an opaque *External* object.
|
|
140
|
+
|
|
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(value, offset, type, len)` to decode it into something you can use in Javascript.
|
|
142
|
+
|
|
143
|
+
Some arguments are optional and this function can be called in several ways:
|
|
144
|
+
|
|
145
|
+
- `koffi.decode(value, offset, type, len)`: this is the full signature, value is the JS external object, offset (optional, defaults to 0) is specified in bytes, type is the value type, and length (optional) can be used for arrays and strings.
|
|
146
|
+
- `koffi.decode(value, type)`: no offset, expect NUL-terminated strings
|
|
147
|
+
- `koffi.decode(value, type, len)`: use specified length when decoding strings and arrays
|
|
148
|
+
- `koffi.decode(value, offset, type)`
|
|
149
|
+
|
|
150
|
+
The following example sorts an array of strings (in-place) with `qsort()`:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
const koffi = require('koffi');
|
|
154
|
+
const lib = koffi.load('libc.so.6');
|
|
155
|
+
|
|
156
|
+
const SortCallback = koffi.callback('int SortCallback(const void *first, const void *second)');
|
|
157
|
+
const qsort = lib.func('void qsort(_Inout_ void *array, size_t count, size_t size, SortCallback *cb)');
|
|
158
|
+
|
|
159
|
+
let array = ['foo', 'bar', '123', 'foobar'];
|
|
160
|
+
|
|
161
|
+
qsort(koffi.as(array, 'char **'), array.length, koffi.sizeof('void *'), (ptr1, ptr2) => {
|
|
162
|
+
let str1 = koffi.decode(ptr1, 'char *');
|
|
163
|
+
let str2 = koffi.decode(ptr2, 'char *');
|
|
164
|
+
|
|
165
|
+
return str1.localeCompare(str2);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
console.log(array); // Prints ['123', 'bar', 'foo', 'foobar']
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Handling of exceptions
|
|
172
|
+
|
|
173
|
+
If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
|
|
174
|
+
|
|
175
|
+
Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
```{include}
|
|
2
|
-
```
|
|
1
|
+
{{ "```{include} " ~ root ~ "/ChangeLog.md" ~ "\n```" }}
|
|
3
2
|
|
|
4
3
|
## Migration guide
|
|
5
4
|
|
|
@@ -64,7 +63,7 @@ let ret = TransferToJS('Niels', 27, (str, age) => {
|
|
|
64
63
|
console.log(ret);
|
|
65
64
|
```
|
|
66
65
|
|
|
67
|
-
Koffi 1.x only supported [transient callbacks](
|
|
66
|
+
Koffi 1.x only supported [transient callbacks](callbacks.md#callbacks), you must use Koffi 2.x for registered callbacks.
|
|
68
67
|
|
|
69
68
|
#### Opaque types
|
|
70
69
|
|
|
@@ -7,12 +7,31 @@ project = 'Koffi'
|
|
|
7
7
|
copyright = '2022, Niels Martignène'
|
|
8
8
|
author = 'Niels Martignène'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
root = None
|
|
11
|
+
version = None
|
|
12
|
+
revision = None
|
|
13
|
+
stable = None
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
names = ['../../src/koffi/package.json', '../package.json']
|
|
16
|
+
|
|
17
|
+
for name in names:
|
|
18
|
+
filename = os.path.dirname(__file__) + '/' + name
|
|
19
|
+
|
|
20
|
+
if not os.path.exists(filename):
|
|
21
|
+
continue;
|
|
22
|
+
|
|
23
|
+
with open(filename) as f:
|
|
24
|
+
config = json.load(f)
|
|
25
|
+
|
|
26
|
+
root = os.path.dirname(name)
|
|
27
|
+
version = config['version']
|
|
28
|
+
revision = config['version']
|
|
29
|
+
stable = config['stable']
|
|
30
|
+
|
|
31
|
+
break
|
|
32
|
+
|
|
33
|
+
if root is None:
|
|
34
|
+
raise FileNotFoundError('Cannot find Koffi package.json')
|
|
16
35
|
|
|
17
36
|
# -- General configuration ---------------------------------------------------
|
|
18
37
|
|
|
@@ -63,15 +82,19 @@ html_sidebars = {
|
|
|
63
82
|
}
|
|
64
83
|
|
|
65
84
|
html_context = {
|
|
85
|
+
"root": root,
|
|
66
86
|
"stable": stable
|
|
67
87
|
}
|
|
68
88
|
|
|
69
89
|
# -- MyST parser options -------------------------------------------------
|
|
70
90
|
|
|
71
91
|
myst_enable_extensions = [
|
|
72
|
-
'linkify'
|
|
92
|
+
'linkify',
|
|
93
|
+
'substitution'
|
|
73
94
|
]
|
|
74
95
|
|
|
96
|
+
myst_substitutions = html_context
|
|
97
|
+
|
|
75
98
|
myst_heading_anchors = 3
|
|
76
99
|
|
|
77
100
|
myst_linkify_fuzzy_links = False
|
|
File without changes
|
|
@@ -115,6 +115,7 @@ For simplicity, and because Javascript only has value semantics for primitive ty
|
|
|
115
115
|
|
|
116
116
|
- [Structs](types.md#struct-types) (to/from JS objects)
|
|
117
117
|
- [Opaque types](types.md#opaque-types)
|
|
118
|
+
- String buffers
|
|
118
119
|
|
|
119
120
|
In order to change an argument from input-only to output or input/output, use the following functions:
|
|
120
121
|
|
|
@@ -177,6 +178,44 @@ let db = out[0];
|
|
|
177
178
|
sqlite3_close_v2(db);
|
|
178
179
|
```
|
|
179
180
|
|
|
181
|
+
#### String buffer example
|
|
182
|
+
|
|
183
|
+
*New in Koffi 2.2*
|
|
184
|
+
|
|
185
|
+
This example calls a C function to concatenate two strings to a pre-allocated string buffer. Since JS strings are immutable, you must pass an array with a single string instead.
|
|
186
|
+
|
|
187
|
+
```c
|
|
188
|
+
void ConcatToBuffer(const char *str1, const char *str2, char *out)
|
|
189
|
+
{
|
|
190
|
+
size_t len = 0;
|
|
191
|
+
|
|
192
|
+
for (size_t i = 0; str1[i]; i++) {
|
|
193
|
+
out[len++] = str1[i];
|
|
194
|
+
}
|
|
195
|
+
for (size_t i = 0; str2[i]; i++) {
|
|
196
|
+
out[len++] = str2[i];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
out[len] = 0;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
const ConcatToBuffer = lib.func('void ConcatToBuffer(const char *str1, const char *str2, _Out_ char *out)');
|
|
205
|
+
|
|
206
|
+
let str1 = 'Hello ';
|
|
207
|
+
let str2 = 'Friends!';
|
|
208
|
+
|
|
209
|
+
// We need to reserve space for the output buffer! Including the NUL terminator
|
|
210
|
+
// because ConcatToBuffer() expects so, but Koffi can convert back to a JS string
|
|
211
|
+
// without it (if we reserve the right size).
|
|
212
|
+
let out = ['\0'.repeat(str1.length + str2.length + 1)];
|
|
213
|
+
|
|
214
|
+
ConcatToBuffer(str1, str2, out);
|
|
215
|
+
|
|
216
|
+
console.log(out[0]);
|
|
217
|
+
```
|
|
218
|
+
|
|
180
219
|
### Polymorphic parameters
|
|
181
220
|
|
|
182
221
|
*New in Koffi 2.1*
|
|
@@ -290,130 +329,6 @@ Disposable types can only be created from pointer or string types.
|
|
|
290
329
|
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()`.
|
|
291
330
|
```
|
|
292
331
|
|
|
293
|
-
## Javascript callbacks
|
|
294
|
-
|
|
295
|
-
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.
|
|
296
|
-
|
|
297
|
-
```js
|
|
298
|
-
const koffi = require('koffi');
|
|
299
|
-
|
|
300
|
-
// With the classic syntax, this callback expects an integer and returns nothing
|
|
301
|
-
const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
|
|
302
|
-
|
|
303
|
-
// With the prototype parser, this callback expects a double and float, and returns the sum as a double
|
|
304
|
-
const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
|
|
308
|
-
|
|
309
|
-
```{note}
|
|
310
|
-
Callbacks **have changed in version 2.0**.
|
|
311
|
-
|
|
312
|
-
In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
|
|
313
|
-
|
|
314
|
-
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.
|
|
315
|
-
|
|
316
|
-
Consult the [migration guide](changes.md) for more information.
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
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.
|
|
320
|
-
|
|
321
|
-
Thus, Koffi distinguishes two callback modes:
|
|
322
|
-
|
|
323
|
-
- [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.
|
|
324
|
-
- [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.
|
|
325
|
-
|
|
326
|
-
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.
|
|
327
|
-
|
|
328
|
-
### Transient callbacks
|
|
329
|
-
|
|
330
|
-
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.
|
|
331
|
-
|
|
332
|
-
```c
|
|
333
|
-
#include <string.h>
|
|
334
|
-
|
|
335
|
-
int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
|
|
336
|
-
{
|
|
337
|
-
char buf[64];
|
|
338
|
-
snprintf(buf, sizeof(buf), "Hello %s!", str);
|
|
339
|
-
return cb(buf, age);
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
```js
|
|
344
|
-
const koffi = require('koffi');
|
|
345
|
-
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
346
|
-
|
|
347
|
-
const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
|
|
348
|
-
|
|
349
|
-
const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
|
|
350
|
-
|
|
351
|
-
let ret = TransferToJS('Niels', 27, (str, age) => {
|
|
352
|
-
console.log(str);
|
|
353
|
-
console.log('Your age is:', age);
|
|
354
|
-
return 42;
|
|
355
|
-
});
|
|
356
|
-
console.log(ret);
|
|
357
|
-
|
|
358
|
-
// This example prints:
|
|
359
|
-
// Hello Niels!
|
|
360
|
-
// Your age is: 27
|
|
361
|
-
// 42
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Registered callbacks
|
|
365
|
-
|
|
366
|
-
*New in Koffi 2.0*
|
|
367
|
-
|
|
368
|
-
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.
|
|
369
|
-
|
|
370
|
-
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.
|
|
371
|
-
|
|
372
|
-
The example below shows how to register and unregister delayed callbacks.
|
|
373
|
-
|
|
374
|
-
```c
|
|
375
|
-
static const char *(*g_cb1)(const char *name);
|
|
376
|
-
static void (*g_cb2)(const char *str);
|
|
377
|
-
|
|
378
|
-
void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
|
|
379
|
-
{
|
|
380
|
-
g_cb1 = cb1;
|
|
381
|
-
g_cb2 = cb2;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
void SayIt(const char *name)
|
|
385
|
-
{
|
|
386
|
-
const char *str = g_cb1(name);
|
|
387
|
-
g_cb2(str);
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
```js
|
|
392
|
-
const koffi = require('koffi');
|
|
393
|
-
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
394
|
-
|
|
395
|
-
const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
|
|
396
|
-
const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
|
|
397
|
-
|
|
398
|
-
const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
|
|
399
|
-
const SayIt = lib.func('void SayIt(const char *name)');
|
|
400
|
-
|
|
401
|
-
let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
|
|
402
|
-
let cb2 = koffi.register(console.log, 'PrintCallback *');
|
|
403
|
-
|
|
404
|
-
RegisterFunctions(cb1, cb2);
|
|
405
|
-
SayIt('Kyoto'); // Prints Hello Kyoto!
|
|
406
|
-
|
|
407
|
-
koffi.unregister(cb1);
|
|
408
|
-
koffi.unregister(cb2);
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Handling of exceptions
|
|
412
|
-
|
|
413
|
-
If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
|
|
414
|
-
|
|
415
|
-
Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
|
|
416
|
-
|
|
417
332
|
## Thread safety
|
|
418
333
|
|
|
419
334
|
Asynchronous functions run on worker threads. You need to deal with thread safety issues if you share data between threads.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -509,23 +509,50 @@ Read the documentation for [disposable types](functions.md#heap-allocated-values
|
|
|
509
509
|
|
|
510
510
|
### Type introspection
|
|
511
511
|
|
|
512
|
-
*New in Koffi 2.0: `koffi.resolve()`*
|
|
513
|
-
|
|
514
|
-
Koffi exposes three functions to explore type information:
|
|
515
|
-
|
|
516
|
-
- `koffi.sizeof(type)` to get the size of a type
|
|
517
|
-
- `koffi.alignof(type)` to get the alignment of a type
|
|
518
|
-
- `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)
|
|
519
|
-
- `koffi.resolve(type)` to get the resolved type object from a type string
|
|
512
|
+
*New in Koffi 2.0: `koffi.resolve()`, new in Koffi 2.2: `koffi.offsetof()`*
|
|
520
513
|
|
|
521
514
|
```{note}
|
|
522
|
-
The value returned by `introspect()` has **changed in version 2.0**.
|
|
515
|
+
The value returned by `introspect()` has **changed in version 2.0 and in version 2.2**.
|
|
523
516
|
|
|
524
517
|
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.
|
|
525
518
|
|
|
519
|
+
Starting in Koffi 2.2, each record member is exposed as an object containing the name, the type and the offset within the record.
|
|
520
|
+
|
|
526
521
|
Consult the [migration guide](changes.md) for more information.
|
|
527
522
|
```
|
|
528
523
|
|
|
524
|
+
Use `koffi.introspect(type)` to get detailed information about a type: name, primitive, size, alignment, members (record types), reference type (array, pointer) and length (array).
|
|
525
|
+
|
|
526
|
+
```js
|
|
527
|
+
const FoobarType = koffi.struct('FoobarType', {
|
|
528
|
+
a: 'int',
|
|
529
|
+
b: 'char *',
|
|
530
|
+
c: 'double'
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
console.log(koffi.introspect(FoobarType));
|
|
534
|
+
|
|
535
|
+
// Expected result on 64-bit machines:
|
|
536
|
+
// {
|
|
537
|
+
// name: 'FoobarType',
|
|
538
|
+
// primitive: 'Record',
|
|
539
|
+
// size: 24,
|
|
540
|
+
// alignment: 8,
|
|
541
|
+
// members: {
|
|
542
|
+
// a: { name: 'a', type: [External: 4b28a60], offset: 0 },
|
|
543
|
+
// b: { name: 'b', type: [External: 4b292e0], offset: 8 },
|
|
544
|
+
// c: { name: 'c', type: [External: 4b29260], offset: 16 }
|
|
545
|
+
// }
|
|
546
|
+
// }
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Koffi also exposes a few more utility functions to get a subset of this information:
|
|
550
|
+
|
|
551
|
+
- `koffi.sizeof(type)` to get the size of a type
|
|
552
|
+
- `koffi.alignof(type)` to get the alignment of a type
|
|
553
|
+
- `koffi.offsetof(type, member_name)` to get the offset of a record member
|
|
554
|
+
- `koffi.resolve(type)` to get the resolved type object from a type string
|
|
555
|
+
|
|
529
556
|
Just like before, you can refer to primitive types by their name or through `koffi.types`:
|
|
530
557
|
|
|
531
558
|
```js
|