koffi 2.2.3 → 2.2.5

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.
Files changed (102) hide show
  1. package/{ChangeLog.md → CHANGELOG.md} +19 -3
  2. package/LICENSE.txt +160 -656
  3. package/README.md +1 -1
  4. package/doc/calls.md +277 -0
  5. package/doc/changes.md +1 -1
  6. package/doc/functions.md +17 -275
  7. package/doc/index.rst +4 -2
  8. package/doc/misc.md +97 -0
  9. package/doc/pointers.md +135 -0
  10. package/doc/static/custom.css +3 -3
  11. package/doc/types.md +3 -193
  12. package/package.json +4 -4
  13. package/src/cnoke/LICENSE.txt +160 -656
  14. package/src/cnoke/assets/FindCNoke.cmake +3 -3
  15. package/src/cnoke/assets/win_delay_hook.c +3 -3
  16. package/src/cnoke/cnoke.js +3 -3
  17. package/src/cnoke/package.json +2 -2
  18. package/src/core/libcc/libcc.cc +4 -4
  19. package/src/core/libcc/libcc.hh +3 -3
  20. package/src/koffi/CMakeLists.txt +3 -3
  21. package/src/koffi/benchmark/CMakeLists.txt +3 -3
  22. package/src/koffi/benchmark/atoi_koffi.js +3 -3
  23. package/src/koffi/benchmark/atoi_napi.cc +3 -3
  24. package/src/koffi/benchmark/atoi_napi.js +3 -3
  25. package/src/koffi/benchmark/atoi_node_ffi.js +3 -3
  26. package/src/koffi/benchmark/benchmark.js +3 -3
  27. package/src/koffi/benchmark/rand_koffi.js +3 -3
  28. package/src/koffi/benchmark/rand_napi.cc +3 -3
  29. package/src/koffi/benchmark/rand_napi.js +3 -3
  30. package/src/koffi/benchmark/rand_node_ffi.js +3 -3
  31. package/src/koffi/benchmark/raylib_cc.cc +3 -3
  32. package/src/koffi/benchmark/raylib_cc.js +3 -3
  33. package/src/koffi/benchmark/raylib_koffi.js +3 -3
  34. package/src/koffi/benchmark/raylib_node_ffi.js +3 -3
  35. package/src/koffi/benchmark/raylib_node_raylib.js +3 -3
  36. package/src/koffi/build/2.2.5/koffi_darwin_arm64.tar.gz +0 -0
  37. package/src/koffi/build/2.2.5/koffi_darwin_x64.tar.gz +0 -0
  38. package/src/koffi/build/2.2.5/koffi_freebsd_arm64.tar.gz +0 -0
  39. package/src/koffi/build/2.2.5/koffi_freebsd_ia32.tar.gz +0 -0
  40. package/src/koffi/build/2.2.5/koffi_freebsd_x64.tar.gz +0 -0
  41. package/src/koffi/build/2.2.5/koffi_linux_arm32hf.tar.gz +0 -0
  42. package/src/koffi/build/2.2.5/koffi_linux_arm64.tar.gz +0 -0
  43. package/src/koffi/build/2.2.5/koffi_linux_ia32.tar.gz +0 -0
  44. package/src/koffi/build/2.2.5/koffi_linux_riscv64hf64.tar.gz +0 -0
  45. package/src/koffi/build/2.2.5/koffi_linux_x64.tar.gz +0 -0
  46. package/src/koffi/build/2.2.5/koffi_openbsd_ia32.tar.gz +0 -0
  47. package/src/koffi/build/2.2.5/koffi_openbsd_x64.tar.gz +0 -0
  48. package/src/koffi/build/2.2.5/koffi_win32_arm64.tar.gz +0 -0
  49. package/src/koffi/build/2.2.5/koffi_win32_ia32.tar.gz +0 -0
  50. package/src/koffi/build/2.2.5/koffi_win32_x64.tar.gz +0 -0
  51. package/src/koffi/src/abi_arm32.cc +3 -3
  52. package/src/koffi/src/abi_arm32_fwd.S +3 -3
  53. package/src/koffi/src/abi_arm64.cc +3 -3
  54. package/src/koffi/src/abi_arm64_fwd.S +3 -3
  55. package/src/koffi/src/abi_arm64_fwd.asm +3 -3
  56. package/src/koffi/src/abi_riscv64.cc +3 -3
  57. package/src/koffi/src/abi_riscv64_fwd.S +3 -3
  58. package/src/koffi/src/abi_trampolines.inc +3 -3
  59. package/src/koffi/src/abi_x64_sysv.cc +3 -3
  60. package/src/koffi/src/abi_x64_sysv_fwd.S +3 -3
  61. package/src/koffi/src/abi_x64_win.cc +3 -3
  62. package/src/koffi/src/abi_x64_win_fwd.asm +3 -3
  63. package/src/koffi/src/abi_x86.cc +3 -3
  64. package/src/koffi/src/abi_x86_fwd.S +3 -3
  65. package/src/koffi/src/abi_x86_fwd.asm +3 -3
  66. package/src/koffi/src/call.cc +3 -3
  67. package/src/koffi/src/call.hh +3 -3
  68. package/src/koffi/src/ffi.cc +5 -5
  69. package/src/koffi/src/ffi.hh +3 -7
  70. package/src/koffi/src/index.js +3 -3
  71. package/src/koffi/src/parser.cc +3 -3
  72. package/src/koffi/src/parser.hh +3 -3
  73. package/src/koffi/src/util.cc +3 -3
  74. package/src/koffi/src/util.hh +3 -3
  75. package/src/koffi/src/win32.hh +3 -3
  76. package/src/koffi/test/CMakeLists.txt +3 -3
  77. package/src/koffi/test/async.js +3 -3
  78. package/src/koffi/test/callbacks.js +3 -3
  79. package/src/koffi/test/misc.c +3 -3
  80. package/src/koffi/test/raylib.js +3 -3
  81. package/src/koffi/test/sqlite.js +3 -3
  82. package/src/koffi/test/sync.js +3 -3
  83. package/src/koffi/test/win32.c +3 -3
  84. package/src/koffi/test/win32.js +3 -3
  85. package/src/koffi/tools/qemu.js +4 -4
  86. package/src/koffi/tools/write_trampolines.py +3 -3
  87. package/doc/memory.md +0 -33
  88. package/src/koffi/build/2.2.3/koffi_darwin_arm64.tar.gz +0 -0
  89. package/src/koffi/build/2.2.3/koffi_darwin_x64.tar.gz +0 -0
  90. package/src/koffi/build/2.2.3/koffi_freebsd_arm64.tar.gz +0 -0
  91. package/src/koffi/build/2.2.3/koffi_freebsd_ia32.tar.gz +0 -0
  92. package/src/koffi/build/2.2.3/koffi_freebsd_x64.tar.gz +0 -0
  93. package/src/koffi/build/2.2.3/koffi_linux_arm32hf.tar.gz +0 -0
  94. package/src/koffi/build/2.2.3/koffi_linux_arm64.tar.gz +0 -0
  95. package/src/koffi/build/2.2.3/koffi_linux_ia32.tar.gz +0 -0
  96. package/src/koffi/build/2.2.3/koffi_linux_riscv64hf64.tar.gz +0 -0
  97. package/src/koffi/build/2.2.3/koffi_linux_x64.tar.gz +0 -0
  98. package/src/koffi/build/2.2.3/koffi_openbsd_ia32.tar.gz +0 -0
  99. package/src/koffi/build/2.2.3/koffi_openbsd_x64.tar.gz +0 -0
  100. package/src/koffi/build/2.2.3/koffi_win32_arm64.tar.gz +0 -0
  101. package/src/koffi/build/2.2.3/koffi_win32_ia32.tar.gz +0 -0
  102. package/src/koffi/build/2.2.3/koffi_win32_x64.tar.gz +0 -0
package/README.md CHANGED
@@ -31,6 +31,6 @@ Major version increments can include breaking API changes, use the [migration gu
31
31
 
32
32
  # License
33
33
 
34
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
34
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
35
35
 
36
36
  Find more information here: https://www.gnu.org/licenses/
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/changes.md CHANGED
@@ -1,4 +1,4 @@
1
- {{ "```{include} " ~ root ~ "/ChangeLog.md" ~ "\n```" }}
1
+ {{ "```{include} " ~ root ~ "/CHANGELOG.md" ~ "\n```" }}
2
2
 
3
3
  ## Migration guide
4
4
 
package/doc/functions.md CHANGED
@@ -1,6 +1,6 @@
1
- # Functions
1
+ # Function definitions
2
2
 
3
- ## Function definitions
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
- ## Function calls
39
+ ## Variadic functions
40
40
 
41
- ### Calling conventions
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
@@ -25,9 +25,11 @@ Table of contents
25
25
  platforms
26
26
  start
27
27
  types
28
+ pointers
28
29
  functions
30
+ calls
29
31
  callbacks
30
- memory
32
+ misc
31
33
  benchmarks
32
34
  contribute
33
35
  changes
@@ -35,6 +37,6 @@ Table of contents
35
37
  License
36
38
  -------
37
39
 
38
- This program is free software: you can redistribute it and/or modify it under the terms of the **GNU Affero General Public License** as published by the Free Software Foundation, either **version 3 of the License**, or (at your option) any later version.
40
+ This program is free software: you can redistribute it and/or modify it under the terms of the **GNU Lesser General Public License** as published by the Free Software Foundation, either **version 3 of the License**, or (at your option) any later version.
39
41
 
40
42
  Find more information here: https://www.gnu.org/licenses/