koffi 2.0.0 → 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 (127) hide show
  1. package/CMakeLists.txt +2 -9
  2. package/ChangeLog.md +25 -2
  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.2/koffi_darwin_arm64.tar.gz +0 -0
  11. package/build/qemu/2.1.0-beta.2/koffi_darwin_x64.tar.gz +0 -0
  12. package/build/qemu/2.1.0-beta.2/koffi_freebsd_arm64.tar.gz +0 -0
  13. package/build/qemu/2.1.0-beta.2/koffi_freebsd_ia32.tar.gz +0 -0
  14. package/build/qemu/2.1.0-beta.2/koffi_freebsd_x64.tar.gz +0 -0
  15. package/build/qemu/2.1.0-beta.2/koffi_linux_arm32hf.tar.gz +0 -0
  16. package/build/qemu/2.1.0-beta.2/koffi_linux_arm64.tar.gz +0 -0
  17. package/build/qemu/2.1.0-beta.2/koffi_linux_ia32.tar.gz +0 -0
  18. package/build/qemu/2.1.0-beta.2/koffi_linux_riscv64hf64.tar.gz +0 -0
  19. package/build/qemu/2.1.0-beta.2/koffi_linux_x64.tar.gz +0 -0
  20. package/build/qemu/2.1.0-beta.2/koffi_openbsd_ia32.tar.gz +0 -0
  21. package/build/qemu/2.1.0-beta.2/koffi_openbsd_x64.tar.gz +0 -0
  22. package/build/qemu/2.1.0-beta.2/koffi_win32_arm64.tar.gz +0 -0
  23. package/build/qemu/2.1.0-beta.2/koffi_win32_ia32.tar.gz +0 -0
  24. package/build/qemu/2.1.0-beta.2/koffi_win32_x64.tar.gz +0 -0
  25. package/doc/benchmarks.md +2 -2
  26. package/doc/changes.md +23 -19
  27. package/doc/conf.py +14 -1
  28. package/doc/dist/doctrees/benchmarks.doctree +0 -0
  29. package/doc/dist/doctrees/changes.doctree +0 -0
  30. package/doc/dist/doctrees/environment.pickle +0 -0
  31. package/doc/dist/doctrees/functions.doctree +0 -0
  32. package/doc/dist/doctrees/index.doctree +0 -0
  33. package/doc/dist/doctrees/types.doctree +0 -0
  34. package/doc/functions.md +76 -10
  35. package/doc/templates/badges.html +4 -0
  36. package/doc/types.md +157 -161
  37. package/package.json +2 -2
  38. package/qemu/qemu.js +1 -1
  39. package/src/abi_arm32.cc +167 -3
  40. package/src/abi_arm64.cc +196 -3
  41. package/src/abi_riscv64.cc +107 -3
  42. package/src/abi_x64_sysv.cc +114 -4
  43. package/src/abi_x64_win.cc +113 -3
  44. package/src/abi_x86.cc +156 -5
  45. package/src/call.cc +241 -26
  46. package/src/call.hh +15 -3
  47. package/src/ffi.cc +123 -34
  48. package/src/ffi.hh +19 -0
  49. package/src/index.js +4 -2
  50. package/src/parser.cc +3 -5
  51. package/src/util.cc +44 -1
  52. package/src/util.hh +4 -0
  53. package/test/async.js +1 -2
  54. package/test/callbacks.js +2 -3
  55. package/test/misc.c +21 -1
  56. package/test/raylib.js +1 -1
  57. package/test/sqlite.js +7 -7
  58. package/test/sync.js +30 -4
  59. package/build/qemu/2.0.0/koffi_darwin_arm64.tar.gz +0 -0
  60. package/build/qemu/2.0.0/koffi_darwin_x64.tar.gz +0 -0
  61. package/build/qemu/2.0.0/koffi_freebsd_arm64.tar.gz +0 -0
  62. package/build/qemu/2.0.0/koffi_freebsd_ia32.tar.gz +0 -0
  63. package/build/qemu/2.0.0/koffi_freebsd_x64.tar.gz +0 -0
  64. package/build/qemu/2.0.0/koffi_linux_arm32hf.tar.gz +0 -0
  65. package/build/qemu/2.0.0/koffi_linux_arm64.tar.gz +0 -0
  66. package/build/qemu/2.0.0/koffi_linux_ia32.tar.gz +0 -0
  67. package/build/qemu/2.0.0/koffi_linux_riscv64hf64.tar.gz +0 -0
  68. package/build/qemu/2.0.0/koffi_linux_x64.tar.gz +0 -0
  69. package/build/qemu/2.0.0/koffi_openbsd_ia32.tar.gz +0 -0
  70. package/build/qemu/2.0.0/koffi_openbsd_x64.tar.gz +0 -0
  71. package/build/qemu/2.0.0/koffi_win32_arm64.tar.gz +0 -0
  72. package/build/qemu/2.0.0/koffi_win32_ia32.tar.gz +0 -0
  73. package/build/qemu/2.0.0/koffi_win32_x64.tar.gz +0 -0
  74. package/doc/dist/html/.buildinfo +0 -4
  75. package/doc/dist/html/_sources/benchmarks.md.txt +0 -137
  76. package/doc/dist/html/_sources/changes.md.txt +0 -157
  77. package/doc/dist/html/_sources/contribute.md.txt +0 -127
  78. package/doc/dist/html/_sources/functions.md.txt +0 -355
  79. package/doc/dist/html/_sources/index.rst.txt +0 -39
  80. package/doc/dist/html/_sources/memory.md.txt +0 -32
  81. package/doc/dist/html/_sources/platforms.md.txt +0 -31
  82. package/doc/dist/html/_sources/start.md.txt +0 -100
  83. package/doc/dist/html/_sources/types.md.txt +0 -545
  84. package/doc/dist/html/_static/_sphinx_javascript_frameworks_compat.js +0 -134
  85. package/doc/dist/html/_static/basic.css +0 -932
  86. package/doc/dist/html/_static/bench_linux.png +0 -0
  87. package/doc/dist/html/_static/bench_windows.png +0 -0
  88. package/doc/dist/html/_static/custom.css +0 -22
  89. package/doc/dist/html/_static/debug.css +0 -69
  90. package/doc/dist/html/_static/doctools.js +0 -264
  91. package/doc/dist/html/_static/documentation_options.js +0 -14
  92. package/doc/dist/html/_static/file.png +0 -0
  93. package/doc/dist/html/_static/jquery-3.6.0.js +0 -10881
  94. package/doc/dist/html/_static/jquery.js +0 -2
  95. package/doc/dist/html/_static/language_data.js +0 -199
  96. package/doc/dist/html/_static/minus.png +0 -0
  97. package/doc/dist/html/_static/perf_linux_20220623.png +0 -0
  98. package/doc/dist/html/_static/perf_linux_20220623_2.png +0 -0
  99. package/doc/dist/html/_static/perf_windows_20220623.png +0 -0
  100. package/doc/dist/html/_static/perf_windows_20220623_2.png +0 -0
  101. package/doc/dist/html/_static/plus.png +0 -0
  102. package/doc/dist/html/_static/pygments.css +0 -252
  103. package/doc/dist/html/_static/scripts/furo-extensions.js +0 -0
  104. package/doc/dist/html/_static/scripts/furo.js +0 -3
  105. package/doc/dist/html/_static/scripts/furo.js.LICENSE.txt +0 -7
  106. package/doc/dist/html/_static/scripts/furo.js.map +0 -1
  107. package/doc/dist/html/_static/searchtools.js +0 -531
  108. package/doc/dist/html/_static/skeleton.css +0 -296
  109. package/doc/dist/html/_static/styles/furo-extensions.css +0 -2
  110. package/doc/dist/html/_static/styles/furo-extensions.css.map +0 -1
  111. package/doc/dist/html/_static/styles/furo.css +0 -2
  112. package/doc/dist/html/_static/styles/furo.css.map +0 -1
  113. package/doc/dist/html/_static/underscore-1.13.1.js +0 -2042
  114. package/doc/dist/html/_static/underscore.js +0 -6
  115. package/doc/dist/html/benchmarks.html +0 -568
  116. package/doc/dist/html/changes.html +0 -653
  117. package/doc/dist/html/contribute.html +0 -400
  118. package/doc/dist/html/functions.html +0 -653
  119. package/doc/dist/html/genindex.html +0 -250
  120. package/doc/dist/html/index.html +0 -356
  121. package/doc/dist/html/memory.html +0 -343
  122. package/doc/dist/html/objects.inv +0 -0
  123. package/doc/dist/html/platforms.html +0 -368
  124. package/doc/dist/html/search.html +0 -258
  125. package/doc/dist/html/searchindex.js +0 -1
  126. package/doc/dist/html/start.html +0 -381
  127. package/doc/dist/html/types.html +0 -1044
package/test/sync.js CHANGED
@@ -15,7 +15,6 @@
15
15
 
16
16
  const koffi = require('./build/koffi.node');
17
17
  const assert = require('assert');
18
- const path = require('path');
19
18
 
20
19
  const Pack1 = koffi.struct('Pack1', {
21
20
  a: 'int'
@@ -62,7 +61,7 @@ const IntFloat = koffi.struct('IntFloat', {
62
61
 
63
62
  const BFG = koffi.struct('BFG', {
64
63
  a: 'int8_t',
65
- b: 'int64_t',
64
+ b: [16, 'int64_t'],
66
65
  c: 'char',
67
66
  d: 'const char *',
68
67
  e: 'short',
@@ -128,7 +127,7 @@ async function main() {
128
127
  }
129
128
 
130
129
  async function test() {
131
- let lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
130
+ let lib_filename = __dirname + '/build/misc' + koffi.extension;
132
131
  let lib = koffi.load(lib_filename);
133
132
 
134
133
  const GetMinusOne1 = lib.func('int8_t GetMinusOne1(void)');
@@ -160,6 +159,7 @@ async function test() {
160
159
  const ConcatenateToStr8 = lib.func('ConcatenateToStr8', 'str', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
161
160
  const MakeBFG = lib.func('BFG __stdcall MakeBFG(_Out_ BFG *p, int x, double y, const char *str)');
162
161
  const MakePackedBFG = lib.func('AliasBFG __fastcall MakePackedBFG(int x, double y, _Out_ PackedBFG *p, const char *str)');
162
+ const MakePolymorphBFG = lib.func('void MakePolymorphBFG(int type, int x, double y, const char *str, _Out_ void *p)');
163
163
  const ReturnBigString = process.platform == 'win32' ?
164
164
  lib.stdcall(1, koffi.disposable('str', koffi.free), ['str']) :
165
165
  lib.func('const char * __stdcall ReturnBigString(const char *str)');
@@ -186,6 +186,7 @@ async function test() {
186
186
  const MultiplyIntegers = lib.func('void MultiplyIntegers(int multiplier, _Inout_ int *values, int len)');
187
187
  const ThroughStr = lib.func('str ThroughStr(StrStruct s)');
188
188
  const ThroughStr16 = lib.func('str16 ThroughStr16(StrStruct s)');
189
+ const ReverseBytes = lib.func('void ReverseBytes(_Inout_ void *array, int len)');
189
190
 
190
191
  // Simple signed value returns
191
192
  assert.equal(GetMinusOne1(), -1);
@@ -295,6 +296,17 @@ async function test() {
295
296
  assert.deepEqual(out, bfg);
296
297
  }
297
298
 
299
+ // Polymorph pointer
300
+ {
301
+ let bfg = {};
302
+
303
+ MakePolymorphBFG(0, 2, 7, 'boo', koffi.as(bfg, 'BFG *'));
304
+ assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/boo/X', e: 54, inner: { f: 14, g: 5 }});
305
+
306
+ MakePolymorphBFG(1, 2, 7, 'bies', koffi.as(bfg, 'PackedBFG *'));
307
+ assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/bies/X', e: 54, inner: { f: 14, g: 5 }});
308
+ }
309
+
298
310
  // Big string
299
311
  {
300
312
  let str = 'fooBAR!'.repeat(1024 * 1024);
@@ -372,10 +384,12 @@ async function test() {
372
384
  let out1 = Array(10);
373
385
  let out2 = new Int32Array(10);
374
386
  let mult = -3;
387
+ let ret = null;
375
388
 
376
389
  FillRange(2, 7, out1, out1.length);
377
- FillRange(13, 3, out2, out2.length);
390
+ ret = FillRange(13, 3, out2, out2.length);
378
391
 
392
+ assert.strictEqual(ret, undefined);
379
393
  assert.deepEqual(out1, [2, 9, 16, 23, 30, 37, 44, 51, 58, 65]);
380
394
  assert.deepEqual(out2, new Int32Array([13, 16, 19, 22, 25, 28, 31, 34, 37, 40]));
381
395
 
@@ -392,4 +406,16 @@ async function test() {
392
406
  assert.equal(ThroughStr16({ str: null, str16: 'World!' }), 'World!');
393
407
  assert.equal(ThroughStr16({ str: 'World!', str16: null }), null);
394
408
  }
409
+
410
+ // Transparent typed arrays for void pointers
411
+ {
412
+ let arr8 = Uint8Array.from([1, 2, 3, 4, 5]);
413
+ let arr16 = Int16Array.from([1, 2, 3, 4, 5]);
414
+
415
+ ReverseBytes(arr8, arr8.byteLength);
416
+ assert.deepEqual(arr8, Uint8Array.from([5, 4, 3, 2, 1]));
417
+
418
+ ReverseBytes(arr16, arr16.byteLength);
419
+ assert.deepEqual(arr16, Int16Array.from([1280, 1024, 768, 512, 256]));
420
+ }
395
421
  }
@@ -1,4 +0,0 @@
1
- # Sphinx build info version 1
2
- # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3
- config: 3958eced2fda3c6c0751ec1a88d416ca
4
- tags: 645f666f9bcd5a90fca523b33c5a78b7
@@ -1,137 +0,0 @@
1
- # Benchmarks
2
-
3
- ## Overview
4
-
5
- Here is a quick overview of the execution time of Koffi calls on three benchmarks, where it is compared to a theoretical ideal FFI implementation (approximated with pre-compiled static N-API glue code):
6
-
7
- - The first benchmark is based on `rand()` calls
8
- - The second benchmark is based on `atoi()` calls
9
- - The third benchmark is based on [Raylib](https://www.raylib.com/)
10
-
11
- <table style="margin: 0 auto;">
12
- <tr>
13
- <td><a href="static/perf_linux_20220628.png" target="_blank"><img src="static/perf_linux_20220628.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
14
- <td><a href="static/perf_windows_20220628.png" target="_blank"><img src="static/perf_windows_20220628.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
15
- </tr>
16
- </table>
17
-
18
- These results are detailed and explained below, and compared to node-ffi/node-ffi-napi.
19
-
20
- ## Linux x86_64
21
-
22
- The results presented below were measured on my x86_64 Linux machine (Intel® Core™ i5-4460).
23
-
24
- ### rand results
25
-
26
- This test is based around repeated calls to a simple standard C function atoi, and has three implementations:
27
-
28
- - the first one is the reference, it calls atoi through an N-API module, and is close to the theoretical limit of a perfect (no overhead) Node.js > C FFI implementation (pre-compiled static glue code)
29
- - the second one calls atoi through Koffi
30
- - the third one uses the official Node.js FFI implementation, node-ffi-napi
31
-
32
- Benchmark | Iteration time | Relative performance | Overhead
33
- ------------- | -------------- | -------------------- | --------
34
- rand_napi | 644 ns | x1.00 | (ref)
35
- rand_koffi | 950 ns | x0.68 | +48%
36
- rand_node_ffi | 30350 ns | x0.02 | +4613%
37
-
38
- Because rand is a pretty small function, the FFI overhead is clearly visible.
39
-
40
- ### atoi results
41
-
42
- This test is similar to the rand one, but it is based on atoi, which takes a string parameter. Javascript (V8) to C string conversion is relatively slow and heavy.
43
-
44
- Benchmark | Iteration time | Relative performance | Overhead
45
- ------------- | -------------- | -------------------- | --------
46
- atoi_napi | 1104 ns | x1.00 | (ref)
47
- atoi_koffi | 1778 ns | x0.62 | +61%
48
- atoi_node_ffi | 125300 ns | x0.009 | +11250%
49
-
50
- Because atoi is a pretty small function, the FFI overhead is clearly visible.
51
-
52
- ### Raylib results
53
-
54
- This benchmark uses the CPU-based image drawing functions in Raylib. The calls are much heavier than in the atoi benchmark, thus the FFI overhead is reduced. In this implementation, Koffi is compared to:
55
-
56
- - Baseline: Full C++ version of the code (no JS)
57
- - [node-raylib](https://github.com/RobLoach/node-raylib): This is a native wrapper implemented with N-API
58
-
59
- Benchmark | Iteration time | Relative performance | Overhead
60
- ------------------ | -------------- | -------------------- | --------
61
- raylib_cc | 215.7 µs | x1.20 | -17%
62
- raylib_node_raylib | 258.9 µs | x1.00 | (ref)
63
- raylib_koffi | 311.6 µs | x0.83 | +20%
64
- raylib_node_ffi | 928.4 µs | x0.28 | +259%
65
-
66
- ## Windows x86_64
67
-
68
- The results presented below were measured on my x86_64 Windows machine (Intel® Core™ i5-4460).
69
-
70
- ### rand results
71
-
72
- This test is based around repeated calls to a simple standard C function atoi, and has three implementations:
73
-
74
- - the first one is the reference, it calls atoi through an N-API module, and is close to the theoretical limit of a perfect (no overhead) Node.js > C FFI implementation (pre-compiled static glue code)
75
- - the second one calls atoi through Koffi
76
- - the third one uses the official Node.js FFI implementation, node-ffi-napi
77
-
78
- Benchmark | Iteration time | Relative performance | Overhead
79
- ------------- | -------------- | -------------------- | --------
80
- rand_napi | 965 ns | x1.00 | (ref)
81
- rand_koffi | 1248 ns | x0.77 | +29%
82
- rand_node_ffi | 41500 ns | x0.02 | +4203%
83
-
84
- Because rand is a pretty small function, the FFI overhead is clearly visible.
85
-
86
- ### atoi results
87
-
88
- This test is similar to the rand one, but it is based on atoi, which takes a string parameter. Javascript (V8) to C string conversion is relatively slow and heavy.
89
-
90
- The results below were measured on my x86_64 Windows machine (Intel® Core™ i5-4460):
91
-
92
- Benchmark | Iteration time | Relative performance | Overhead
93
- ------------- | -------------- | -------------------- | --------
94
- atoi_napi | 1393 ns | x1.00 | (ref)
95
- atoi_koffi | 2246 ns | x0.62 | +61%
96
- atoi_node_ffi | 157550 ns | x0.009 | +11210%
97
-
98
- Because atoi is a pretty small function, the FFI overhead is clearly visible.
99
-
100
- ### Raylib results
101
-
102
- This benchmark uses the CPU-based image drawing functions in Raylib. The calls are much heavier than in the atoi benchmark, thus the FFI overhead is reduced. In this implementation, Koffi is compared to:
103
-
104
- - [node-raylib](https://github.com/RobLoach/node-raylib) (baseline): This is a native wrapper implemented with N-API
105
- - raylib_cc: C++ implementation of the benchmark, without any Javascript
106
-
107
- Benchmark | Iteration time | Relative performance | Overhead
108
- ------------------ | -------------- | -------------------- | --------
109
- raylib_cc | 211.8 µs | x1.25 | -20%
110
- raylib_node_raylib | 264.4 µs | x1.00 | (ref)
111
- raylib_koffi | 318.9 µs | x0.83 | +21%
112
- raylib_node_ffi | 1146.2 µs | x0.23 | +334%
113
-
114
- Please note that in order to get fair numbers for raylib_node_raylib, it was recompiled with clang-cl before running the benchmark with the following commands:
115
-
116
- ```batch
117
- cd node_modules\raylib
118
- rmdir /S /Q bin build
119
- npx cmake-js compile -t ClangCL
120
- ```
121
-
122
- ## Running benchmarks
123
-
124
- Open a console, go to `koffi/benchmark` and run `../../cnoke/cnoke.js` (or `node ..\..\cnoke\cnoke.js` on Windows) before doing anything else.
125
-
126
- Please note that all benchmark results are made with Clang-built binaries.
127
-
128
- ```sh
129
- cd koffi/benchmark
130
- node ../../cnoke/cnoke.js --prefer-clang
131
- ```
132
-
133
- Once everything is built and ready, run:
134
-
135
- ```sh
136
- node benchmark.js
137
- ```
@@ -1,157 +0,0 @@
1
- ```{include} ../ChangeLog.md
2
- ```
3
-
4
- ## Migration guide
5
-
6
- ### Koffi 1.x to 2.x
7
-
8
- The API was changed in 2.x in a few ways, in order to reduce some excessively "magic" behavior and reduce the syntax differences between C and the C-like prototypes.
9
-
10
- You may need to change your code if you use:
11
-
12
- - Callback functions
13
- - Opaque handles
14
- - `koffi.introspect()`
15
-
16
- #### Callback types
17
-
18
- In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer. Now, you must use them through a pointer: `void CallIt(CallbackType func)` in Koffi 1.x becomes `void CallIt(CallbackType *func)` in version 2.0 and newer.
19
-
20
- Given the following C code:
21
-
22
- ```c
23
- #include <string.h>
24
-
25
- int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
26
- {
27
- char buf[64];
28
- snprintf(buf, sizeof(buf), "Hello %s!", str);
29
- return cb(buf, age);
30
- }
31
- ```
32
-
33
- The two versions below illustrate the API difference between Koffi 1.x and Koffi 2.x:
34
-
35
- ```js
36
- // Koffi 1.x
37
-
38
- const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
39
-
40
- const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', TransferCallback]);
41
- // Equivalent to: const TransferToJS = lib.func('int TransferToJS(str s, int x, TransferCallback cb)');
42
-
43
- let ret = TransferToJS('Niels', 27, (str, age) => {
44
- console.log(str);
45
- console.log('Your age is:', age);
46
- return 42;
47
- });
48
- console.log(ret);
49
- ```
50
-
51
- ```js
52
- // Koffi 2.x
53
-
54
- const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
55
-
56
- const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
57
- // Equivalent to: const TransferToJS = lib.func('int TransferToJS(str s, int x, TransferCallback *cb)');
58
-
59
- let ret = TransferToJS('Niels', 27, (str, age) => {
60
- console.log(str);
61
- console.log('Your age is:', age);
62
- return 42;
63
- });
64
- console.log(ret);
65
- ```
66
-
67
- Koffi 1.x only supported [transient callbacks](functions.md#javascript-callbacks), you must use Koffi 2.x for registered callbacks.
68
-
69
- #### Opaque handles
70
-
71
- 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. Now, you must use them through a pointer, and use an array for output parameters.
72
-
73
- For functions that return handles or pass them by parameter:
74
-
75
- ```js
76
- // Koffi 1.x
77
-
78
- const FILE = koffi.handle('FILE');
79
- const fopen = lib.func('FILE fopen(const char *path, const char *mode)');
80
- const fclose = lib.func('int fclose(FILE stream)');
81
-
82
- let fp = fopen('touch', 'wb');
83
- if (!fp)
84
- throw new Error('Failed to open file');
85
- fclose(fp);
86
- ```
87
-
88
- ```js
89
- // Koffi 2.x
90
-
91
- const FILE = koffi.handle('FILE');
92
- const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
93
- const fclose = lib.func('int fclose(FILE *stream)');
94
-
95
- let fp = fopen('touch', 'wb');
96
- if (!fp)
97
- throw new Error('Failed to open file');
98
- fclose(fp);
99
- ```
100
-
101
- For functions that set opaque handles through output parameters (such as `sqlite3_open_v2`), you must now use a single element array as shown below:
102
-
103
- ```js
104
- // Koffi 1.x
105
-
106
- const sqlite3_db = koffi.handle('sqlite3_db');
107
-
108
- const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(sqlite3_db), 'int', 'str']);
109
- const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
110
-
111
- const SQLITE_OPEN_READWRITE = 0x2;
112
- const SQLITE_OPEN_CREATE = 0x4;
113
-
114
- let db = {};
115
-
116
- if (sqlite3_open_v2(':memory:', db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
117
- throw new Error('Failed to open database');
118
-
119
- sqlite3_close_v2(db);
120
- ```
121
-
122
- ```js
123
- // Koffi 2.x
124
-
125
- const sqlite3_db = koffi.handle('sqlite3_db');
126
-
127
- const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db, 2)), 'int', 'str']);
128
- const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3_db)]);
129
-
130
- const SQLITE_OPEN_READWRITE = 0x2;
131
- const SQLITE_OPEN_CREATE = 0x4;
132
-
133
- let db = null;
134
-
135
- let ptr = [null];
136
- if (sqlite3_open_v2(':memory:', ptr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
137
- throw new Error('Failed to open database');
138
- db = ptr[0];
139
-
140
- sqlite3_close_v2(db);
141
- ```
142
-
143
- #### koffi.introspect()
144
-
145
- In Koffi 1.x, `koffi.introspect()` would only work with struct types, and return the object passed to `koffi.struct()` to initialize the type. Now this function works with all types.
146
-
147
- You can still get the list of struct members:
148
-
149
- ```js
150
- const StructType = koffi.struct('StructType', { dummy: 'int' });
151
-
152
- // Koffi 1.x
153
- let members = koffi.introspect(StructType);
154
-
155
- // Koffi 2.x
156
- let members = koffi.introspect(StructType).members;
157
- ```
@@ -1,127 +0,0 @@
1
- # Contributing
2
-
3
- ## Bugs and feature requests
4
-
5
- Use the official repository (named Luigi, because this is a monorepo containing multiple projects) for bugs, ideas and features requests.
6
-
7
- Go here: https://github.com/Koromix/luigi/
8
-
9
- ## Build from source
10
-
11
- We provide prebuilt binaries, packaged in the NPM archive, so in most cases it should be as simple as `npm install koffi`. If you want to hack Koffi or use a specific platform, follow the instructions below.
12
-
13
- Start by cloning the repository with [Git](https://git-scm.com/):
14
-
15
- ```sh
16
- git clone https://github.com/Koromix/luigi
17
- cd luigi/koffi
18
- ```
19
-
20
- As said before, this is a monorepository containg multiple projects, hence the name.
21
-
22
- ### Windows
23
-
24
- First, make sure the following dependencies are met:
25
-
26
- - The "Desktop development with C++" workload from [Visual Studio 2022 or 2019](https://visualstudio.microsoft.com/downloads/) or the "C++ build tools" workload from the [Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022), with the default optional components.
27
- - [CMake meta build system](https://cmake.org/)
28
- - [Node.js](https://nodejs.org/) 12 or later
29
-
30
- Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
31
-
32
- ```sh
33
- cd koffi/test # or cd koffi/benchmark
34
- node ../../cnoke/cnoke.js
35
- ```
36
-
37
- ### Other platforms
38
-
39
- Make sure the following dependencies are met:
40
-
41
- - `gcc` and `g++` >= 8.3 or newer
42
- - GNU Make 3.81 or newer
43
- - [CMake meta build system](https://cmake.org/)
44
- - [Node.js](https://nodejs.org/) 12 or later
45
-
46
- Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
47
-
48
- ```sh
49
- cd koffi/test # or cd koffi/benchmark
50
- node ../../cnoke/cnoke.js
51
- ```
52
-
53
- ## Running tests
54
-
55
- Koffi is tested on multiple architectures using emulated (accelerated when possible) QEMU machines. First, you need to install qemu packages, such as `qemu-system` (or even `qemu-system-gui`) on Ubuntu.
56
-
57
- These machines are not included directly in this repository (for license and size reasons), but they are available here: https://koromix.dev/files/machines/
58
-
59
- For example, if you want to run the tests on Debian ARM64, run the following commands:
60
-
61
- ```sh
62
- cd luigi/koffi/qemu/
63
- wget -q -O- https://koromix.dev/files/machines/qemu_debian_arm64.tar.zst | zstd -d | tar xv
64
- sha256sum -c --ignore-missing registry/sha256sum.txt
65
- ```
66
-
67
- Note that the machine disk content may change each time the machine runs, so the checksum test will fail once a machine has been used at least once.
68
-
69
- And now you can run the tests with:
70
-
71
- ```sh
72
- node qemu.js test # Several options are available, use --help
73
- ```
74
-
75
- And be patient, this can be pretty slow for emulated machines. The Linux machines have and use ccache to build Koffi, so subsequent build steps will get much more tolerable.
76
-
77
- By default, machines are started and stopped for each test. But you can start the machines ahead of time and run the tests multiple times instead:
78
-
79
- ```sh
80
- node qemu.js start # Start the machines
81
- node qemu.js test # Test (without shutting down)
82
- node qemu.js test # Test again
83
- node qemu.js stop # Stop everything
84
- ```
85
-
86
- You can also restrict the test to a subset of machines:
87
-
88
- ```sh
89
- # Full test cycle
90
- node qemu.js test debian_x64 debian_i386
91
-
92
- # Separate start, test, shutdown
93
- node qemu.js start debian_x64 debian_i386
94
- node qemu.js test debian_x64 debian_i386
95
- node qemu.js stop
96
- ```
97
-
98
- Finally, you can join a running machine with SSH with the following shortcut, if you need to do some debugging or any other manual procedure:
99
-
100
- ```sh
101
- node qemu.js ssh debian_i386
102
- ```
103
-
104
- Each machine is configured to run a VNC server available locally, which you can use to access the display, using KRDC or any other compatible viewer. Use the `info` command to get the VNC port.
105
-
106
- ```sh
107
- node qemu.js info debian_x64
108
- ```
109
-
110
- ## Todo list
111
-
112
- The following features and improvements are planned, not necessarily in that order:
113
-
114
- - Optimize passing of structs and arrays (avoid setting named properties one by one? separate HFA-specific helper functions?)
115
- - Automate Windows/AArch64 (qemu) and macOS/AArch64 (how? ... thanks Apple) tests
116
- - Create a real-world example, using several libraries (Raylib, SQLite, libsodium) to illustrate various C API styles
117
- - Add simple struct type parser
118
- - Add more ways to manually encode and decode various types to and from byte arrays
119
- - Add support for unions
120
- - Port Koffi to PowerPC (POWER9+) ABI
121
- - Fix assembly unwind and CFI directives for better debugging experience
122
-
123
- ## Code style
124
-
125
- Koffi is programmed in a mix of C++ and assembly code (architecture-specific code). It uses [node-addon-api](https://github.com/nodejs/node-addon-api) (C++ N-API wrapper) to interact with Node.js.
126
-
127
- My personal preference goes to a rather C-like C++ style, with careful use of templates (mainly for containers) and little object-oriented programming. I strongly prefer tagged unions and code locality over inheritance and virtual methods. Exceptions are disabled.