koffi 2.1.0-beta.2 → 2.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.md +2 -2
- package/build/qemu/2.1.0-beta.3/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.3/koffi_win32_x64.tar.gz +0 -0
- package/doc/Makefile +1 -1
- package/doc/conf.py +5 -0
- package/doc/dist/doctrees/changes.doctree +0 -0
- package/doc/dist/doctrees/environment.pickle +0 -0
- package/doc/dist/doctrees/functions.doctree +0 -0
- package/doc/dist/doctrees/index.doctree +0 -0
- package/doc/dist/doctrees/types.doctree +0 -0
- package/doc/dist/html/.buildinfo +4 -0
- package/doc/dist/html/_sources/benchmarks.md.txt +137 -0
- package/doc/dist/html/_sources/changes.md.txt +161 -0
- package/doc/dist/html/_sources/contribute.md.txt +127 -0
- package/doc/dist/html/_sources/functions.md.txt +421 -0
- package/doc/dist/html/_sources/index.rst.txt +39 -0
- package/doc/dist/html/_sources/memory.md.txt +32 -0
- package/doc/dist/html/_sources/platforms.md.txt +31 -0
- package/doc/dist/html/_sources/start.md.txt +100 -0
- package/doc/dist/html/_sources/types.md.txt +541 -0
- package/doc/dist/html/_static/_sphinx_javascript_frameworks_compat.js +134 -0
- package/doc/dist/html/_static/basic.css +932 -0
- package/doc/dist/html/_static/bench_linux.png +0 -0
- package/doc/dist/html/_static/bench_windows.png +0 -0
- package/doc/dist/html/_static/custom.css +22 -0
- package/doc/dist/html/_static/debug.css +69 -0
- package/doc/dist/html/_static/doctools.js +264 -0
- package/doc/dist/html/_static/documentation_options.js +14 -0
- package/doc/dist/html/_static/file.png +0 -0
- package/doc/dist/html/_static/jquery-3.6.0.js +10881 -0
- package/doc/dist/html/_static/jquery.js +2 -0
- package/doc/dist/html/_static/language_data.js +199 -0
- package/doc/dist/html/_static/minus.png +0 -0
- package/doc/dist/html/_static/perf_linux_20220623.png +0 -0
- package/doc/dist/html/_static/perf_linux_20220623_2.png +0 -0
- package/doc/dist/html/_static/perf_windows_20220623.png +0 -0
- package/doc/dist/html/_static/perf_windows_20220623_2.png +0 -0
- package/doc/dist/html/_static/plus.png +0 -0
- package/doc/dist/html/_static/pygments.css +252 -0
- package/doc/dist/html/_static/scripts/furo-extensions.js +0 -0
- package/doc/dist/html/_static/scripts/furo.js +3 -0
- package/doc/dist/html/_static/scripts/furo.js.LICENSE.txt +7 -0
- package/doc/dist/html/_static/scripts/furo.js.map +1 -0
- package/doc/dist/html/_static/searchtools.js +531 -0
- package/doc/dist/html/_static/skeleton.css +296 -0
- package/doc/dist/html/_static/styles/furo-extensions.css +2 -0
- package/doc/dist/html/_static/styles/furo-extensions.css.map +1 -0
- package/doc/dist/html/_static/styles/furo.css +2 -0
- package/doc/dist/html/_static/styles/furo.css.map +1 -0
- package/doc/dist/html/_static/underscore-1.13.1.js +2042 -0
- package/doc/dist/html/_static/underscore.js +6 -0
- package/doc/dist/html/benchmarks.html +571 -0
- package/doc/dist/html/changes.html +686 -0
- package/doc/dist/html/contribute.html +403 -0
- package/doc/dist/html/functions.html +718 -0
- package/doc/dist/html/genindex.html +253 -0
- package/doc/dist/html/index.html +359 -0
- package/doc/dist/html/memory.html +346 -0
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +371 -0
- package/doc/dist/html/search.html +261 -0
- package/doc/dist/html/searchindex.js +1 -0
- package/doc/dist/html/start.html +384 -0
- package/doc/dist/html/types.html +1061 -0
- package/doc/make.bat +1 -1
- package/doc/templates/badges.html +1 -1
- package/doc/types.md +2 -0
- package/package.json +2 -1
- package/src/abi_arm32.cc +142 -200
- package/src/abi_arm64.cc +113 -122
- package/src/abi_riscv64.cc +76 -92
- package/src/abi_x64_sysv.cc +76 -92
- package/src/abi_x64_win.cc +76 -92
- package/src/abi_x86.cc +128 -164
- package/test/misc.c +43 -0
- package/test/sync.js +81 -0
- package/build/qemu/2.1.0-beta.2/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.2/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
- Improve global performance with inlining and unity builds
|
|
17
17
|
- Add `size_t` primitive type
|
|
18
|
-
- Support member-specific alignement
|
|
19
|
-
- Detect impossible
|
|
18
|
+
- Support member-specific alignement values in structs
|
|
19
|
+
- Detect impossible parameter and return types (such as non-pointer opaque types)
|
|
20
20
|
- Various documentation fixes and improvements
|
|
21
21
|
|
|
22
22
|
### Koffi 2.0.1
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/doc/Makefile
CHANGED
package/doc/conf.py
CHANGED
|
@@ -12,6 +12,7 @@ with open(os.path.dirname(__file__) + '/../package.json') as f:
|
|
|
12
12
|
|
|
13
13
|
version = config['version']
|
|
14
14
|
revision = config['version']
|
|
15
|
+
stable = config['stable']
|
|
15
16
|
|
|
16
17
|
# -- General configuration ---------------------------------------------------
|
|
17
18
|
|
|
@@ -61,6 +62,10 @@ html_sidebars = {
|
|
|
61
62
|
]
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
html_context = {
|
|
66
|
+
"stable": stable
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
# -- MyST parser options -------------------------------------------------
|
|
65
70
|
|
|
66
71
|
myst_enable_extensions = [
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
```
|
|
@@ -0,0 +1,161 @@
|
|
|
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 types
|
|
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 types
|
|
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, in Koffi 2.0, you must use them through a pointer, and use an array for output parameters.
|
|
72
|
+
|
|
73
|
+
In addition to that, `koffi.handle()` has been deprecated in Koffi 2.1 and replaced with `koffi.opaque()`. They work the same but new code should use `koffi.opaque()`, the former one will eventually be removed in Koffi 3.0.
|
|
74
|
+
|
|
75
|
+
For functions that return opaque pointers or pass them by parameter:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// Koffi 1.x
|
|
79
|
+
|
|
80
|
+
const FILE = koffi.handle('FILE');
|
|
81
|
+
const fopen = lib.func('fopen', 'FILE', ['str', 'str']);
|
|
82
|
+
const fopen = lib.func('fclose', 'int', ['FILE']);
|
|
83
|
+
|
|
84
|
+
let fp = fopen('EMPTY', 'wb');
|
|
85
|
+
if (!fp)
|
|
86
|
+
throw new Error('Failed to open file');
|
|
87
|
+
fclose(fp);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// Koffi 2.1
|
|
92
|
+
|
|
93
|
+
// If you use Koffi 2.0: const FILE = koffi.handle('FILE');
|
|
94
|
+
const FILE = koffi.opaque('FILE');
|
|
95
|
+
const fopen = lib.func('fopen', 'FILE *', ['str', 'str']);
|
|
96
|
+
const fopen = lib.func('fclose', 'int', ['FILE *']);
|
|
97
|
+
|
|
98
|
+
let fp = fopen('EMPTY', 'wb');
|
|
99
|
+
if (!fp)
|
|
100
|
+
throw new Error('Failed to open file');
|
|
101
|
+
fclose(fp);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
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:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
// Koffi 1.x
|
|
108
|
+
|
|
109
|
+
const sqlite3 = koffi.handle('sqlite3');
|
|
110
|
+
|
|
111
|
+
const sqlite3_open_v2 = lib.func('int sqlite3_open_v2(const char *, _Out_ sqlite3 *db, int, const char *)');
|
|
112
|
+
const sqlite3_close_v2 = lib.func('int sqlite3_close_v2(sqlite3 db)');
|
|
113
|
+
|
|
114
|
+
const SQLITE_OPEN_READWRITE = 0x2;
|
|
115
|
+
const SQLITE_OPEN_CREATE = 0x4;
|
|
116
|
+
|
|
117
|
+
let db = {};
|
|
118
|
+
|
|
119
|
+
if (sqlite3_open_v2(':memory:', db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
120
|
+
throw new Error('Failed to open database');
|
|
121
|
+
|
|
122
|
+
sqlite3_close_v2(db);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
// Koffi 2.1
|
|
127
|
+
|
|
128
|
+
// If you use Koffi 2.0: const sqlite3 = koffi.handle('sqlite3');
|
|
129
|
+
const sqlite3 = koffi.opaque('sqlite3');
|
|
130
|
+
|
|
131
|
+
const sqlite3_open_v2 = lib.func('int sqlite3_open_v2(const char *, _Out_ sqlite3 **db, int, const char *)');
|
|
132
|
+
const sqlite3_close_v2 = lib.func('int sqlite3_close_v2(sqlite3 *db)');
|
|
133
|
+
|
|
134
|
+
const SQLITE_OPEN_READWRITE = 0x2;
|
|
135
|
+
const SQLITE_OPEN_CREATE = 0x4;
|
|
136
|
+
|
|
137
|
+
let db = null;
|
|
138
|
+
|
|
139
|
+
let ptr = [null];
|
|
140
|
+
if (sqlite3_open_v2(':memory:', ptr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
141
|
+
throw new Error('Failed to open database');
|
|
142
|
+
db = ptr[0];
|
|
143
|
+
|
|
144
|
+
sqlite3_close_v2(db);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### koffi.introspect()
|
|
148
|
+
|
|
149
|
+
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.
|
|
150
|
+
|
|
151
|
+
You can still get the list of struct members:
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
const StructType = koffi.struct('StructType', { dummy: 'int' });
|
|
155
|
+
|
|
156
|
+
// Koffi 1.x
|
|
157
|
+
let members = koffi.introspect(StructType);
|
|
158
|
+
|
|
159
|
+
// Koffi 2.x
|
|
160
|
+
let members = koffi.introspect(StructType).members;
|
|
161
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
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.
|