koffi 2.1.0-beta.1 → 2.1.0-beta.2

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 (99) hide show
  1. package/CMakeLists.txt +1 -1
  2. package/ChangeLog.md +17 -0
  3. package/build/qemu/2.1.0-beta.2/koffi_darwin_arm64.tar.gz +0 -0
  4. package/build/qemu/2.1.0-beta.2/koffi_darwin_x64.tar.gz +0 -0
  5. package/build/qemu/2.1.0-beta.2/koffi_freebsd_arm64.tar.gz +0 -0
  6. package/build/qemu/2.1.0-beta.2/koffi_freebsd_ia32.tar.gz +0 -0
  7. package/build/qemu/2.1.0-beta.2/koffi_freebsd_x64.tar.gz +0 -0
  8. package/build/qemu/2.1.0-beta.2/koffi_linux_arm32hf.tar.gz +0 -0
  9. package/build/qemu/2.1.0-beta.2/koffi_linux_arm64.tar.gz +0 -0
  10. package/build/qemu/2.1.0-beta.2/koffi_linux_ia32.tar.gz +0 -0
  11. package/build/qemu/2.1.0-beta.2/koffi_linux_riscv64hf64.tar.gz +0 -0
  12. package/build/qemu/2.1.0-beta.2/koffi_linux_x64.tar.gz +0 -0
  13. package/build/qemu/2.1.0-beta.2/koffi_openbsd_ia32.tar.gz +0 -0
  14. package/build/qemu/2.1.0-beta.2/koffi_openbsd_x64.tar.gz +0 -0
  15. package/build/qemu/2.1.0-beta.2/koffi_win32_arm64.tar.gz +0 -0
  16. package/build/qemu/2.1.0-beta.2/koffi_win32_ia32.tar.gz +0 -0
  17. package/build/qemu/2.1.0-beta.2/koffi_win32_x64.tar.gz +0 -0
  18. package/doc/functions.md +67 -1
  19. package/doc/templates/badges.html +1 -2
  20. package/doc/types.md +100 -147
  21. package/package.json +1 -1
  22. package/src/abi_arm32.cc +166 -2
  23. package/src/abi_arm64.cc +195 -2
  24. package/src/abi_riscv64.cc +106 -2
  25. package/src/abi_x64_sysv.cc +113 -3
  26. package/src/abi_x64_win.cc +112 -2
  27. package/src/abi_x86.cc +155 -4
  28. package/src/call.cc +199 -0
  29. package/src/ffi.cc +50 -18
  30. package/src/ffi.hh +12 -0
  31. package/build/qemu/2.1.0-beta.1/koffi_darwin_arm64.tar.gz +0 -0
  32. package/build/qemu/2.1.0-beta.1/koffi_darwin_x64.tar.gz +0 -0
  33. package/build/qemu/2.1.0-beta.1/koffi_freebsd_arm64.tar.gz +0 -0
  34. package/build/qemu/2.1.0-beta.1/koffi_freebsd_ia32.tar.gz +0 -0
  35. package/build/qemu/2.1.0-beta.1/koffi_freebsd_x64.tar.gz +0 -0
  36. package/build/qemu/2.1.0-beta.1/koffi_linux_arm32hf.tar.gz +0 -0
  37. package/build/qemu/2.1.0-beta.1/koffi_linux_arm64.tar.gz +0 -0
  38. package/build/qemu/2.1.0-beta.1/koffi_linux_ia32.tar.gz +0 -0
  39. package/build/qemu/2.1.0-beta.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  40. package/build/qemu/2.1.0-beta.1/koffi_linux_x64.tar.gz +0 -0
  41. package/build/qemu/2.1.0-beta.1/koffi_openbsd_ia32.tar.gz +0 -0
  42. package/build/qemu/2.1.0-beta.1/koffi_openbsd_x64.tar.gz +0 -0
  43. package/build/qemu/2.1.0-beta.1/koffi_win32_arm64.tar.gz +0 -0
  44. package/build/qemu/2.1.0-beta.1/koffi_win32_ia32.tar.gz +0 -0
  45. package/build/qemu/2.1.0-beta.1/koffi_win32_x64.tar.gz +0 -0
  46. package/doc/dist/html/.buildinfo +0 -4
  47. package/doc/dist/html/_sources/benchmarks.md.txt +0 -137
  48. package/doc/dist/html/_sources/changes.md.txt +0 -161
  49. package/doc/dist/html/_sources/contribute.md.txt +0 -127
  50. package/doc/dist/html/_sources/functions.md.txt +0 -355
  51. package/doc/dist/html/_sources/index.rst.txt +0 -39
  52. package/doc/dist/html/_sources/memory.md.txt +0 -32
  53. package/doc/dist/html/_sources/platforms.md.txt +0 -31
  54. package/doc/dist/html/_sources/start.md.txt +0 -100
  55. package/doc/dist/html/_sources/types.md.txt +0 -588
  56. package/doc/dist/html/_static/_sphinx_javascript_frameworks_compat.js +0 -134
  57. package/doc/dist/html/_static/basic.css +0 -932
  58. package/doc/dist/html/_static/bench_linux.png +0 -0
  59. package/doc/dist/html/_static/bench_windows.png +0 -0
  60. package/doc/dist/html/_static/custom.css +0 -22
  61. package/doc/dist/html/_static/debug.css +0 -69
  62. package/doc/dist/html/_static/doctools.js +0 -264
  63. package/doc/dist/html/_static/documentation_options.js +0 -14
  64. package/doc/dist/html/_static/file.png +0 -0
  65. package/doc/dist/html/_static/jquery-3.6.0.js +0 -10881
  66. package/doc/dist/html/_static/jquery.js +0 -2
  67. package/doc/dist/html/_static/language_data.js +0 -199
  68. package/doc/dist/html/_static/minus.png +0 -0
  69. package/doc/dist/html/_static/perf_linux_20220623.png +0 -0
  70. package/doc/dist/html/_static/perf_linux_20220623_2.png +0 -0
  71. package/doc/dist/html/_static/perf_windows_20220623.png +0 -0
  72. package/doc/dist/html/_static/perf_windows_20220623_2.png +0 -0
  73. package/doc/dist/html/_static/plus.png +0 -0
  74. package/doc/dist/html/_static/pygments.css +0 -252
  75. package/doc/dist/html/_static/scripts/furo-extensions.js +0 -0
  76. package/doc/dist/html/_static/scripts/furo.js +0 -3
  77. package/doc/dist/html/_static/scripts/furo.js.LICENSE.txt +0 -7
  78. package/doc/dist/html/_static/scripts/furo.js.map +0 -1
  79. package/doc/dist/html/_static/searchtools.js +0 -531
  80. package/doc/dist/html/_static/skeleton.css +0 -296
  81. package/doc/dist/html/_static/styles/furo-extensions.css +0 -2
  82. package/doc/dist/html/_static/styles/furo-extensions.css.map +0 -1
  83. package/doc/dist/html/_static/styles/furo.css +0 -2
  84. package/doc/dist/html/_static/styles/furo.css.map +0 -1
  85. package/doc/dist/html/_static/underscore-1.13.1.js +0 -2042
  86. package/doc/dist/html/_static/underscore.js +0 -6
  87. package/doc/dist/html/benchmarks.html +0 -572
  88. package/doc/dist/html/changes.html +0 -668
  89. package/doc/dist/html/contribute.html +0 -404
  90. package/doc/dist/html/functions.html +0 -657
  91. package/doc/dist/html/genindex.html +0 -254
  92. package/doc/dist/html/index.html +0 -360
  93. package/doc/dist/html/memory.html +0 -347
  94. package/doc/dist/html/objects.inv +0 -0
  95. package/doc/dist/html/platforms.html +0 -372
  96. package/doc/dist/html/search.html +0 -262
  97. package/doc/dist/html/searchindex.js +0 -1
  98. package/doc/dist/html/start.html +0 -385
  99. package/doc/dist/html/types.html +0 -1097
package/CMakeLists.txt CHANGED
@@ -31,7 +31,7 @@ if(MSVC)
31
31
  enable_language(ASM_MASM)
32
32
  endif()
33
33
  else()
34
- add_compile_options(-Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter)
34
+ add_compile_options(-Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wswitch -Werror=switch)
35
35
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
36
36
  add_compile_options(-Wno-unknown-warning-option)
37
37
  endif()
package/ChangeLog.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## History
4
4
 
5
+ ### Koffi 2.1.0 (in beta)
6
+
7
+ **Main changes:**
8
+
9
+ - Add [koffi.as()](functions.md#polymorphic-parameters) to support polymorphic APIs based on `void *` parameters
10
+ - Add [endian-sensitive integer types](types.md#endian-sensitive-types): `intX_le_t`, `intX_be_t`
11
+ - Accept typed arrays for `void *` parameters
12
+ - Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
13
+
14
+ **Other changes:**
15
+
16
+ - Improve global performance with inlining and unity builds
17
+ - Add `size_t` primitive type
18
+ - Support member-specific alignement value in structs
19
+ - Detect impossible parameters and return types (such as non-pointer opaque types)
20
+ - Various documentation fixes and improvements
21
+
5
22
  ### Koffi 2.0.1
6
23
 
7
24
  **Main changes:**
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
- ## C to JS conversion gotchas
108
+ ## Special considerations
109
109
 
110
110
  ### Output parameters
111
111
 
@@ -177,8 +177,72 @@ 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
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.
@@ -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.
@@ -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-{{version}}-brightgreen" alt="NPM"/></a>
2
+ <a href="https://www.npmjs.com/package/koffi"><img src="https://img.shields.io/badge/NPM-Koffi-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 | C type | Bytes | Signedness | Note
8
- ----------------- | ------------------ | ----- | ---------- | ---------------------------
9
- Undefined | 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,6 +56,25 @@ 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
 
72
80
  ### Struct definition
@@ -328,8 +336,8 @@ In C, pointer arguments are used for differenty purposes. It is important to dis
328
336
 
329
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).
330
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.
331
- - **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.
332
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.
333
341
 
334
342
  ### Struct pointers
335
343
 
@@ -357,6 +365,8 @@ console.log(pos);
357
365
 
358
366
  ### Named pointer types
359
367
 
368
+ *New in Koffi 2.0*
369
+
360
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:
361
371
 
362
372
  ```js
@@ -367,111 +377,6 @@ const GetHandleInformation = lib.func('bool __stdcall GetHandleInformation(HANDL
367
377
  const CloseHandle = lib.func('bool __stdcall CloseHandle(HANDLE h)');
368
378
  ```
369
379
 
370
- ### Array pointers
371
-
372
- 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).
373
-
374
- 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.
375
-
376
- 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.
377
-
378
- ```c
379
- // Build with: clang -fPIC -o length.so -shared length.c -Wall -O2
380
-
381
- #include <stdlib.h>
382
- #include <stdint.h>
383
- #include <string.h>
384
-
385
- int64_t ComputeTotalLength(const char **strings)
386
- {
387
- int64_t total = 0;
388
-
389
- for (const char **ptr = strings; *ptr; ptr++) {
390
- const char *str = *ptr;
391
- total += strlen(str);
392
- }
393
-
394
- return total;
395
- }
396
- ```
397
-
398
- ```js
399
- const koffi = require('koffi');
400
- const lib = koffi.load('./length.so');
401
-
402
- const ComputeTotalLength = lib.func('int64_t ComputeTotalLength(const char **strings)');
403
-
404
- let strings = ['Get', 'Total', 'Length', null];
405
- let total = ComputeTotalLength(strings);
406
-
407
- console.log(total); // Prints 14
408
- ```
409
-
410
- 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).
411
-
412
- Here is an example based on the Win32 API, listing files in the current directory with `FindFirstFileW()` and `FindNextFileW()`:
413
-
414
- ```js
415
- const koffi = require('koffi');
416
- const lib = koffi.load('kernel32.dll');
417
-
418
- const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
419
- const FILETIME = koffi.struct('FILETIME', {
420
- dwLowDateTime: 'uint',
421
- dwHighDateTime: 'uint'
422
- });
423
- const WIN32_FIND_DATA = koffi.struct('WIN32_FIND_DATA', {
424
- dwFileAttributes: 'uint',
425
- ftCreationTime: FILETIME,
426
- ftLastAccessTime: FILETIME,
427
- ftLastWriteTime: FILETIME,
428
- nFileSizeHigh: 'uint',
429
- nFileSizeLow: 'uint',
430
- dwReserved0: 'uint',
431
- dwReserved1: 'uint',
432
- cFileName: koffi.array('char16', 260),
433
- cAlternateFileName: koffi.array('char16', 14),
434
- dwFileType: 'uint', // Obsolete. Do not use.
435
- dwCreatorType: 'uint', // Obsolete. Do not use
436
- wFinderFlags: 'ushort' // Obsolete. Do not use
437
- });
438
-
439
- const FindFirstFile = lib.func('HANDLE __stdcall FindFirstFileW(str16 path, _Out_ WIN32_FIND_DATA *data)');
440
- const FindNextFile = lib.func('bool __stdcall FindNextFileW(HANDLE h, _Out_ WIN32_FIND_DATA *data)');
441
- const FindClose = lib.func('bool __stdcall FindClose(HANDLE h)');
442
- const GetLastError = lib.func('uint GetLastError()');
443
-
444
- function list(dirname) {
445
- let filenames = [];
446
-
447
- let data = {};
448
- let h = FindFirstFile(dirname + '\\*', data);
449
-
450
- if (!h) {
451
- if (GetLastError() == 2) // ERROR_FILE_NOT_FOUND
452
- return filenames;
453
- throw new Error('FindFirstFile() failed');
454
- }
455
-
456
- try {
457
- do {
458
- if (data.cFileName != '.' && data.cFileName != '..')
459
- filenames.push(data.cFileName);
460
- } while (FindNextFile(h, data));
461
-
462
- if (GetLastError() != 18) // ERROR_NO_MORE_FILES
463
- throw new Error('FindNextFile() failed');
464
- } finally {
465
- FindClose(h);
466
- }
467
-
468
- return filenames;
469
- }
470
-
471
- let filenames = list('.');
472
- console.log(filenames);
473
- ```
474
-
475
380
  ### Pointers to primitive types
476
381
 
477
382
  In javascript, it is not possible to pass a primitive value by reference to another function. This means that you cannot call a function and expect it to modify the value of one of its number or string parameter.
@@ -509,7 +414,9 @@ AddInt(sum, 6);
509
414
  console.log(sum[0]); // Prints 42
510
415
  ```
511
416
 
512
- ## Fixed-size C arrays
417
+ ## Array types
418
+
419
+ ### Fixed-size C arrays
513
420
 
514
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.
515
422
 
@@ -541,7 +448,7 @@ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array
541
448
  console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
542
449
  ```
543
450
 
544
- ### Handling of strings
451
+ ### Fixed-size string buffers
545
452
 
546
453
  Koffi can also convert JS strings to fixed-sized arrays in the following cases:
547
454
 
@@ -550,6 +457,48 @@ Koffi can also convert JS strings to fixed-sized arrays in the following cases:
550
457
 
551
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')`).
552
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
+
553
502
  ## Disposable types
554
503
 
555
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.
@@ -560,6 +509,8 @@ Read the documentation for [disposable types](functions.md#heap-allocated-values
560
509
 
561
510
  ### Type introspection
562
511
 
512
+ *New in Koffi 2.0: `koffi.resolve()`*
513
+
563
514
  Koffi exposes three functions to explore type information:
564
515
 
565
516
  - `koffi.sizeof(type)` to get the size of a type
@@ -585,4 +536,6 @@ console.log(koffi.sizeof(koffi.types.long));
585
536
 
586
537
  ### Type aliases
587
538
 
539
+ *New in Koffi 2.0*
540
+
588
541
  You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.1.0-beta.1",
3
+ "version": "2.1.0-beta.2",
4
4
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",