koffi 2.0.1 → 2.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +2 -9
- package/ChangeLog.md +17 -0
- package/benchmark/atoi_koffi.js +12 -8
- package/benchmark/atoi_napi.js +12 -8
- package/benchmark/atoi_node_ffi.js +11 -10
- package/benchmark/raylib_cc.cc +12 -9
- package/benchmark/raylib_koffi.js +15 -13
- package/benchmark/raylib_node_ffi.js +15 -13
- package/benchmark/raylib_node_raylib.js +14 -11
- package/build/qemu/2.1.0-beta.3/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_x64.tar.gz +0 -0
- package/doc/Makefile +1 -1
- package/doc/changes.md +12 -8
- package/doc/conf.py +5 -0
- package/doc/dist/doctrees/changes.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/types.doctree +0 -0
- package/doc/dist/html/.buildinfo +1 -1
- package/doc/dist/html/_sources/changes.md.txt +12 -8
- package/doc/dist/html/_sources/functions.md.txt +71 -5
- package/doc/dist/html/_sources/types.md.txt +147 -159
- package/doc/dist/html/benchmarks.html +2 -3
- package/doc/dist/html/changes.html +64 -35
- package/doc/dist/html/contribute.html +2 -3
- package/doc/dist/html/functions.html +73 -12
- package/doc/dist/html/genindex.html +2 -3
- package/doc/dist/html/index.html +6 -7
- package/doc/dist/html/memory.html +2 -3
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +3 -4
- package/doc/dist/html/search.html +2 -3
- package/doc/dist/html/searchindex.js +1 -1
- package/doc/dist/html/start.html +2 -3
- package/doc/dist/html/types.html +238 -237
- package/doc/functions.md +71 -5
- package/doc/make.bat +1 -1
- package/doc/templates/badges.html +1 -2
- package/doc/types.md +149 -159
- package/package.json +3 -2
- package/qemu/qemu.js +1 -1
- package/src/abi_arm32.cc +208 -102
- package/src/abi_arm64.cc +239 -55
- package/src/abi_riscv64.cc +128 -40
- package/src/abi_x64_sysv.cc +135 -41
- package/src/abi_x64_win.cc +134 -40
- package/src/abi_x86.cc +182 -67
- package/src/call.cc +241 -26
- package/src/call.hh +15 -3
- package/src/ffi.cc +120 -31
- package/src/ffi.hh +19 -0
- package/src/index.js +4 -2
- package/src/parser.cc +3 -5
- package/src/util.cc +44 -1
- package/src/util.hh +4 -0
- package/test/async.js +1 -2
- package/test/callbacks.js +2 -3
- package/test/misc.c +64 -1
- package/test/raylib.js +1 -1
- package/test/sqlite.js +3 -3
- package/test/sync.js +108 -3
- package/build/qemu/2.0.1/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_x64.tar.gz +0 -0
package/doc/functions.md
CHANGED
|
@@ -105,7 +105,7 @@ printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END
|
|
|
105
105
|
|
|
106
106
|
On x86 platforms, only the Cdecl convention can be used for variadic functions.
|
|
107
107
|
|
|
108
|
-
##
|
|
108
|
+
## Special considerations
|
|
109
109
|
|
|
110
110
|
### Output parameters
|
|
111
111
|
|
|
@@ -114,7 +114,7 @@ By default, Koffi will only forward arguments from Javascript to C. However, man
|
|
|
114
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
115
|
|
|
116
116
|
- [Structs](types.md#struct-types) (to/from JS objects)
|
|
117
|
-
- [Opaque
|
|
117
|
+
- [Opaque types](types.md#opaque-types)
|
|
118
118
|
|
|
119
119
|
In order to change an argument from input-only to output or input/output, use the following functions:
|
|
120
120
|
|
|
@@ -152,7 +152,7 @@ gettimeofday(tv, null);
|
|
|
152
152
|
console.log(tv);
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
#### Opaque
|
|
155
|
+
#### Opaque type example
|
|
156
156
|
|
|
157
157
|
This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
|
|
158
158
|
|
|
@@ -160,7 +160,7 @@ This example opens an in-memory SQLite database, and uses the node-ffi-style fun
|
|
|
160
160
|
const koffi = require('koffi');
|
|
161
161
|
const lib = koffi.load('sqlite3.so');
|
|
162
162
|
|
|
163
|
-
const sqlite3 = koffi.
|
|
163
|
+
const sqlite3 = koffi.opaque('sqlite3');
|
|
164
164
|
|
|
165
165
|
// Use koffi.out() on a double pointer to copy out (from C to JS) after the call
|
|
166
166
|
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);
|
|
@@ -177,11 +177,75 @@ let db = out[0];
|
|
|
177
177
|
sqlite3_close_v2(db);
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
+
### Polymorphic parameters
|
|
181
|
+
|
|
182
|
+
*New in Koffi 2.1*
|
|
183
|
+
|
|
184
|
+
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.
|
|
185
|
+
|
|
186
|
+
Koffi provides two features to deal with this:
|
|
187
|
+
|
|
188
|
+
- 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.
|
|
189
|
+
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected.
|
|
190
|
+
|
|
191
|
+
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()`.
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
const koffi = require('koffi');
|
|
195
|
+
const lib = koffi.load('libc.so.6');
|
|
196
|
+
|
|
197
|
+
const FILE = koffi.opaque('FILE');
|
|
198
|
+
|
|
199
|
+
const PngHeader = koffi.pack('PngHeader', {
|
|
200
|
+
signature: koffi.array('uint8_t', 8),
|
|
201
|
+
ihdr: koffi.pack({
|
|
202
|
+
length: 'uint32_be_t',
|
|
203
|
+
chunk: koffi.array('char', 4),
|
|
204
|
+
width: 'uint32_be_t',
|
|
205
|
+
height: 'uint32_be_t',
|
|
206
|
+
depth: 'uint8_t',
|
|
207
|
+
color: 'uint8_t',
|
|
208
|
+
compression: 'uint8_t',
|
|
209
|
+
filter: 'uint8_t',
|
|
210
|
+
interlace: 'uint8_t',
|
|
211
|
+
crc: 'uint32_be_t'
|
|
212
|
+
})
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
|
|
216
|
+
const fclose = lib.func('int fclose(FILE *fp)');
|
|
217
|
+
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
|
|
218
|
+
|
|
219
|
+
let filename = process.argv[2];
|
|
220
|
+
if (filename == null)
|
|
221
|
+
throw new Error('Usage: node png.js <image.png>');
|
|
222
|
+
|
|
223
|
+
let hdr = {};
|
|
224
|
+
{
|
|
225
|
+
|
|
226
|
+
let fp = fopen(filename, 'rb');
|
|
227
|
+
if (!fp)
|
|
228
|
+
throw new Error(`Failed to open '${filename}'`);
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
|
|
232
|
+
if (len < koffi.sizeof(PngHeader))
|
|
233
|
+
throw new Error('Failed to read PNG header');
|
|
234
|
+
} finally {
|
|
235
|
+
fclose(fp);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log('PNG header:', hdr);
|
|
240
|
+
```
|
|
241
|
+
|
|
180
242
|
### Heap-allocated values
|
|
181
243
|
|
|
244
|
+
*New in Koffi 2.0*
|
|
245
|
+
|
|
182
246
|
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.
|
|
183
247
|
|
|
184
|
-
For opaque
|
|
248
|
+
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.
|
|
185
249
|
|
|
186
250
|
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.
|
|
187
251
|
|
|
@@ -299,6 +363,8 @@ console.log(ret);
|
|
|
299
363
|
|
|
300
364
|
### Registered callbacks
|
|
301
365
|
|
|
366
|
+
*New in Koffi 2.0*
|
|
367
|
+
|
|
302
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.
|
|
303
369
|
|
|
304
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.
|
package/doc/make.bat
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<div style="text-align: center; margin-top: 2em;">
|
|
2
|
-
<a href="https://www.npmjs.com/package/koffi"><img src="https://img.shields.io/badge/NPM-{{
|
|
2
|
+
<a href="https://www.npmjs.com/package/koffi"><img src="https://img.shields.io/badge/NPM-{{ stable }}-brightgreen" alt="NPM"/></a>
|
|
3
3
|
<a href="https://github.com/Koromix/luigi/tree/master/koffi"><img src="https://img.shields.io/badge/GitHub-Koffi-ff6600" alt="GitHub"/></a>
|
|
4
4
|
</div>
|
|
5
|
-
|
package/doc/types.md
CHANGED
|
@@ -2,45 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## Primitive types
|
|
4
4
|
|
|
5
|
+
### Standard types
|
|
6
|
+
|
|
5
7
|
While the C standard allows for variation in the size of most integer types, Koffi enforces the same definition for most primitive types, listed below:
|
|
6
8
|
|
|
7
|
-
JS type
|
|
8
|
-
|
|
9
|
-
Undefined
|
|
10
|
-
Number (integer)
|
|
11
|
-
Number (integer)
|
|
12
|
-
Number (integer)
|
|
13
|
-
Number (integer)
|
|
14
|
-
Number (integer)
|
|
15
|
-
Number (integer)
|
|
16
|
-
Number (integer)
|
|
17
|
-
Number (integer)
|
|
18
|
-
Number (integer)
|
|
19
|
-
Number (integer)
|
|
20
|
-
Number (integer)
|
|
21
|
-
Number (integer)
|
|
22
|
-
Number (integer)
|
|
23
|
-
Number (integer)
|
|
24
|
-
Number (integer)
|
|
25
|
-
Number (integer)
|
|
26
|
-
Number (integer)
|
|
27
|
-
Number (
|
|
28
|
-
Number (
|
|
29
|
-
Number (
|
|
30
|
-
Number (
|
|
31
|
-
Number (integer) | unsigned int | 4 | Unsigned |
|
|
32
|
-
Number (integer) | int64 | 8 | Signed |
|
|
33
|
-
Number (integer) | int64_t | 8 | Signed |
|
|
34
|
-
Number (integer) | uint64 | 8 | Unsigned |
|
|
35
|
-
Number (integer) | uint64_t | 8 | Unsigned |
|
|
36
|
-
Number (integer) | longlong | 8 | Signed |
|
|
37
|
-
Number (integer) | long long | 8 | Signed |
|
|
38
|
-
Number (integer) | ulonglong | 8 | Unsigned |
|
|
39
|
-
Number (integer) | unsigned long long | 8 | Unsigned |
|
|
40
|
-
Number (float) | float32 | 4 | |
|
|
41
|
-
Number (float) | float64 | 8 | |
|
|
42
|
-
Number (float) | float | 4 | |
|
|
43
|
-
Number (float) | double | 8 | |
|
|
9
|
+
JS type | C type | Bytes | Signedness | Note
|
|
10
|
+
---------------- | ----------------------------- | ----- | ---------- | ---------------------------
|
|
11
|
+
Undefined | void | 0 | | Only valid as a return type
|
|
12
|
+
Number (integer) | int8, int8_t | 1 | Signed |
|
|
13
|
+
Number (integer) | uint8, uint8_t | 1 | Unsigned |
|
|
14
|
+
Number (integer) | char | 1 | Signed |
|
|
15
|
+
Number (integer) | uchar, unsigned char | 1 | Unsigned |
|
|
16
|
+
Number (integer) | char16, char16_t | 2 | Signed |
|
|
17
|
+
Number (integer) | int16, int16_t | 2 | Signed |
|
|
18
|
+
Number (integer) | uint16, uint16_t | 2 | Unsigned |
|
|
19
|
+
Number (integer) | short | 2 | Signed |
|
|
20
|
+
Number (integer) | ushort, unsigned short | 2 | Unsigned |
|
|
21
|
+
Number (integer) | int32, int32_t | 4 | Signed |
|
|
22
|
+
Number (integer) | uint32, uint32_t | 4 | Unsigned |
|
|
23
|
+
Number (integer) | int | 4 | Signed |
|
|
24
|
+
Number (integer) | uint, unsigned int | 4 | Unsigned |
|
|
25
|
+
Number (integer) | int64, int64_t | 8 | Signed |
|
|
26
|
+
Number (integer) | uint64, uint64_t | 8 | Unsigned |
|
|
27
|
+
Number (integer) | longlong, long long | 8 | Signed |
|
|
28
|
+
Number (integer) | ulonglong, unsigned long long | 8 | Unsigned |
|
|
29
|
+
Number (float) | float32 | 4 | |
|
|
30
|
+
Number (float) | float64 | 8 | |
|
|
31
|
+
Number (float) | float | 4 | |
|
|
32
|
+
Number (float) | double | 8 | |
|
|
44
33
|
|
|
45
34
|
Koffi also accepts BigInt values when converting from JS to C integers. If the value exceeds the range of the C type, Koffi will convert the number to an undefined value. In the reverse direction, BigInt values are automatically used when needed for big 64-bit integers.
|
|
46
35
|
|
|
@@ -56,8 +45,8 @@ Number (integer) | intptr | Signed | 4 or 8 bytes depending on reg
|
|
|
56
45
|
Number (integer) | intptr_t | Signed | 4 or 8 bytes depending on register width
|
|
57
46
|
Number (integer) | uintptr | Unsigned | 4 or 8 bytes depending on register width
|
|
58
47
|
Number (integer) | uintptr_t | Unsigned | 4 or 8 bytes depending on register width
|
|
59
|
-
String | str
|
|
60
|
-
String | str16
|
|
48
|
+
String | str, string | | JS strings are converted to and from UTF-8
|
|
49
|
+
String | str16, string16 | | JS strings are converted to and from UTF-16 (LE)
|
|
61
50
|
|
|
62
51
|
Primitive types can be specified by name (in a string) or through `koffi.types`:
|
|
63
52
|
|
|
@@ -67,8 +56,31 @@ let struct1 = koffi.struct({ dummy: 'long' });
|
|
|
67
56
|
let struct2 = koffi.struct({ dummy: koffi.types.long });
|
|
68
57
|
```
|
|
69
58
|
|
|
59
|
+
### Endian-sensitive types
|
|
60
|
+
|
|
61
|
+
*New in Koffi 2.1*
|
|
62
|
+
|
|
63
|
+
Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
|
|
64
|
+
|
|
65
|
+
JS type | C type | Bytes | Signedness | Endianness
|
|
66
|
+
---------------- | ---------------------- | ----- | ---------- | -------------
|
|
67
|
+
Number (integer) | int16_le, int16_le_t | 2 | Signed | Little Endian
|
|
68
|
+
Number (integer) | int16_be, int16_be_t | 2 | Signed | Big Endian
|
|
69
|
+
Number (integer) | uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
|
|
70
|
+
Number (integer) | uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
|
|
71
|
+
Number (integer) | int32_le, int32_le_t | 4 | Signed | Little Endian
|
|
72
|
+
Number (integer) | int32_be, int32_be_t | 4 | Signed | Big Endian
|
|
73
|
+
Number (integer) | uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
|
|
74
|
+
Number (integer) | uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
|
|
75
|
+
Number (integer) | int64_le, int64_le_t | 8 | Signed | Little Endian
|
|
76
|
+
Number (integer) | int64_be, int64_be_t | 8 | Signed | Big Endian
|
|
77
|
+
Number (integer) | uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
|
|
78
|
+
Number (integer) | uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
|
|
79
|
+
|
|
70
80
|
## Struct types
|
|
71
81
|
|
|
82
|
+
### Struct definition
|
|
83
|
+
|
|
72
84
|
Koffi converts JS objects to C structs, and vice-versa.
|
|
73
85
|
|
|
74
86
|
Unlike function declarations, as of now there is only one way to create a struct type, with the `koffi.struct()` function. This function takes two arguments: the first one is the name of the type, and the second one is an object containing the struct member names and types. You can omit the first argument to declare an anonymous struct.
|
|
@@ -109,56 +121,44 @@ const Function1 = lib.func('A Function(A value)');
|
|
|
109
121
|
const Function2 = lib.func('Function', A, [A]);
|
|
110
122
|
```
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
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:
|
|
115
|
-
|
|
116
|
-
- **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).
|
|
117
|
-
- **Opaque handles**: 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 a handle. This is usually done for ABI-stability reason, and to prevent library users from messing directly with library internals.
|
|
118
|
-
- **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.
|
|
119
|
-
- **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.
|
|
124
|
+
Koffi automatically follows the platform C ABI regarding alignment and padding. However, you can override these rules if needed with:
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
|
|
126
|
+
- Pack all members without padding with `koffi.pack()` (instead of `koffi.struct()`)
|
|
127
|
+
- Change alignment of a specific member as shown below
|
|
124
128
|
|
|
125
129
|
```js
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const POINT = koffi.struct('POINT', {
|
|
131
|
-
x: 'long',
|
|
132
|
-
y: 'long'
|
|
130
|
+
// This struct is 3 bytes long
|
|
131
|
+
const PackedStruct = koffi.pack('PackedStruct', {
|
|
132
|
+
a: 'int8_t',
|
|
133
|
+
b: 'int16_t'
|
|
133
134
|
});
|
|
134
135
|
|
|
135
|
-
//
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!GetCursorPos(pos))
|
|
141
|
-
throw new Error('Failed to get cursor position');
|
|
142
|
-
console.log(pos);
|
|
136
|
+
// This one is 18 bytes long, the second member has an alignment requirement of 16 bytes
|
|
137
|
+
const BigStruct = koffi.struct('BigStruct', {
|
|
138
|
+
a: 'int8_t',
|
|
139
|
+
b: [16, 'int16_t']
|
|
140
|
+
})
|
|
143
141
|
```
|
|
144
142
|
|
|
145
|
-
### Opaque
|
|
143
|
+
### Opaque types
|
|
146
144
|
|
|
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
|
|
145
|
+
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()`.
|
|
148
146
|
|
|
149
|
-
In Koffi, you can manage this with opaque
|
|
147
|
+
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](functions.md#output-parameters) (with a double pointer).
|
|
150
148
|
|
|
151
149
|
```{note}
|
|
152
|
-
Opaque
|
|
150
|
+
Opaque types **have changed in version 2.0, and again in version 2.1**.
|
|
153
151
|
|
|
154
152
|
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
153
|
|
|
156
154
|
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
155
|
|
|
156
|
+
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.
|
|
157
|
+
|
|
158
158
|
Consult the [migration guide](changes.md) for more information.
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
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
|
|
161
|
+
The full example below implements an iterative string builder (concatenator) in C, and uses it from Javascript to output a mix of Hello World and FizzBuzz. The builder is hidden behind an opaque type, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
|
|
162
162
|
|
|
163
163
|
```c
|
|
164
164
|
// Build with: clang -fPIC -o handles.so -shared handles.c -Wall -O2
|
|
@@ -283,7 +283,7 @@ const char *ConcatBuild(Concat *c)
|
|
|
283
283
|
const koffi = require('koffi');
|
|
284
284
|
const lib = koffi.load('./handles.so');
|
|
285
285
|
|
|
286
|
-
const Concat = koffi.
|
|
286
|
+
const Concat = koffi.opaque('Concat');
|
|
287
287
|
const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat **out)');
|
|
288
288
|
const ConcatNew = lib.func('Concat *ConcatNew()');
|
|
289
289
|
const ConcatFree = lib.func('void ConcatFree(Concat *c)');
|
|
@@ -332,109 +332,51 @@ try {
|
|
|
332
332
|
}
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
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).
|
|
338
|
-
|
|
339
|
-
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.
|
|
340
|
-
|
|
341
|
-
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.
|
|
342
|
-
|
|
343
|
-
```c
|
|
344
|
-
// Build with: clang -fPIC -o length.so -shared length.c -Wall -O2
|
|
345
|
-
|
|
346
|
-
#include <stdlib.h>
|
|
347
|
-
#include <stdint.h>
|
|
348
|
-
#include <string.h>
|
|
349
|
-
|
|
350
|
-
int64_t ComputeTotalLength(const char **strings)
|
|
351
|
-
{
|
|
352
|
-
int64_t total = 0;
|
|
353
|
-
|
|
354
|
-
for (const char **ptr = strings; *ptr; ptr++) {
|
|
355
|
-
const char *str = *ptr;
|
|
356
|
-
total += strlen(str);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return total;
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
```js
|
|
364
|
-
const koffi = require('koffi');
|
|
365
|
-
const lib = koffi.load('./length.so');
|
|
366
|
-
|
|
367
|
-
const ComputeTotalLength = lib.func('int64_t ComputeTotalLength(const char **strings)');
|
|
335
|
+
## Pointer types
|
|
368
336
|
|
|
369
|
-
|
|
370
|
-
let total = ComputeTotalLength(strings);
|
|
337
|
+
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:
|
|
371
338
|
|
|
372
|
-
|
|
373
|
-
|
|
339
|
+
- **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).
|
|
340
|
+
- **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.
|
|
341
|
+
- **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.
|
|
342
|
+
- **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.
|
|
374
343
|
|
|
375
|
-
|
|
344
|
+
### Struct pointers
|
|
376
345
|
|
|
377
|
-
|
|
346
|
+
The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
|
|
378
347
|
|
|
379
348
|
```js
|
|
380
349
|
const koffi = require('koffi');
|
|
381
350
|
const lib = koffi.load('kernel32.dll');
|
|
382
351
|
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
});
|
|
388
|
-
const WIN32_FIND_DATA = koffi.struct('WIN32_FIND_DATA', {
|
|
389
|
-
dwFileAttributes: 'uint',
|
|
390
|
-
ftCreationTime: FILETIME,
|
|
391
|
-
ftLastAccessTime: FILETIME,
|
|
392
|
-
ftLastWriteTime: FILETIME,
|
|
393
|
-
nFileSizeHigh: 'uint',
|
|
394
|
-
nFileSizeLow: 'uint',
|
|
395
|
-
dwReserved0: 'uint',
|
|
396
|
-
dwReserved1: 'uint',
|
|
397
|
-
cFileName: koffi.array('char16', 260),
|
|
398
|
-
cAlternateFileName: koffi.array('char16', 14),
|
|
399
|
-
dwFileType: 'uint', // Obsolete. Do not use.
|
|
400
|
-
dwCreatorType: 'uint', // Obsolete. Do not use
|
|
401
|
-
wFinderFlags: 'ushort' // Obsolete. Do not use
|
|
352
|
+
// Type declarations
|
|
353
|
+
const POINT = koffi.struct('POINT', {
|
|
354
|
+
x: 'long',
|
|
355
|
+
y: 'long'
|
|
402
356
|
});
|
|
403
357
|
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
const FindClose = lib.func('bool __stdcall FindClose(HANDLE h)');
|
|
407
|
-
const GetLastError = lib.func('uint GetLastError()');
|
|
408
|
-
|
|
409
|
-
function list(dirname) {
|
|
410
|
-
let filenames = [];
|
|
358
|
+
// Functions declarations
|
|
359
|
+
const GetCursorPos = lib.func('int __stdcall GetCursorPos(_Out_ POINT *pos)');
|
|
411
360
|
|
|
412
|
-
|
|
413
|
-
|
|
361
|
+
// Get and show cursor position
|
|
362
|
+
let pos = {};
|
|
363
|
+
if (!GetCursorPos(pos))
|
|
364
|
+
throw new Error('Failed to get cursor position');
|
|
365
|
+
console.log(pos);
|
|
366
|
+
```
|
|
414
367
|
|
|
415
|
-
|
|
416
|
-
if (GetLastError() == 2) // ERROR_FILE_NOT_FOUND
|
|
417
|
-
return filenames;
|
|
418
|
-
throw new Error('FindFirstFile() failed');
|
|
419
|
-
}
|
|
368
|
+
### Named pointer types
|
|
420
369
|
|
|
421
|
-
|
|
422
|
-
do {
|
|
423
|
-
if (data.cFileName != '.' && data.cFileName != '..')
|
|
424
|
-
filenames.push(data.cFileName);
|
|
425
|
-
} while (FindNextFile(h, data));
|
|
370
|
+
*New in Koffi 2.0*
|
|
426
371
|
|
|
427
|
-
|
|
428
|
-
throw new Error('FindNextFile() failed');
|
|
429
|
-
} finally {
|
|
430
|
-
FindClose(h);
|
|
431
|
-
}
|
|
372
|
+
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:
|
|
432
373
|
|
|
433
|
-
|
|
434
|
-
|
|
374
|
+
```js
|
|
375
|
+
const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
|
|
435
376
|
|
|
436
|
-
|
|
437
|
-
|
|
377
|
+
// And now you get to use it this way:
|
|
378
|
+
const GetHandleInformation = lib.func('bool __stdcall GetHandleInformation(HANDLE h, _Out_ uint32_t *flags)');
|
|
379
|
+
const CloseHandle = lib.func('bool __stdcall CloseHandle(HANDLE h)');
|
|
438
380
|
```
|
|
439
381
|
|
|
440
382
|
### Pointers to primitive types
|
|
@@ -474,7 +416,9 @@ AddInt(sum, 6);
|
|
|
474
416
|
console.log(sum[0]); // Prints 42
|
|
475
417
|
```
|
|
476
418
|
|
|
477
|
-
##
|
|
419
|
+
## Array types
|
|
420
|
+
|
|
421
|
+
### Fixed-size C arrays
|
|
478
422
|
|
|
479
423
|
Fixed-size arrays are declared with `koffi.array(type, length)`. Just like in C, they cannot be passed as functions parameters (they degenerate to pointers), or returned by value. You can however embed them in struct types.
|
|
480
424
|
|
|
@@ -506,7 +450,7 @@ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array
|
|
|
506
450
|
console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
|
|
507
451
|
```
|
|
508
452
|
|
|
509
|
-
###
|
|
453
|
+
### Fixed-size string buffers
|
|
510
454
|
|
|
511
455
|
Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
|
512
456
|
|
|
@@ -515,6 +459,48 @@ Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
|
|
515
459
|
|
|
516
460
|
The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS string. This happens by default for char, char16 and char16_t arrays, but you can also explicitly ask for this with the `string` array hint (e.g. `koffi.array('char', 8, 'string')`).
|
|
517
461
|
|
|
462
|
+
### Array pointers (dynamic arrays)
|
|
463
|
+
|
|
464
|
+
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).
|
|
465
|
+
|
|
466
|
+
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.
|
|
467
|
+
|
|
468
|
+
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.
|
|
469
|
+
|
|
470
|
+
```c
|
|
471
|
+
// Build with: clang -fPIC -o length.so -shared length.c -Wall -O2
|
|
472
|
+
|
|
473
|
+
#include <stdlib.h>
|
|
474
|
+
#include <stdint.h>
|
|
475
|
+
#include <string.h>
|
|
476
|
+
|
|
477
|
+
int64_t ComputeTotalLength(const char **strings)
|
|
478
|
+
{
|
|
479
|
+
int64_t total = 0;
|
|
480
|
+
|
|
481
|
+
for (const char **ptr = strings; *ptr; ptr++) {
|
|
482
|
+
const char *str = *ptr;
|
|
483
|
+
total += strlen(str);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return total;
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
```js
|
|
491
|
+
const koffi = require('koffi');
|
|
492
|
+
const lib = koffi.load('./length.so');
|
|
493
|
+
|
|
494
|
+
const ComputeTotalLength = lib.func('int64_t ComputeTotalLength(const char **strings)');
|
|
495
|
+
|
|
496
|
+
let strings = ['Get', 'Total', 'Length', null];
|
|
497
|
+
let total = ComputeTotalLength(strings);
|
|
498
|
+
|
|
499
|
+
console.log(total); // Prints 14
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
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).
|
|
503
|
+
|
|
518
504
|
## Disposable types
|
|
519
505
|
|
|
520
506
|
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.
|
|
@@ -525,6 +511,8 @@ Read the documentation for [disposable types](functions.md#heap-allocated-values
|
|
|
525
511
|
|
|
526
512
|
### Type introspection
|
|
527
513
|
|
|
514
|
+
*New in Koffi 2.0: `koffi.resolve()`*
|
|
515
|
+
|
|
528
516
|
Koffi exposes three functions to explore type information:
|
|
529
517
|
|
|
530
518
|
- `koffi.sizeof(type)` to get the size of a type
|
|
@@ -550,4 +538,6 @@ console.log(koffi.sizeof(koffi.types.long));
|
|
|
550
538
|
|
|
551
539
|
### Type aliases
|
|
552
540
|
|
|
541
|
+
*New in Koffi 2.0*
|
|
542
|
+
|
|
553
543
|
You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koffi",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-beta.3",
|
|
4
|
+
"stable": "2.0.1",
|
|
4
5
|
"description": "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"foreign",
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
},
|
|
26
27
|
"license": "AGPL-3.0",
|
|
27
28
|
"dependencies": {
|
|
28
|
-
"cnoke": "^3.
|
|
29
|
+
"cnoke": "^3.2.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"chalk": "^4.1.2",
|
package/qemu/qemu.js
CHANGED
|
@@ -42,7 +42,7 @@ let qemu_prefix = null;
|
|
|
42
42
|
main();
|
|
43
43
|
|
|
44
44
|
async function main() {
|
|
45
|
-
script_dir = fs.realpathSync(
|
|
45
|
+
script_dir = fs.realpathSync(__dirname);
|
|
46
46
|
root_dir = fs.realpathSync(script_dir + '/../..');
|
|
47
47
|
|
|
48
48
|
// All the code assumes we are working from the script directory
|