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.
Files changed (90) hide show
  1. package/CMakeLists.txt +2 -9
  2. package/ChangeLog.md +17 -0
  3. package/benchmark/atoi_koffi.js +12 -8
  4. package/benchmark/atoi_napi.js +12 -8
  5. package/benchmark/atoi_node_ffi.js +11 -10
  6. package/benchmark/raylib_cc.cc +12 -9
  7. package/benchmark/raylib_koffi.js +15 -13
  8. package/benchmark/raylib_node_ffi.js +15 -13
  9. package/benchmark/raylib_node_raylib.js +14 -11
  10. package/build/qemu/2.1.0-beta.3/koffi_darwin_arm64.tar.gz +0 -0
  11. package/build/qemu/2.1.0-beta.3/koffi_darwin_x64.tar.gz +0 -0
  12. package/build/qemu/2.1.0-beta.3/koffi_freebsd_arm64.tar.gz +0 -0
  13. package/build/qemu/2.1.0-beta.3/koffi_freebsd_ia32.tar.gz +0 -0
  14. package/build/qemu/2.1.0-beta.3/koffi_freebsd_x64.tar.gz +0 -0
  15. package/build/qemu/2.1.0-beta.3/koffi_linux_arm32hf.tar.gz +0 -0
  16. package/build/qemu/2.1.0-beta.3/koffi_linux_arm64.tar.gz +0 -0
  17. package/build/qemu/2.1.0-beta.3/koffi_linux_ia32.tar.gz +0 -0
  18. package/build/qemu/2.1.0-beta.3/koffi_linux_riscv64hf64.tar.gz +0 -0
  19. package/build/qemu/2.1.0-beta.3/koffi_linux_x64.tar.gz +0 -0
  20. package/build/qemu/2.1.0-beta.3/koffi_openbsd_ia32.tar.gz +0 -0
  21. package/build/qemu/2.1.0-beta.3/koffi_openbsd_x64.tar.gz +0 -0
  22. package/build/qemu/2.1.0-beta.3/koffi_win32_arm64.tar.gz +0 -0
  23. package/build/qemu/2.1.0-beta.3/koffi_win32_ia32.tar.gz +0 -0
  24. package/build/qemu/2.1.0-beta.3/koffi_win32_x64.tar.gz +0 -0
  25. package/doc/Makefile +1 -1
  26. package/doc/changes.md +12 -8
  27. package/doc/conf.py +5 -0
  28. package/doc/dist/doctrees/changes.doctree +0 -0
  29. package/doc/dist/doctrees/environment.pickle +0 -0
  30. package/doc/dist/doctrees/functions.doctree +0 -0
  31. package/doc/dist/doctrees/index.doctree +0 -0
  32. package/doc/dist/doctrees/types.doctree +0 -0
  33. package/doc/dist/html/.buildinfo +1 -1
  34. package/doc/dist/html/_sources/changes.md.txt +12 -8
  35. package/doc/dist/html/_sources/functions.md.txt +71 -5
  36. package/doc/dist/html/_sources/types.md.txt +147 -159
  37. package/doc/dist/html/benchmarks.html +2 -3
  38. package/doc/dist/html/changes.html +64 -35
  39. package/doc/dist/html/contribute.html +2 -3
  40. package/doc/dist/html/functions.html +73 -12
  41. package/doc/dist/html/genindex.html +2 -3
  42. package/doc/dist/html/index.html +6 -7
  43. package/doc/dist/html/memory.html +2 -3
  44. package/doc/dist/html/objects.inv +0 -0
  45. package/doc/dist/html/platforms.html +3 -4
  46. package/doc/dist/html/search.html +2 -3
  47. package/doc/dist/html/searchindex.js +1 -1
  48. package/doc/dist/html/start.html +2 -3
  49. package/doc/dist/html/types.html +238 -237
  50. package/doc/functions.md +71 -5
  51. package/doc/make.bat +1 -1
  52. package/doc/templates/badges.html +1 -2
  53. package/doc/types.md +149 -159
  54. package/package.json +3 -2
  55. package/qemu/qemu.js +1 -1
  56. package/src/abi_arm32.cc +208 -102
  57. package/src/abi_arm64.cc +239 -55
  58. package/src/abi_riscv64.cc +128 -40
  59. package/src/abi_x64_sysv.cc +135 -41
  60. package/src/abi_x64_win.cc +134 -40
  61. package/src/abi_x86.cc +182 -67
  62. package/src/call.cc +241 -26
  63. package/src/call.hh +15 -3
  64. package/src/ffi.cc +120 -31
  65. package/src/ffi.hh +19 -0
  66. package/src/index.js +4 -2
  67. package/src/parser.cc +3 -5
  68. package/src/util.cc +44 -1
  69. package/src/util.hh +4 -0
  70. package/test/async.js +1 -2
  71. package/test/callbacks.js +2 -3
  72. package/test/misc.c +64 -1
  73. package/test/raylib.js +1 -1
  74. package/test/sqlite.js +3 -3
  75. package/test/sync.js +108 -3
  76. package/build/qemu/2.0.1/koffi_darwin_arm64.tar.gz +0 -0
  77. package/build/qemu/2.0.1/koffi_darwin_x64.tar.gz +0 -0
  78. package/build/qemu/2.0.1/koffi_freebsd_arm64.tar.gz +0 -0
  79. package/build/qemu/2.0.1/koffi_freebsd_ia32.tar.gz +0 -0
  80. package/build/qemu/2.0.1/koffi_freebsd_x64.tar.gz +0 -0
  81. package/build/qemu/2.0.1/koffi_linux_arm32hf.tar.gz +0 -0
  82. package/build/qemu/2.0.1/koffi_linux_arm64.tar.gz +0 -0
  83. package/build/qemu/2.0.1/koffi_linux_ia32.tar.gz +0 -0
  84. package/build/qemu/2.0.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  85. package/build/qemu/2.0.1/koffi_linux_x64.tar.gz +0 -0
  86. package/build/qemu/2.0.1/koffi_openbsd_ia32.tar.gz +0 -0
  87. package/build/qemu/2.0.1/koffi_openbsd_x64.tar.gz +0 -0
  88. package/build/qemu/2.0.1/koffi_win32_arm64.tar.gz +0 -0
  89. package/build/qemu/2.0.1/koffi_win32_ia32.tar.gz +0 -0
  90. package/build/qemu/2.0.1/koffi_win32_x64.tar.gz +0 -0
@@ -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
- ## C to JS conversion gotchas
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 handles](types.md#opaque-handles)
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 handle example
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.handle('sqlite3');
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 handles, such as FILE, this does not matter because you will explicitly call `fclose()` on them. But some values (such as strings) get implicitly converted by Koffi, and you lose access to the original pointer. This creates a leak if the string is heap-allocated.
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.
@@ -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 | C type | Bytes | Signedness | Note
8
- ----------------- | ------------------ | ----- | ---------- | ---------------------------
9
- Null | void | 0 | | Only valid as a return type
10
- Number (integer) | int8 | 1 | Signed |
11
- Number (integer) | int8_t | 1 | Signed |
12
- Number (integer) | uint8 | 1 | Unsigned |
13
- Number (integer) | uint8_t | 1 | Unsigned |
14
- Number (integer) | char | 1 | Signed |
15
- Number (integer) | uchar | 1 | Unsigned |
16
- Number (integer) | unsigned char | 1 | Unsigned |
17
- Number (integer) | char16 | 2 | Signed |
18
- Number (integer) | char16_t | 2 | Signed |
19
- Number (integer) | int16 | 2 | Signed |
20
- Number (integer) | int16_t | 2 | Signed |
21
- Number (integer) | uint16 | 2 | Unsigned |
22
- Number (integer) | uint16_t | 2 | Unsigned |
23
- Number (integer) | short | 2 | Signed |
24
- Number (integer) | unsigned short | 2 | Unsigned |
25
- Number (integer) | int32 | 4 | Signed |
26
- Number (integer) | int32_t | 4 | Signed |
27
- Number (integer) | uint32 | 4 | Unsigned |
28
- Number (integer) | uint32_t | 4 | Unsigned |
29
- Number (integer) | int | 4 | Signed |
30
- Number (integer) | uint | 4 | Unsigned |
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 (string) | | JS strings are converted to and from UTF-8
60
- String | str16 (string16) | | JS strings are converted to and from UTF-16 (LE)
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,29 @@ 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
+ Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
62
+
63
+ JS type | C type | Bytes | Signedness | Endianness
64
+ ---------------- | ---------------------- | ----- | ---------- | -------------
65
+ Number (integer) | int16_le, int16_le_t | 2 | Signed | Little Endian
66
+ Number (integer) | int16_be, int16_be_t | 2 | Signed | Big Endian
67
+ Number (integer) | uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
68
+ Number (integer) | uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
69
+ Number (integer) | int32_le, int32_le_t | 4 | Signed | Little Endian
70
+ Number (integer) | int32_be, int32_be_t | 4 | Signed | Big Endian
71
+ Number (integer) | uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
72
+ Number (integer) | uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
73
+ Number (integer) | int64_le, int64_le_t | 8 | Signed | Little Endian
74
+ Number (integer) | int64_be, int64_be_t | 8 | Signed | Big Endian
75
+ Number (integer) | uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
76
+ Number (integer) | uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
77
+
70
78
  ## Struct types
71
79
 
80
+ ### Struct definition
81
+
72
82
  Koffi converts JS objects to C structs, and vice-versa.
73
83
 
74
84
  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 +119,44 @@ const Function1 = lib.func('A Function(A value)');
109
119
  const Function2 = lib.func('Function', A, [A]);
110
120
  ```
111
121
 
112
- ## Pointer types
122
+ Koffi automatically follows the platform C ABI regarding alignment and padding. However, you can override these rules if needed with:
113
123
 
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.
120
-
121
- ### Struct pointers
122
-
123
- The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
124
+ - Pack all members without padding with `koffi.pack()` (instead of `koffi.struct()`)
125
+ - Change alignment of a specific member as shown below
124
126
 
125
127
  ```js
126
- const koffi = require('koffi');
127
- const lib = koffi.load('kernel32.dll');
128
-
129
- // Type declarations
130
- const POINT = koffi.struct('POINT', {
131
- x: 'long',
132
- y: 'long'
128
+ // This struct is 3 bytes long
129
+ const PackedStruct = koffi.pack('PackedStruct', {
130
+ a: 'int8_t',
131
+ b: 'int16_t'
133
132
  });
134
133
 
135
- // Functions declarations
136
- const GetCursorPos = lib.func('int __stdcall GetCursorPos(_Out_ POINT *pos)');
137
-
138
- // Get and show cursor position
139
- let pos = {};
140
- if (!GetCursorPos(pos))
141
- throw new Error('Failed to get cursor position');
142
- console.log(pos);
134
+ // This one is 18 bytes long, the second member has an alignment requirement of 16 bytes
135
+ const BigStruct = koffi.struct('BigStruct', {
136
+ a: 'int8_t',
137
+ b: [16, 'int16_t']
138
+ })
143
139
  ```
144
140
 
145
- ### Opaque handles
141
+ ### Opaque types
146
142
 
147
- Many C libraries use some kind of object-oriented API, with a pair of functions dedicated to create and delete objects. An obvious example of this can be found in stdio.h, with the opaque `FILE *` pointer. You can open and close files with `fopen()` and `fclose()`, and manipule the handle with other functions such as `fread()` or `ftell()`.
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()`.
148
144
 
149
- In Koffi, you can manage this with opaque handles. Declare the handle type with `koffi.handle(name)`, and use a pointer to this type either as a return type or some kind of [output parameter](functions.md#output-parameters) (with a double pointer).
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](functions.md#output-parameters) (with a double pointer).
150
146
 
151
147
  ```{note}
152
- Opaque handles **have changed in version 2.0**.
148
+ Opaque types **have changed in version 2.0, and again in version 2.1**.
153
149
 
154
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.
155
151
 
156
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](functions.md#output-parameters).
157
153
 
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
+
158
156
  Consult the [migration guide](changes.md) for more information.
159
157
  ```
160
158
 
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 handle, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
159
+ The full example below implements an iterative string builder (concatenator) in C, and uses it from Javascript to output a mix of Hello World and FizzBuzz. The builder is hidden behind an opaque type, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
162
160
 
163
161
  ```c
164
162
  // Build with: clang -fPIC -o handles.so -shared handles.c -Wall -O2
@@ -283,7 +281,7 @@ const char *ConcatBuild(Concat *c)
283
281
  const koffi = require('koffi');
284
282
  const lib = koffi.load('./handles.so');
285
283
 
286
- const Concat = koffi.handle('Concat');
284
+ const Concat = koffi.opaque('Concat');
287
285
  const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat **out)');
288
286
  const ConcatNew = lib.func('Concat *ConcatNew()');
289
287
  const ConcatFree = lib.func('void ConcatFree(Concat *c)');
@@ -332,109 +330,51 @@ try {
332
330
  }
333
331
  ```
334
332
 
335
- ### Array pointers
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)');
333
+ ## Pointer types
368
334
 
369
- let strings = ['Get', 'Total', 'Length', null];
370
- let total = ComputeTotalLength(strings);
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:
371
336
 
372
- console.log(total); // Prints 14
373
- ```
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.
374
341
 
375
- 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).
342
+ ### Struct pointers
376
343
 
377
- Here is an example based on the Win32 API, listing files in the current directory with `FindFirstFileW()` and `FindNextFileW()`:
344
+ The following Win32 example uses `GetCursorPos()` (with an output parameter) to retrieve and show the current cursor position.
378
345
 
379
346
  ```js
380
347
  const koffi = require('koffi');
381
348
  const lib = koffi.load('kernel32.dll');
382
349
 
383
- const HANDLE = koffi.pointer('HANDLE', koffi.handle());
384
- const FILETIME = koffi.struct('FILETIME', {
385
- dwLowDateTime: 'uint',
386
- dwHighDateTime: 'uint'
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
350
+ // Type declarations
351
+ const POINT = koffi.struct('POINT', {
352
+ x: 'long',
353
+ y: 'long'
402
354
  });
403
355
 
404
- const FindFirstFile = lib.func('HANDLE __stdcall FindFirstFileW(str16 path, _Out_ WIN32_FIND_DATA *data)');
405
- const FindNextFile = lib.func('bool __stdcall FindNextFileW(HANDLE h, _Out_ WIN32_FIND_DATA *data)');
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 = [];
356
+ // Functions declarations
357
+ const GetCursorPos = lib.func('int __stdcall GetCursorPos(_Out_ POINT *pos)');
411
358
 
412
- let data = {};
413
- let h = FindFirstFile(dirname + '\\*', data);
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
+ ```
414
365
 
415
- if (!h) {
416
- if (GetLastError() == 2) // ERROR_FILE_NOT_FOUND
417
- return filenames;
418
- throw new Error('FindFirstFile() failed');
419
- }
366
+ ### Named pointer types
420
367
 
421
- try {
422
- do {
423
- if (data.cFileName != '.' && data.cFileName != '..')
424
- filenames.push(data.cFileName);
425
- } while (FindNextFile(h, data));
368
+ *New in Koffi 2.0*
426
369
 
427
- if (GetLastError() != 18) // ERROR_NO_MORE_FILES
428
- throw new Error('FindNextFile() failed');
429
- } finally {
430
- FindClose(h);
431
- }
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:
432
371
 
433
- return filenames;
434
- }
372
+ ```js
373
+ const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
435
374
 
436
- let filenames = list('.');
437
- console.log(filenames);
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)');
438
378
  ```
439
379
 
440
380
  ### Pointers to primitive types
@@ -474,7 +414,9 @@ AddInt(sum, 6);
474
414
  console.log(sum[0]); // Prints 42
475
415
  ```
476
416
 
477
- ## Fixed-size C arrays
417
+ ## Array types
418
+
419
+ ### Fixed-size C arrays
478
420
 
479
421
  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
422
 
@@ -506,7 +448,7 @@ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array
506
448
  console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
507
449
  ```
508
450
 
509
- ### Handling of strings
451
+ ### Fixed-size string buffers
510
452
 
511
453
  Koffi can also convert JS strings to fixed-sized arrays in the following cases:
512
454
 
@@ -515,6 +457,48 @@ Koffi can also convert JS strings to fixed-sized arrays in the following cases:
515
457
 
516
458
  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
459
 
460
+ ### Array pointers (dynamic arrays)
461
+
462
+ 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).
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
+
518
502
  ## Disposable types
519
503
 
520
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.
@@ -525,6 +509,8 @@ Read the documentation for [disposable types](functions.md#heap-allocated-values
525
509
 
526
510
  ### Type introspection
527
511
 
512
+ *New in Koffi 2.0: `koffi.resolve()`*
513
+
528
514
  Koffi exposes three functions to explore type information:
529
515
 
530
516
  - `koffi.sizeof(type)` to get the size of a type
@@ -550,4 +536,6 @@ console.log(koffi.sizeof(koffi.types.long));
550
536
 
551
537
  ### Type aliases
552
538
 
539
+ *New in Koffi 2.0*
540
+
553
541
  You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
@@ -174,10 +174,9 @@
174
174
 
175
175
  </div>
176
176
  <div style="text-align: center; margin-top: 2em;">
177
+ <a href="https://www.npmjs.com/package/koffi"><img src="https://img.shields.io/badge/NPM-2.O.1-brightgreen" alt="NPM"/></a>
177
178
  <a href="https://github.com/Koromix/luigi/tree/master/koffi"><img src="https://img.shields.io/badge/GitHub-Koffi-ff6600" alt="GitHub"/></a>
178
- <a href="https://github.com/Koromix/luigi/tree/master/koffi"><img src="https://img.shields.io/badge/NPM-2.0.0-brightgreen" alt="NPM"/></a>
179
- </div>
180
- </div>
179
+ </div></div>
181
180
 
182
181
  </div>
183
182