koffi 2.2.3 → 2.2.4
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 +13 -3
- package/doc/calls.md +277 -0
- package/doc/functions.md +17 -275
- package/doc/index.rst +3 -1
- package/doc/misc.md +97 -0
- package/doc/pointers.md +135 -0
- package/doc/types.md +3 -193
- package/package.json +2 -2
- package/src/koffi/build/2.2.4/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.4/koffi_win32_x64.tar.gz +0 -0
- package/src/koffi/src/ffi.cc +2 -2
- package/src/koffi/src/ffi.hh +0 -4
- package/doc/memory.md +0 -33
- package/src/koffi/build/2.2.3/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.3/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## History
|
|
4
4
|
|
|
5
|
+
### Koffi 2.2.4
|
|
6
|
+
|
|
7
|
+
**Main fixes:**
|
|
8
|
+
|
|
9
|
+
- Fix memory leak on Windows (in Koffi 2.2.3) when running many async calls
|
|
10
|
+
|
|
11
|
+
**Other changes:**
|
|
12
|
+
|
|
13
|
+
- Reorganize documentation pages
|
|
14
|
+
|
|
5
15
|
### Koffi 2.2.3
|
|
6
16
|
|
|
7
17
|
**Main fixes:**
|
|
@@ -37,7 +47,7 @@
|
|
|
37
47
|
**New features:**
|
|
38
48
|
|
|
39
49
|
- Add [koffi.decode()](callbacks.md#pointer-arguments) for callback pointer arguments
|
|
40
|
-
- Support transparent [output string parameters](
|
|
50
|
+
- Support transparent [output string parameters](calls.md#output-parameters)
|
|
41
51
|
- Add `koffi.offsetof()` utility function
|
|
42
52
|
- Support optional *this* binding in `koffi.register()`
|
|
43
53
|
|
|
@@ -85,7 +95,7 @@
|
|
|
85
95
|
|
|
86
96
|
**Main changes:**
|
|
87
97
|
|
|
88
|
-
- Add [koffi.as()](
|
|
98
|
+
- Add [koffi.as()](calls.md#polymorphic-parameters) to support polymorphic APIs based on `void *` parameters
|
|
89
99
|
- Add [endian-sensitive integer types](types.md#endian-sensitive-types): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
|
|
90
100
|
- Accept typed arrays for `void *` parameters
|
|
91
101
|
- Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
|
|
@@ -109,7 +119,7 @@
|
|
|
109
119
|
|
|
110
120
|
**Major new features:**
|
|
111
121
|
|
|
112
|
-
- Add [disposable types](
|
|
122
|
+
- Add [disposable types](calls.md#heap-allocated-values) for automatic disposal of C values (such as heap-allocated strings)
|
|
113
123
|
- Add support for [registered callbacks](callbacks.md#registered-callbacks), that can be called after the initial FFI call
|
|
114
124
|
- Support named pointer types
|
|
115
125
|
- Support complex type specifications outside of prototype parser
|
package/doc/calls.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Function calls
|
|
2
|
+
|
|
3
|
+
## Call types
|
|
4
|
+
|
|
5
|
+
### Synchronous calls
|
|
6
|
+
|
|
7
|
+
Once a native function has been declared, you can simply call it as you would any other JS function.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
const atoi = lib.func('int atoi(const char *str)');
|
|
11
|
+
|
|
12
|
+
let value = atoi('1257');
|
|
13
|
+
console.log(value);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For [variadic functions](functions.md#variadic-functions), you msut specificy the type and the value for each additional argument.
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const printf = lib.func('printf', 'int', ['str', '...']);
|
|
20
|
+
|
|
21
|
+
// The variadic arguments are: 6 (int), 8.5 (double), 'THE END' (const char *)
|
|
22
|
+
printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END');
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Asynchronous calls
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const koffi = require('koffi');
|
|
31
|
+
const lib = koffi.load('libc.so.6');
|
|
32
|
+
|
|
33
|
+
const atoi = lib.func('int atoi(const char *str)');
|
|
34
|
+
|
|
35
|
+
atoi.async('1257', (err, res) => {
|
|
36
|
+
console.log('Result:', res);
|
|
37
|
+
})
|
|
38
|
+
console.log('Hello World!');
|
|
39
|
+
|
|
40
|
+
// This program will print:
|
|
41
|
+
// Hello World!
|
|
42
|
+
// Result: 1257
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
These calls are executed by worker threads. It is **your responsibility to deal with data sharing issues** in the native code that may be caused by multi-threading.
|
|
46
|
+
|
|
47
|
+
You can easily convert this callback-style async function to a promise-based version with `util.promisify()` from the Node.js standard library.
|
|
48
|
+
|
|
49
|
+
Variadic functions cannot be called asynchronously.
|
|
50
|
+
|
|
51
|
+
## Output parameters
|
|
52
|
+
|
|
53
|
+
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.
|
|
54
|
+
|
|
55
|
+
For simplicity, and because Javascript only has value semantics for primitive types, Koffi can marshal out (or in/out) two types of parameters:
|
|
56
|
+
|
|
57
|
+
- [Structs](types.md#struct-types) (to/from JS objects)
|
|
58
|
+
- [Opaque types](types.md#opaque-types)
|
|
59
|
+
- String buffers
|
|
60
|
+
|
|
61
|
+
In order to change an argument from input-only to output or input/output, use the following functions:
|
|
62
|
+
|
|
63
|
+
- `koffi.out()` on a pointer, e.g. `koffi.out(koffi.pointer(timeval))` (where timeval is a struct type)
|
|
64
|
+
- `koffi.inout()` for dual input/output parameters
|
|
65
|
+
|
|
66
|
+
The same can be done when declaring a function with a C-like prototype string, with the MSDN-like type qualifiers:
|
|
67
|
+
|
|
68
|
+
- `_Out_` for output parameters
|
|
69
|
+
- `_Inout_` for dual input/output parameters
|
|
70
|
+
|
|
71
|
+
### Struct example
|
|
72
|
+
|
|
73
|
+
This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
const koffi = require('koffi');
|
|
77
|
+
const lib = koffi.load('libc.so.6');
|
|
78
|
+
|
|
79
|
+
const timeval = koffi.struct('timeval', {
|
|
80
|
+
tv_sec: 'unsigned int',
|
|
81
|
+
tv_usec: 'unsigned int'
|
|
82
|
+
});
|
|
83
|
+
const timezone = koffi.struct('timezone', {
|
|
84
|
+
tz_minuteswest: 'int',
|
|
85
|
+
tz_dsttime: 'int'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// The _Out_ qualifiers instruct Koffi to marshal out the values
|
|
89
|
+
const gettimeofday = lib.func('int gettimeofday(_Out_ timeval *tv, _Out_ timezone *tz)');
|
|
90
|
+
|
|
91
|
+
let tv = {};
|
|
92
|
+
gettimeofday(tv, null);
|
|
93
|
+
|
|
94
|
+
console.log(tv);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Opaque type example
|
|
98
|
+
|
|
99
|
+
This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const koffi = require('koffi');
|
|
103
|
+
const lib = koffi.load('sqlite3.so');
|
|
104
|
+
|
|
105
|
+
const sqlite3 = koffi.opaque('sqlite3');
|
|
106
|
+
|
|
107
|
+
// Use koffi.out() on a double pointer to copy out (from C to JS) after the call
|
|
108
|
+
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);
|
|
109
|
+
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3)]);
|
|
110
|
+
|
|
111
|
+
const SQLITE_OPEN_READWRITE = 0x2;
|
|
112
|
+
const SQLITE_OPEN_CREATE = 0x4;
|
|
113
|
+
|
|
114
|
+
let out = [null];
|
|
115
|
+
if (sqlite3_open_v2(':memory:', out, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
116
|
+
throw new Error('Failed to open database');
|
|
117
|
+
let db = out[0];
|
|
118
|
+
|
|
119
|
+
sqlite3_close_v2(db);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### String buffer example
|
|
123
|
+
|
|
124
|
+
*New in Koffi 2.2*
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
```c
|
|
129
|
+
void ConcatToBuffer(const char *str1, const char *str2, char *out)
|
|
130
|
+
{
|
|
131
|
+
size_t len = 0;
|
|
132
|
+
|
|
133
|
+
for (size_t i = 0; str1[i]; i++) {
|
|
134
|
+
out[len++] = str1[i];
|
|
135
|
+
}
|
|
136
|
+
for (size_t i = 0; str2[i]; i++) {
|
|
137
|
+
out[len++] = str2[i];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
out[len] = 0;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
const ConcatToBuffer = lib.func('void ConcatToBuffer(const char *str1, const char *str2, _Out_ char *out)');
|
|
146
|
+
|
|
147
|
+
let str1 = 'Hello ';
|
|
148
|
+
let str2 = 'Friends!';
|
|
149
|
+
|
|
150
|
+
// We need to reserve space for the output buffer! Including the NUL terminator
|
|
151
|
+
// because ConcatToBuffer() expects so, but Koffi can convert back to a JS string
|
|
152
|
+
// without it (if we reserve the right size).
|
|
153
|
+
let out = ['\0'.repeat(str1.length + str2.length + 1)];
|
|
154
|
+
|
|
155
|
+
ConcatToBuffer(str1, str2, out);
|
|
156
|
+
|
|
157
|
+
console.log(out[0]);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Polymorphic parameters
|
|
161
|
+
|
|
162
|
+
*New in Koffi 2.1*
|
|
163
|
+
|
|
164
|
+
Many C functions use `void *` parameters in order to pass polymorphic objects and arrays, meaning that the data format changes can change depending on one other argument, or on some kind of struct tag member.
|
|
165
|
+
|
|
166
|
+
Koffi provides two features to deal with this:
|
|
167
|
+
|
|
168
|
+
- Typed JS arrays can be used as values in place everywhere `void *` is expected. See [dynamic arrays](types.md#array-pointers-dynamic-arrays) for more information, for input or output.
|
|
169
|
+
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected.
|
|
170
|
+
|
|
171
|
+
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()`.
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
const koffi = require('koffi');
|
|
175
|
+
const lib = koffi.load('libc.so.6');
|
|
176
|
+
|
|
177
|
+
const FILE = koffi.opaque('FILE');
|
|
178
|
+
|
|
179
|
+
const PngHeader = koffi.pack('PngHeader', {
|
|
180
|
+
signature: koffi.array('uint8_t', 8),
|
|
181
|
+
ihdr: koffi.pack({
|
|
182
|
+
length: 'uint32_be_t',
|
|
183
|
+
chunk: koffi.array('char', 4),
|
|
184
|
+
width: 'uint32_be_t',
|
|
185
|
+
height: 'uint32_be_t',
|
|
186
|
+
depth: 'uint8_t',
|
|
187
|
+
color: 'uint8_t',
|
|
188
|
+
compression: 'uint8_t',
|
|
189
|
+
filter: 'uint8_t',
|
|
190
|
+
interlace: 'uint8_t',
|
|
191
|
+
crc: 'uint32_be_t'
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
|
|
196
|
+
const fclose = lib.func('int fclose(FILE *fp)');
|
|
197
|
+
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
|
|
198
|
+
|
|
199
|
+
let filename = process.argv[2];
|
|
200
|
+
if (filename == null)
|
|
201
|
+
throw new Error('Usage: node png.js <image.png>');
|
|
202
|
+
|
|
203
|
+
let hdr = {};
|
|
204
|
+
{
|
|
205
|
+
|
|
206
|
+
let fp = fopen(filename, 'rb');
|
|
207
|
+
if (!fp)
|
|
208
|
+
throw new Error(`Failed to open '${filename}'`);
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
|
|
212
|
+
if (len < koffi.sizeof(PngHeader))
|
|
213
|
+
throw new Error('Failed to read PNG header');
|
|
214
|
+
} finally {
|
|
215
|
+
fclose(fp);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log('PNG header:', hdr);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Heap-allocated values
|
|
223
|
+
|
|
224
|
+
*New in Koffi 2.0*
|
|
225
|
+
|
|
226
|
+
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.
|
|
227
|
+
|
|
228
|
+
For opaque types, 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.
|
|
229
|
+
|
|
230
|
+
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.
|
|
231
|
+
|
|
232
|
+
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).
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
const AnonHeapStr = koffi.disposable('str'); // Anonymous type (cannot be used in function prototypes)
|
|
236
|
+
const NamedHeapStr = koffi.disposable('HeapStr', 'str'); // Same thing, but named so usable in function prototypes
|
|
237
|
+
const ExplicitFree = koffi.disposable('HeapStr16', 'str16', koffi.free); // You can specify any other JS function
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The following example illustrates the use of a disposable type derived from *str*.
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
const koffi = require('koffi');
|
|
244
|
+
const lib = koffi.load('libc.so.6');
|
|
245
|
+
|
|
246
|
+
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
247
|
+
const HeapStr = koffi.disposable('str');
|
|
248
|
+
const strdup = lib.cdecl('strdup', HeapStr, ['str']);
|
|
249
|
+
|
|
250
|
+
let copy = strdup('Hello!');
|
|
251
|
+
console.log(copy); // Prints Hello!
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
When you declare functions with the [prototype-like syntax](functions.md#c-like-prototypes), you can either use named disposable 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)`.
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const koffi = require('koffi');
|
|
258
|
+
const lib = koffi.load('libc.so.6');
|
|
259
|
+
|
|
260
|
+
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
261
|
+
const strdup = lib.func('str! strdup(const char *str)');
|
|
262
|
+
|
|
263
|
+
let copy = strdup('World!');
|
|
264
|
+
console.log(copy); // Prints World!
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Disposable types can only be created from pointer or string types.
|
|
268
|
+
|
|
269
|
+
```{warning}
|
|
270
|
+
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()`.
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Thread safety
|
|
274
|
+
|
|
275
|
+
Asynchronous functions run on worker threads. You need to deal with thread safety issues if you share data between threads.
|
|
276
|
+
|
|
277
|
+
Callbacks must be called from the main thread, or more precisely from the same thread as the V8 intepreter. Calling a callback from another thread is undefined behavior, and will likely lead to a crash or a big mess. You've been warned!
|
package/doc/functions.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Function definitions
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Definition syntax
|
|
4
4
|
|
|
5
5
|
To declare functions, start by loading the shared library with `koffi.load(filename)`.
|
|
6
6
|
|
|
@@ -36,9 +36,22 @@ 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
|
+
## Variadic functions
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Variadic functions are declared with an ellipsis as the last argument.
|
|
42
|
+
|
|
43
|
+
In order to call a variadic function, you must provide two Javascript arguments for each additional C parameter, the first one is the expected type and the second one is the value.
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const printf = lib.func('printf', 'int', ['str', '...']);
|
|
47
|
+
|
|
48
|
+
// The variadic arguments are: 6 (int), 8.5 (double), 'THE END' (const char *)
|
|
49
|
+
printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
On x86 platforms, only the Cdecl convention can be used for variadic functions.
|
|
53
|
+
|
|
54
|
+
## Calling conventions
|
|
42
55
|
|
|
43
56
|
By default, calling a C function happens synchronously.
|
|
44
57
|
|
|
@@ -63,274 +76,3 @@ const lib = koffi.load('user32.dll');
|
|
|
63
76
|
const MessageBoxA_1 = lib.stdcall('MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
|
|
64
77
|
const MessageBoxA_2 = lib.func('int __stdcall MessageBoxA(void *hwnd, str text, str caption, uint type)');
|
|
65
78
|
```
|
|
66
|
-
|
|
67
|
-
### Asynchronous calls
|
|
68
|
-
|
|
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.
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
const koffi = require('koffi');
|
|
73
|
-
const lib = koffi.load('libc.so.6');
|
|
74
|
-
|
|
75
|
-
const atoi = lib.func('int atoi(const char *str)');
|
|
76
|
-
|
|
77
|
-
atoi.async('1257', (err, res) => {
|
|
78
|
-
console.log('Result:', res);
|
|
79
|
-
})
|
|
80
|
-
console.log('Hello World!');
|
|
81
|
-
|
|
82
|
-
// This program will print:
|
|
83
|
-
// Hello World!
|
|
84
|
-
// Result: 1257
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
These calls are executed by worker threads. It is **your responsibility to deal with data sharing issues** in the native code that may be caused by multi-threading.
|
|
88
|
-
|
|
89
|
-
You can easily convert this callback-style async function to a promise-based version with `util.promisify()` from the Node.js standard library.
|
|
90
|
-
|
|
91
|
-
Variadic functions cannot be called asynchronously.
|
|
92
|
-
|
|
93
|
-
### Variadic functions
|
|
94
|
-
|
|
95
|
-
Variadic functions are declared with an ellipsis as the last argument.
|
|
96
|
-
|
|
97
|
-
In order to call a variadic function, you must provide two Javascript arguments for each additional C parameter, the first one is the expected type and the second one is the value.
|
|
98
|
-
|
|
99
|
-
```js
|
|
100
|
-
const printf = lib.func('printf', 'int', ['str', '...']);
|
|
101
|
-
|
|
102
|
-
// The variadic arguments are: 6 (int), 8.5 (double), 'THE END' (const char *)
|
|
103
|
-
printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END');
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
On x86 platforms, only the Cdecl convention can be used for variadic functions.
|
|
107
|
-
|
|
108
|
-
## Special considerations
|
|
109
|
-
|
|
110
|
-
### Output parameters
|
|
111
|
-
|
|
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.
|
|
113
|
-
|
|
114
|
-
For simplicity, and because Javascript only has value semantics for primitive types, Koffi can marshal out (or in/out) two types of parameters:
|
|
115
|
-
|
|
116
|
-
- [Structs](types.md#struct-types) (to/from JS objects)
|
|
117
|
-
- [Opaque types](types.md#opaque-types)
|
|
118
|
-
- String buffers
|
|
119
|
-
|
|
120
|
-
In order to change an argument from input-only to output or input/output, use the following functions:
|
|
121
|
-
|
|
122
|
-
- `koffi.out()` on a pointer, e.g. `koffi.out(koffi.pointer(timeval))` (where timeval is a struct type)
|
|
123
|
-
- `koffi.inout()` for dual input/output parameters
|
|
124
|
-
|
|
125
|
-
The same can be done when declaring a function with a C-like prototype string, with the MSDN-like type qualifiers:
|
|
126
|
-
|
|
127
|
-
- `_Out_` for output parameters
|
|
128
|
-
- `_Inout_` for dual input/output parameters
|
|
129
|
-
|
|
130
|
-
#### Struct example
|
|
131
|
-
|
|
132
|
-
This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
|
|
133
|
-
|
|
134
|
-
```js
|
|
135
|
-
const koffi = require('koffi');
|
|
136
|
-
const lib = koffi.load('libc.so.6');
|
|
137
|
-
|
|
138
|
-
const timeval = koffi.struct('timeval', {
|
|
139
|
-
tv_sec: 'unsigned int',
|
|
140
|
-
tv_usec: 'unsigned int'
|
|
141
|
-
});
|
|
142
|
-
const timezone = koffi.struct('timezone', {
|
|
143
|
-
tz_minuteswest: 'int',
|
|
144
|
-
tz_dsttime: 'int'
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// The _Out_ qualifiers instruct Koffi to marshal out the values
|
|
148
|
-
const gettimeofday = lib.func('int gettimeofday(_Out_ timeval *tv, _Out_ timezone *tz)');
|
|
149
|
-
|
|
150
|
-
let tv = {};
|
|
151
|
-
gettimeofday(tv, null);
|
|
152
|
-
|
|
153
|
-
console.log(tv);
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
#### Opaque type example
|
|
157
|
-
|
|
158
|
-
This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
|
|
159
|
-
|
|
160
|
-
```js
|
|
161
|
-
const koffi = require('koffi');
|
|
162
|
-
const lib = koffi.load('sqlite3.so');
|
|
163
|
-
|
|
164
|
-
const sqlite3 = koffi.opaque('sqlite3');
|
|
165
|
-
|
|
166
|
-
// Use koffi.out() on a double pointer to copy out (from C to JS) after the call
|
|
167
|
-
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);
|
|
168
|
-
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3)]);
|
|
169
|
-
|
|
170
|
-
const SQLITE_OPEN_READWRITE = 0x2;
|
|
171
|
-
const SQLITE_OPEN_CREATE = 0x4;
|
|
172
|
-
|
|
173
|
-
let out = [null];
|
|
174
|
-
if (sqlite3_open_v2(':memory:', out, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
175
|
-
throw new Error('Failed to open database');
|
|
176
|
-
let db = out[0];
|
|
177
|
-
|
|
178
|
-
sqlite3_close_v2(db);
|
|
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
|
-
|
|
219
|
-
### Polymorphic parameters
|
|
220
|
-
|
|
221
|
-
*New in Koffi 2.1*
|
|
222
|
-
|
|
223
|
-
Many C functions use `void *` parameters in order to pass polymorphic objects and arrays, meaning that the data format changes can change depending on one other argument, or on some kind of struct tag member.
|
|
224
|
-
|
|
225
|
-
Koffi provides two features to deal with this:
|
|
226
|
-
|
|
227
|
-
- Typed JS arrays can be used as values in place everywhere `void *` is expected. See [dynamic arrays](types.md#array-pointers-dynamic-arrays) for more information, for input or output.
|
|
228
|
-
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected.
|
|
229
|
-
|
|
230
|
-
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()`.
|
|
231
|
-
|
|
232
|
-
```js
|
|
233
|
-
const koffi = require('koffi');
|
|
234
|
-
const lib = koffi.load('libc.so.6');
|
|
235
|
-
|
|
236
|
-
const FILE = koffi.opaque('FILE');
|
|
237
|
-
|
|
238
|
-
const PngHeader = koffi.pack('PngHeader', {
|
|
239
|
-
signature: koffi.array('uint8_t', 8),
|
|
240
|
-
ihdr: koffi.pack({
|
|
241
|
-
length: 'uint32_be_t',
|
|
242
|
-
chunk: koffi.array('char', 4),
|
|
243
|
-
width: 'uint32_be_t',
|
|
244
|
-
height: 'uint32_be_t',
|
|
245
|
-
depth: 'uint8_t',
|
|
246
|
-
color: 'uint8_t',
|
|
247
|
-
compression: 'uint8_t',
|
|
248
|
-
filter: 'uint8_t',
|
|
249
|
-
interlace: 'uint8_t',
|
|
250
|
-
crc: 'uint32_be_t'
|
|
251
|
-
})
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
|
|
255
|
-
const fclose = lib.func('int fclose(FILE *fp)');
|
|
256
|
-
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
|
|
257
|
-
|
|
258
|
-
let filename = process.argv[2];
|
|
259
|
-
if (filename == null)
|
|
260
|
-
throw new Error('Usage: node png.js <image.png>');
|
|
261
|
-
|
|
262
|
-
let hdr = {};
|
|
263
|
-
{
|
|
264
|
-
|
|
265
|
-
let fp = fopen(filename, 'rb');
|
|
266
|
-
if (!fp)
|
|
267
|
-
throw new Error(`Failed to open '${filename}'`);
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
|
|
271
|
-
if (len < koffi.sizeof(PngHeader))
|
|
272
|
-
throw new Error('Failed to read PNG header');
|
|
273
|
-
} finally {
|
|
274
|
-
fclose(fp);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
console.log('PNG header:', hdr);
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Heap-allocated values
|
|
282
|
-
|
|
283
|
-
*New in Koffi 2.0*
|
|
284
|
-
|
|
285
|
-
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.
|
|
286
|
-
|
|
287
|
-
For opaque types, 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.
|
|
288
|
-
|
|
289
|
-
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.
|
|
290
|
-
|
|
291
|
-
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).
|
|
292
|
-
|
|
293
|
-
```js
|
|
294
|
-
const AnonHeapStr = koffi.disposable('str'); // Anonymous type (cannot be used in function prototypes)
|
|
295
|
-
const NamedHeapStr = koffi.disposable('HeapStr', 'str'); // Same thing, but named so usable in function prototypes
|
|
296
|
-
const ExplicitFree = koffi.disposable('HeapStr16', 'str16', koffi.free); // You can specify any other JS function
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
The following example illustrates the use of a disposable type derived from *str*.
|
|
300
|
-
|
|
301
|
-
```js
|
|
302
|
-
const koffi = require('koffi');
|
|
303
|
-
const lib = koffi.load('libc.so.6');
|
|
304
|
-
|
|
305
|
-
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
306
|
-
const HeapStr = koffi.disposable('str');
|
|
307
|
-
const strdup = lib.cdecl('strdup', HeapStr, ['str']);
|
|
308
|
-
|
|
309
|
-
let copy = strdup('Hello!');
|
|
310
|
-
console.log(copy); // Prints Hello!
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
When you declare functions with the [prototype-like syntax](#c-like-prototypes), you can either use named disposable 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)`.
|
|
314
|
-
|
|
315
|
-
```js
|
|
316
|
-
const koffi = require('koffi');
|
|
317
|
-
const lib = koffi.load('libc.so.6');
|
|
318
|
-
|
|
319
|
-
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
320
|
-
const strdup = lib.func('str! strdup(const char *str)');
|
|
321
|
-
|
|
322
|
-
let copy = strdup('World!');
|
|
323
|
-
console.log(copy); // Prints World!
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
Disposable types can only be created from pointer or string types.
|
|
327
|
-
|
|
328
|
-
```{warning}
|
|
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()`.
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## Thread safety
|
|
333
|
-
|
|
334
|
-
Asynchronous functions run on worker threads. You need to deal with thread safety issues if you share data between threads.
|
|
335
|
-
|
|
336
|
-
Callbacks must be called from the main thread, or more precisely from the same thread as the V8 intepreter. Calling a callback from another thread is undefined behavior, and will likely lead to a crash or a big mess. You've been warned!
|
package/doc/index.rst
CHANGED
package/doc/misc.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Miscellaneous
|
|
2
|
+
|
|
3
|
+
## Types
|
|
4
|
+
|
|
5
|
+
### Introspection
|
|
6
|
+
|
|
7
|
+
*New in Koffi 2.0: `koffi.resolve()`, new in Koffi 2.2: `koffi.offsetof()`*
|
|
8
|
+
|
|
9
|
+
```{note}
|
|
10
|
+
The value returned by `introspect()` has **changed in version 2.0 and in version 2.2**.
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
Starting in Koffi 2.2, each record member is exposed as an object containing the name, the type and the offset within the record.
|
|
15
|
+
|
|
16
|
+
Consult the [migration guide](changes.md) for more information.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
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).
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const FoobarType = koffi.struct('FoobarType', {
|
|
23
|
+
a: 'int',
|
|
24
|
+
b: 'char *',
|
|
25
|
+
c: 'double'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log(koffi.introspect(FoobarType));
|
|
29
|
+
|
|
30
|
+
// Expected result on 64-bit machines:
|
|
31
|
+
// {
|
|
32
|
+
// name: 'FoobarType',
|
|
33
|
+
// primitive: 'Record',
|
|
34
|
+
// size: 24,
|
|
35
|
+
// alignment: 8,
|
|
36
|
+
// members: {
|
|
37
|
+
// a: { name: 'a', type: [External: 4b28a60], offset: 0 },
|
|
38
|
+
// b: { name: 'b', type: [External: 4b292e0], offset: 8 },
|
|
39
|
+
// c: { name: 'c', type: [External: 4b29260], offset: 16 }
|
|
40
|
+
// }
|
|
41
|
+
// }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Koffi also exposes a few more utility functions to get a subset of this information:
|
|
45
|
+
|
|
46
|
+
- `koffi.sizeof(type)` to get the size of a type
|
|
47
|
+
- `koffi.alignof(type)` to get the alignment of a type
|
|
48
|
+
- `koffi.offsetof(type, member_name)` to get the offset of a record member
|
|
49
|
+
- `koffi.resolve(type)` to get the resolved type object from a type string
|
|
50
|
+
|
|
51
|
+
Just like before, you can refer to primitive types by their name or through `koffi.types`:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// These two lines do the same:
|
|
55
|
+
console.log(koffi.sizeof('long'));
|
|
56
|
+
console.log(koffi.sizeof(koffi.types.long));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Aliases
|
|
60
|
+
|
|
61
|
+
*New in Koffi 2.0*
|
|
62
|
+
|
|
63
|
+
You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
|
|
64
|
+
|
|
65
|
+
## Settings
|
|
66
|
+
|
|
67
|
+
### Memory usage
|
|
68
|
+
|
|
69
|
+
For synchronous/normal calls, Koffi uses two preallocated memory blocks:
|
|
70
|
+
|
|
71
|
+
- One to construct the C stack and assign registers, subsequently used by the platform-specific assembly code (1 MiB by default)
|
|
72
|
+
- One to allocate strings and big objects/structs (2 MiB by default)
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
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.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
let config = koffi.config();
|
|
80
|
+
console.log(config);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The same is true for asynchronous calls. When an asynchronous call is made, Koffi will allocate new blocks unless there is an unused (resident) set of blocks still available. Once the asynchronous call is finished, these blocks are freed if there are more than `resident_async_pools` sets of blocks left around.
|
|
84
|
+
|
|
85
|
+
There cannot be more than `max_async_calls` running at the same time.
|
|
86
|
+
|
|
87
|
+
### Default settings
|
|
88
|
+
|
|
89
|
+
Setting | Default | Description
|
|
90
|
+
-------------------- | ------- | -----------------------------------------------
|
|
91
|
+
sync_stack_size | 1 MiB | Stack size for synchronous calls
|
|
92
|
+
sync_heap_size | 2 MiB | Heap size for synchronous calls
|
|
93
|
+
async_stack_size | 256 kiB | Stack size for asynchronous calls
|
|
94
|
+
async_heap_size | 512 kiB | Heap size for asynchronous calls
|
|
95
|
+
resident_async_pools | 2 | Number of resident pools for asynchronous calls
|
|
96
|
+
max_async_calls | 64 | Maximum number of ongoing asynchronous calls
|
|
97
|
+
max_type_size | 64 MiB | Maximum size of Koffi types (for arrays and structs)
|
package/doc/pointers.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Data pointers
|
|
2
|
+
|
|
3
|
+
## How pointers are used
|
|
4
|
+
|
|
5
|
+
In C, pointer arguments are used for differenty purposes. It is important to distinguish these use cases because Koffi provides different ways to deal with each of them:
|
|
6
|
+
|
|
7
|
+
- **Struct pointers**: Use of struct pointers by C libraries fall in two cases: avoid (potentially) expensive copies, and to let the function change struct contents (output or input/output arguments).
|
|
8
|
+
- **Opaque pointers**: the library does not expose the contents of the structs, and only provides you with a pointer to it (e.g. `FILE *`). Only the functions provided by the library can do something with this pointer, in Koffi we call this an opaque type. This is usually done for ABI-stability reason, and to prevent library users from messing directly with library internals.
|
|
9
|
+
- **Pointers to primitive types**: This is more rare, and generally used for output or input/output arguments. The Win32 API has a lot of these.
|
|
10
|
+
- **Arrays**: in C, you dynamically-sized arrays are usually passed to functions with pointers, either NULL-terminated (or any other sentinel value) or with an additional length argument.
|
|
11
|
+
|
|
12
|
+
## Pointer types
|
|
13
|
+
|
|
14
|
+
### Struct pointers
|
|
15
|
+
|
|
16
|
+
The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const koffi = require('koffi');
|
|
20
|
+
const lib = koffi.load('kernel32.dll');
|
|
21
|
+
|
|
22
|
+
// Type declarations
|
|
23
|
+
const POINT = koffi.struct('POINT', {
|
|
24
|
+
x: 'long',
|
|
25
|
+
y: 'long'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Functions declarations
|
|
29
|
+
const GetCursorPos = lib.func('int __stdcall GetCursorPos(_Out_ POINT *pos)');
|
|
30
|
+
|
|
31
|
+
// Get and show cursor position
|
|
32
|
+
let pos = {};
|
|
33
|
+
if (!GetCursorPos(pos))
|
|
34
|
+
throw new Error('Failed to get cursor position');
|
|
35
|
+
console.log(pos);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Opaque pointers
|
|
39
|
+
|
|
40
|
+
*New in Koffi 2.0*
|
|
41
|
+
|
|
42
|
+
Some C libraries use handles, which behave as pointers to opaque structs. An example of this is the HANDLE type in the Win32 API. If you want to reproduce this behavior, you can define a **named pointer type** to an opaque type, like so:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
|
|
46
|
+
|
|
47
|
+
// And now you get to use it this way:
|
|
48
|
+
const GetHandleInformation = lib.func('bool __stdcall GetHandleInformation(HANDLE h, _Out_ uint32_t *flags)');
|
|
49
|
+
const CloseHandle = lib.func('bool __stdcall CloseHandle(HANDLE h)');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Pointer to primitive types
|
|
53
|
+
|
|
54
|
+
In javascript, it is not possible to pass a primitive value by reference to another function. This means that you cannot call a function and expect it to modify the value of one of its number or string parameter.
|
|
55
|
+
|
|
56
|
+
However, arrays and objects (among others) are reference type values. Assigning an array or an object from one variable to another does not invole any copy. Instead, as the following example illustrates, the new variable references the same array as the first:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
let list1 = [1, 2];
|
|
60
|
+
let list2 = list1;
|
|
61
|
+
|
|
62
|
+
list2[1] = 42;
|
|
63
|
+
|
|
64
|
+
console.log(list1); // Prints [1, 42]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
All of this means that C functions that are expected to modify their primitive output values (such as an `int *` parameter) cannot be used directly. However, thanks to Koffi's transparent array support, you can use Javascript arrays to approximate reference semantics with single-element arrays.
|
|
68
|
+
|
|
69
|
+
Below, you can find an example of an addition function where the result is stored in an `int *` input/output parameter and how to use this function from Koffi.
|
|
70
|
+
|
|
71
|
+
```c
|
|
72
|
+
void AddInt(int *dest, int add)
|
|
73
|
+
{
|
|
74
|
+
*dest += add;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can simply pass a single-element array as the first argument:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const AddInt = lib.func('void AddInt(_Inout_ int *dest, int add)');
|
|
82
|
+
|
|
83
|
+
let sum = [36];
|
|
84
|
+
AddInt(sum, 6);
|
|
85
|
+
|
|
86
|
+
console.log(sum[0]); // Prints 42
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Array pointers (dynamic arrays)
|
|
90
|
+
|
|
91
|
+
In C, dynamically-sized arrays are usually passed around as pointers. The length is either passed as an additional argument, or inferred from the array content itself, for example with a terminating sentinel value (such as a NULL pointers in the case of an array of strings).
|
|
92
|
+
|
|
93
|
+
Koffi can translate JS arrays and TypedArrays to pointer arguments. However, because C does not have a proper notion of dynamically-sized arrays (fat pointers), you need to provide the length or the sentinel value yourself depending on the API.
|
|
94
|
+
|
|
95
|
+
Here is a simple example of a C function taking a NULL-terminated list of strings as input, to calculate the total length of all strings.
|
|
96
|
+
|
|
97
|
+
```c
|
|
98
|
+
// Build with: clang -fPIC -o length.so -shared length.c -Wall -O2
|
|
99
|
+
|
|
100
|
+
#include <stdlib.h>
|
|
101
|
+
#include <stdint.h>
|
|
102
|
+
#include <string.h>
|
|
103
|
+
|
|
104
|
+
int64_t ComputeTotalLength(const char **strings)
|
|
105
|
+
{
|
|
106
|
+
int64_t total = 0;
|
|
107
|
+
|
|
108
|
+
for (const char **ptr = strings; *ptr; ptr++) {
|
|
109
|
+
const char *str = *ptr;
|
|
110
|
+
total += strlen(str);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return total;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const koffi = require('koffi');
|
|
119
|
+
const lib = koffi.load('./length.so');
|
|
120
|
+
|
|
121
|
+
const ComputeTotalLength = lib.func('int64_t ComputeTotalLength(const char **strings)');
|
|
122
|
+
|
|
123
|
+
let strings = ['Get', 'Total', 'Length', null];
|
|
124
|
+
let total = ComputeTotalLength(strings);
|
|
125
|
+
|
|
126
|
+
console.log(total); // Prints 14
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
By default, just like for objects, array arguments are copied from JS to C but not vice-versa. You can however change the direction as documented in the section on [output parameters](calls.md#output-parameters).
|
|
130
|
+
|
|
131
|
+
## Disposable types
|
|
132
|
+
|
|
133
|
+
Disposable types allow you to register a function that will automatically called after each C to JS conversion performed by Koffi. This can be used to avoid leaking heap-allocated strings, for example.
|
|
134
|
+
|
|
135
|
+
Read the documentation for [disposable types](calls.md#heap-allocated-values) on the page about function calls.
|
package/doc/types.md
CHANGED
|
@@ -142,14 +142,14 @@ const Function2 = lib.func('Function', A, [A]);
|
|
|
142
142
|
|
|
143
143
|
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 opaque pointer with other functions such as `fread()` or `ftell()`.
|
|
144
144
|
|
|
145
|
-
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](
|
|
145
|
+
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](calls.md#output-parameters) (with a double pointer).
|
|
146
146
|
|
|
147
147
|
```{note}
|
|
148
148
|
Opaque types **have changed in version 2.0, and again in version 2.1**.
|
|
149
149
|
|
|
150
150
|
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.
|
|
151
151
|
|
|
152
|
-
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](
|
|
152
|
+
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](calls.md#output-parameters).
|
|
153
153
|
|
|
154
154
|
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.
|
|
155
155
|
|
|
@@ -330,90 +330,6 @@ try {
|
|
|
330
330
|
}
|
|
331
331
|
```
|
|
332
332
|
|
|
333
|
-
## Pointer types
|
|
334
|
-
|
|
335
|
-
In C, pointer arguments are used for differenty purposes. It is important to distinguish these use cases because Koffi provides different ways to deal with each of them:
|
|
336
|
-
|
|
337
|
-
- **Struct pointers**: Use of struct pointers by C libraries fall in two cases: avoid (potentially) expensive copies, and to let the function change struct contents (output or input/output arguments).
|
|
338
|
-
- **Opaque pointers**: the library does not expose the contents of the structs, and only provides you with a pointer to it (e.g. `FILE *`). Only the functions provided by the library can do something with this pointer, in Koffi we call this an opaque type. This is usually done for ABI-stability reason, and to prevent library users from messing directly with library internals.
|
|
339
|
-
- **Pointers to primitive types**: This is more rare, and generally used for output or input/output arguments. The Win32 API has a lot of these.
|
|
340
|
-
- **Arrays**: in C, you dynamically-sized arrays are usually passed to functions with pointers, either NULL-terminated (or any other sentinel value) or with an additional length argument.
|
|
341
|
-
|
|
342
|
-
### Struct pointers
|
|
343
|
-
|
|
344
|
-
The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
|
|
345
|
-
|
|
346
|
-
```js
|
|
347
|
-
const koffi = require('koffi');
|
|
348
|
-
const lib = koffi.load('kernel32.dll');
|
|
349
|
-
|
|
350
|
-
// Type declarations
|
|
351
|
-
const POINT = koffi.struct('POINT', {
|
|
352
|
-
x: 'long',
|
|
353
|
-
y: 'long'
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
// Functions declarations
|
|
357
|
-
const GetCursorPos = lib.func('int __stdcall GetCursorPos(_Out_ POINT *pos)');
|
|
358
|
-
|
|
359
|
-
// Get and show cursor position
|
|
360
|
-
let pos = {};
|
|
361
|
-
if (!GetCursorPos(pos))
|
|
362
|
-
throw new Error('Failed to get cursor position');
|
|
363
|
-
console.log(pos);
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Named pointer types
|
|
367
|
-
|
|
368
|
-
*New in Koffi 2.0*
|
|
369
|
-
|
|
370
|
-
Some C libraries use handles, which behave as pointers to opaque structs. An example of this is the HANDLE type in the Win32 API. If you want to reproduce this behavior, you can define a **named pointer type** to an opaque type, like so:
|
|
371
|
-
|
|
372
|
-
```js
|
|
373
|
-
const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
|
|
374
|
-
|
|
375
|
-
// And now you get to use it this way:
|
|
376
|
-
const GetHandleInformation = lib.func('bool __stdcall GetHandleInformation(HANDLE h, _Out_ uint32_t *flags)');
|
|
377
|
-
const CloseHandle = lib.func('bool __stdcall CloseHandle(HANDLE h)');
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Pointers to primitive types
|
|
381
|
-
|
|
382
|
-
In javascript, it is not possible to pass a primitive value by reference to another function. This means that you cannot call a function and expect it to modify the value of one of its number or string parameter.
|
|
383
|
-
|
|
384
|
-
However, arrays and objects (among others) are reference type values. Assigning an array or an object from one variable to another does not invole any copy. Instead, as the following example illustrates, the new variable references the same array as the first:
|
|
385
|
-
|
|
386
|
-
```js
|
|
387
|
-
let list1 = [1, 2];
|
|
388
|
-
let list2 = list1;
|
|
389
|
-
|
|
390
|
-
list2[1] = 42;
|
|
391
|
-
|
|
392
|
-
console.log(list1); // Prints [1, 42]
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
All of this means that C functions that are expected to modify their primitive output values (such as an `int *` parameter) cannot be used directly. However, thanks to Koffi's transparent array support, you can use Javascript arrays to approximate reference semantics with single-element arrays.
|
|
396
|
-
|
|
397
|
-
Below, you can find an example of an addition function where the result is stored in an `int *` input/output parameter and how to use this function from Koffi.
|
|
398
|
-
|
|
399
|
-
```c
|
|
400
|
-
void AddInt(int *dest, int add)
|
|
401
|
-
{
|
|
402
|
-
*dest += add;
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
You can simply pass a single-element array as the first argument:
|
|
407
|
-
|
|
408
|
-
```js
|
|
409
|
-
const AddInt = lib.func('void AddInt(_Inout_ int *dest, int add)');
|
|
410
|
-
|
|
411
|
-
let sum = [36];
|
|
412
|
-
AddInt(sum, 6);
|
|
413
|
-
|
|
414
|
-
console.log(sum[0]); // Prints 42
|
|
415
|
-
```
|
|
416
|
-
|
|
417
333
|
## Array types
|
|
418
334
|
|
|
419
335
|
### Fixed-size C arrays
|
|
@@ -459,110 +375,4 @@ The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS s
|
|
|
459
375
|
|
|
460
376
|
### Array pointers (dynamic arrays)
|
|
461
377
|
|
|
462
|
-
In C, dynamically-sized arrays are usually passed around as pointers.
|
|
463
|
-
|
|
464
|
-
Koffi can translate JS arrays and TypedArrays to pointer arguments. However, because C does not have a proper notion of dynamically-sized arrays (fat pointers), you need to provide the length or the sentinel value yourself depending on the API.
|
|
465
|
-
|
|
466
|
-
Here is a simple example of a C function taking a NULL-terminated list of strings as input, to calculate the total length of all strings.
|
|
467
|
-
|
|
468
|
-
```c
|
|
469
|
-
// Build with: clang -fPIC -o length.so -shared length.c -Wall -O2
|
|
470
|
-
|
|
471
|
-
#include <stdlib.h>
|
|
472
|
-
#include <stdint.h>
|
|
473
|
-
#include <string.h>
|
|
474
|
-
|
|
475
|
-
int64_t ComputeTotalLength(const char **strings)
|
|
476
|
-
{
|
|
477
|
-
int64_t total = 0;
|
|
478
|
-
|
|
479
|
-
for (const char **ptr = strings; *ptr; ptr++) {
|
|
480
|
-
const char *str = *ptr;
|
|
481
|
-
total += strlen(str);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return total;
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
```js
|
|
489
|
-
const koffi = require('koffi');
|
|
490
|
-
const lib = koffi.load('./length.so');
|
|
491
|
-
|
|
492
|
-
const ComputeTotalLength = lib.func('int64_t ComputeTotalLength(const char **strings)');
|
|
493
|
-
|
|
494
|
-
let strings = ['Get', 'Total', 'Length', null];
|
|
495
|
-
let total = ComputeTotalLength(strings);
|
|
496
|
-
|
|
497
|
-
console.log(total); // Prints 14
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
By default, just like for objects, array arguments are copied from JS to C but not vice-versa. You can however change the direction as documented in the section on [output parameters](functions.md#output-parameters).
|
|
501
|
-
|
|
502
|
-
## Disposable types
|
|
503
|
-
|
|
504
|
-
Disposable types allow you to register a function that will automatically called after each C to JS conversion performed by Koffi. This can be used to avoid leaking heap-allocated strings, for example.
|
|
505
|
-
|
|
506
|
-
Read the documentation for [disposable types](functions.md#heap-allocated-values) on the page about function calls.
|
|
507
|
-
|
|
508
|
-
## Utility functions
|
|
509
|
-
|
|
510
|
-
### Type introspection
|
|
511
|
-
|
|
512
|
-
*New in Koffi 2.0: `koffi.resolve()`, new in Koffi 2.2: `koffi.offsetof()`*
|
|
513
|
-
|
|
514
|
-
```{note}
|
|
515
|
-
The value returned by `introspect()` has **changed in version 2.0 and in version 2.2**.
|
|
516
|
-
|
|
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.
|
|
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
|
-
|
|
521
|
-
Consult the [migration guide](changes.md) for more information.
|
|
522
|
-
```
|
|
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
|
-
|
|
556
|
-
Just like before, you can refer to primitive types by their name or through `koffi.types`:
|
|
557
|
-
|
|
558
|
-
```js
|
|
559
|
-
// These two lines do the same:
|
|
560
|
-
console.log(koffi.sizeof('long'));
|
|
561
|
-
console.log(koffi.sizeof(koffi.types.long));
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
### Type aliases
|
|
565
|
-
|
|
566
|
-
*New in Koffi 2.0*
|
|
567
|
-
|
|
568
|
-
You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
|
|
378
|
+
In C, dynamically-sized arrays are usually passed around as pointers. Read more about [array pointers](pointers.md#array-pointers-dynamic-arrays) in the relevant section.
|
package/package.json
CHANGED
|
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/src/koffi/src/ffi.cc
CHANGED
|
@@ -1723,8 +1723,8 @@ void FunctionInfo::Unref() const
|
|
|
1723
1723
|
InstanceMemory::~InstanceMemory()
|
|
1724
1724
|
{
|
|
1725
1725
|
#ifdef _WIN32
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
1726
|
+
if (stack.ptr) {
|
|
1727
|
+
VirtualFree(stack.ptr, 0, MEM_RELEASE);
|
|
1728
1728
|
}
|
|
1729
1729
|
if (heap.ptr) {
|
|
1730
1730
|
VirtualFree(heap.ptr, 0, MEM_RELEASE);
|
package/src/koffi/src/ffi.hh
CHANGED
package/doc/memory.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Memory usage
|
|
2
|
-
|
|
3
|
-
## How it works
|
|
4
|
-
|
|
5
|
-
For synchronous/normal calls, Koffi uses two preallocated memory blocks:
|
|
6
|
-
|
|
7
|
-
- One to construct the C stack and assign registers, subsequently used by the platform-specific assembly code (1 MiB by default)
|
|
8
|
-
- One to allocate strings and big objects/structs (2 MiB by default)
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
```js
|
|
15
|
-
let config = koffi.config();
|
|
16
|
-
console.log(config);
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The same is true for asynchronous calls. When an asynchronous call is made, Koffi will allocate new blocks unless there is an unused (resident) set of blocks still available. Once the asynchronous call is finished, these blocks are freed if there are more than `resident_async_pools` sets of blocks left around.
|
|
20
|
-
|
|
21
|
-
There cannot be more than `max_async_calls` running at the same time.
|
|
22
|
-
|
|
23
|
-
## Default settings
|
|
24
|
-
|
|
25
|
-
Setting | Default | Description
|
|
26
|
-
-------------------- | ------- | -----------------------------------------------
|
|
27
|
-
sync_stack_size | 1 MiB | Stack size for synchronous calls
|
|
28
|
-
sync_heap_size | 2 MiB | Heap size for synchronous calls
|
|
29
|
-
async_stack_size | 256 kiB | Stack size for asynchronous calls
|
|
30
|
-
async_heap_size | 512 kiB | Heap size for asynchronous calls
|
|
31
|
-
resident_async_pools | 2 | Number of resident pools for asynchronous calls
|
|
32
|
-
max_async_calls | 64 | Maximum number of ongoing asynchronous calls
|
|
33
|
-
max_type_size | 64 MiB | Maximum size of Koffi types (for arrays and structs)
|
|
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
|