koffi 3.0.0-alpha.8 → 3.0.0-rc.1
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 +26 -4
- package/LICENSE.txt +1 -1
- package/README.md +0 -2
- package/{build.cjs → cnoke.cjs} +46 -46
- package/doc/benchmarks.md +17 -65
- package/doc/callbacks.md +8 -8
- package/doc/functions.md +6 -6
- package/doc/index.md +4 -6
- package/doc/input.md +6 -6
- package/doc/misc.md +2 -2
- package/doc/output.md +10 -10
- package/doc/pointers.md +12 -12
- package/doc/start.md +4 -4
- package/doc/unions.md +2 -2
- package/index.d.ts +124 -94
- package/lib/native/base/base.cc +15 -10
- package/lib/native/base/base.hh +1 -1
- package/lib/native/base/unicode.inc +1 -1
- package/package.json +19 -3
- package/src/koffi/CMakeLists.txt +6 -12
- package/src/koffi/index.cjs +91 -89
- package/src/koffi/index.js +75 -76
- package/src/koffi/indirect.cjs +91 -89
- package/src/koffi/indirect.js +19 -6
- package/src/koffi/src/abi/arm64.cc +57 -151
- package/src/koffi/src/abi/arm64_asm.S +1 -1
- package/src/koffi/src/abi/arm64_asm.asm +1 -1
- package/src/koffi/src/abi/loong64.cc +1 -1
- package/src/koffi/src/abi/loong64_asm.S +51 -51
- package/src/koffi/src/abi/riscv64.cc +1009 -567
- package/src/koffi/src/abi/riscv64_asm.S +51 -51
- package/src/koffi/src/abi/x64sysv.cc +104 -108
- package/src/koffi/src/abi/x64sysv_asm.S +1 -5
- package/src/koffi/src/abi/x64win.cc +62 -83
- package/src/koffi/src/abi/x64win_asm.S +1 -1
- package/src/koffi/src/abi/x64win_asm.asm +1 -1
- package/src/koffi/src/abi/x86.cc +60 -100
- package/src/koffi/src/abi/x86_asm.S +3 -3
- package/src/koffi/src/abi/x86_asm.asm +1 -1
- package/src/koffi/src/call.cc +763 -93
- package/src/koffi/src/call.hh +27 -2
- package/src/koffi/src/errno.inc +1 -1
- package/src/koffi/src/ffi.cc +386 -653
- package/src/koffi/src/ffi.hh +80 -45
- package/src/koffi/src/parser.cc +1 -1
- package/src/koffi/src/parser.hh +1 -1
- package/src/koffi/src/primitives.inc +1 -1
- package/src/koffi/src/trampolines.cjs +3 -3
- package/src/koffi/src/util.cc +185 -68
- package/src/koffi/src/util.hh +88 -74
- package/src/koffi/src/uv.cc +36 -19
- package/src/koffi/src/uv.def +1 -3
- package/src/koffi/src/uv.hh +27 -14
- package/src/koffi/src/win32.cc +1 -1
- package/src/koffi/src/win32.hh +1 -1
- package/vendor/node-addon-api/napi-inl.h +28 -28
- package/bin/koffi/darwin_arm64/koffi.node +0 -0
- package/bin/koffi/darwin_x64/koffi.node +0 -0
- package/bin/koffi/freebsd_arm64/koffi.node +0 -0
- package/bin/koffi/freebsd_ia32/koffi.node +0 -0
- package/bin/koffi/freebsd_x64/koffi.node +0 -0
- package/bin/koffi/linux_arm64/koffi.node +0 -0
- package/bin/koffi/linux_ia32/koffi.node +0 -0
- package/bin/koffi/linux_x64/koffi.node +0 -0
- package/bin/koffi/musl_arm64/koffi.node +0 -0
- package/bin/koffi/musl_x64/koffi.node +0 -0
- package/bin/koffi/openbsd_ia32/koffi.node +0 -0
- package/bin/koffi/openbsd_x64/koffi.node +0 -0
- package/bin/koffi/win32_ia32/koffi.exp +0 -0
- package/bin/koffi/win32_ia32/koffi.lib +0 -0
- package/bin/koffi/win32_ia32/koffi.node +0 -0
- package/bin/koffi/win32_x64/koffi.exp +0 -0
- package/bin/koffi/win32_x64/koffi.lib +0 -0
- package/bin/koffi/win32_x64/koffi.node +0 -0
- package/doc/packaging.md +0 -88
- package/src/koffi/src/abi/arm32.cc +0 -1022
- package/src/koffi/src/abi/arm32_asm.S +0 -166
package/CHANGELOG.md
CHANGED
|
@@ -7,19 +7,35 @@
|
|
|
7
7
|
|
|
8
8
|
### Koffi 3.0
|
|
9
9
|
|
|
10
|
-
#### Koffi 3.0.0
|
|
10
|
+
#### Koffi 3.0.0
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
*Released on 2026-05-16*
|
|
13
|
+
|
|
14
|
+
**Highlights:**
|
|
15
|
+
|
|
16
|
+
- Rewrite call preparation and execution for **vastly improved performance**
|
|
17
|
+
- Distribute prebuilt binaries in **separate subpackages**
|
|
18
|
+
|
|
19
|
+
**Breaking changes:**
|
|
13
20
|
|
|
14
21
|
- Replace use of externals with type objects:
|
|
15
22
|
* Use `koffi.type()` to resolve type specifiers (strings or objects) to type objects
|
|
16
|
-
* Access type information directly on type
|
|
23
|
+
* Access type information directly on type objects without `koffi.introspect()`
|
|
17
24
|
- Replace use of externals with BigInt pointers
|
|
18
|
-
-
|
|
25
|
+
- Support ESM and CJS module types
|
|
19
26
|
|
|
20
27
|
**Other changes:**
|
|
21
28
|
|
|
22
29
|
- Add `koffi.enumeration()` to create [enum types](input#enum-types)
|
|
30
|
+
- Add fast decode functions for integers, floats and strings
|
|
31
|
+
- Use proper types for various objects and handles:
|
|
32
|
+
* Use *LibraryHandle* objects for loaded libraries
|
|
33
|
+
* Use *TypeObject* objects for Koffi types
|
|
34
|
+
* Use *PollHandle* for socket poll handles
|
|
35
|
+
- Add `Symbol.dispose` on library objects and poll handles
|
|
36
|
+
- Prefer types to interfaces in TypeScript declaration file
|
|
37
|
+
- Fix various bugs and small leaks (such as library handles)
|
|
38
|
+
- The ARM32 backend has been temporarily removed
|
|
23
39
|
|
|
24
40
|
**Newly deprecated functions:**
|
|
25
41
|
|
|
@@ -37,6 +53,12 @@ Consult the [migration guide](migration) for more information.
|
|
|
37
53
|
|
|
38
54
|
### Koffi 2.16
|
|
39
55
|
|
|
56
|
+
#### Koffi 2.16.2
|
|
57
|
+
|
|
58
|
+
*Released on 2026-05-06*
|
|
59
|
+
|
|
60
|
+
- Fix string truncation bugs when passing some kinds of long V8 strings (see [Koromix/koffi#266](https://github.com/Koromix/koffi/issues/266))
|
|
61
|
+
|
|
40
62
|
#### Koffi 2.16.1
|
|
41
63
|
|
|
42
64
|
*Released on 2026-04-17*
|
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2026 Niels Martignène <niels.martignene@protonmail.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the “Software”), to deal in
|
package/README.md
CHANGED
|
@@ -13,13 +13,11 @@ ISA / OS | Windows | Linux/glibc | Linux/musl | macOS | Fre
|
|
|
13
13
|
------------------ | ----------- | ----------- | ----------- | ----------- | ----------- | --------
|
|
14
14
|
x86 (IA32) [^1] | ✅ Yes | ✅ Yes | 🟨 Probably | ⬜️ *N/A* | ✅ Yes | ✅ Yes
|
|
15
15
|
x86_64 (AMD64) | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes
|
|
16
|
-
ARM32 LE [^2] | ⬜️ *N/A* | ✅ Yes | 🟨 Probably | ⬜️ *N/A* | 🟨 Probably | 🟨 Probably
|
|
17
16
|
ARM64 (AArch64) LE | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | 🟨 Probably
|
|
18
17
|
RISC-V 64 [^3] | ⬜️ *N/A* | ✅ Yes | 🟨 Probably | ⬜️ *N/A* | 🟨 Probably | 🟨 Probably
|
|
19
18
|
LoongArch64 | ⬜️ *N/A* | ✅ Yes | 🟨 Probably | ⬜️ *N/A* | 🟨 Probably | 🟨 Probably
|
|
20
19
|
|
|
21
20
|
[^1]: The following call conventions are supported: cdecl, stdcall, MS fastcall, thiscall.
|
|
22
|
-
[^2]: The prebuilt binary uses the hard float ABI and expects a VFP coprocessor. Build from source to use Koffi with a different ABI (softfp, soft).
|
|
23
21
|
[^3]: The prebuilt binary uses the LP64D (double-precision float) ABI. The LP64 ABI is supported in theory if you build Koffi from source but this is untested. The LP64F ABI is not supported.
|
|
24
22
|
|
|
25
23
|
Go to the web site for more information: https://koffi.dev/
|
package/{build.cjs → cnoke.cjs}
RENAMED
|
@@ -23,16 +23,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// ../cnoke/cnoke.js
|
|
26
|
-
var
|
|
26
|
+
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
27
27
|
|
|
28
28
|
// ../cnoke/src/builder.js
|
|
29
|
-
var
|
|
30
|
-
var
|
|
31
|
-
var
|
|
32
|
-
var
|
|
29
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
30
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
31
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
32
|
+
var import_node_child_process = require("node:child_process");
|
|
33
33
|
|
|
34
34
|
// ../cnoke/src/abi.js
|
|
35
|
-
var
|
|
35
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
36
36
|
function determineAbi() {
|
|
37
37
|
let abi = process.arch;
|
|
38
38
|
if (abi == "riscv32" || abi == "riscv64") {
|
|
@@ -76,13 +76,13 @@ function determineAbi() {
|
|
|
76
76
|
function readFileHeader(filename, read) {
|
|
77
77
|
let fd = null;
|
|
78
78
|
try {
|
|
79
|
-
let fd2 =
|
|
79
|
+
let fd2 = import_node_fs.default.openSync(filename);
|
|
80
80
|
let buf = Buffer.allocUnsafe(read);
|
|
81
|
-
let len =
|
|
81
|
+
let len = import_node_fs.default.readSync(fd2, buf);
|
|
82
82
|
return buf.subarray(0, len);
|
|
83
83
|
} finally {
|
|
84
84
|
if (fd != null)
|
|
85
|
-
|
|
85
|
+
import_node_fs.default.closeSync(fd);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
function decodeElfHeader(buf) {
|
|
@@ -122,7 +122,7 @@ function decodeElfHeader(buf) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// ../cnoke/src/util.js
|
|
125
|
-
var
|
|
125
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
126
126
|
function pathIsAbsolute(path2) {
|
|
127
127
|
if (process.platform == "win32" && path2.match(/^[a-zA-Z]:/))
|
|
128
128
|
path2 = path2.substr(2);
|
|
@@ -138,28 +138,28 @@ function isPathSeparator(c) {
|
|
|
138
138
|
function syncFiles(src_dir, dest_dir) {
|
|
139
139
|
let keep = /* @__PURE__ */ new Set();
|
|
140
140
|
{
|
|
141
|
-
let entries =
|
|
141
|
+
let entries = import_node_fs2.default.readdirSync(src_dir, { withFileTypes: true });
|
|
142
142
|
for (let entry of entries) {
|
|
143
143
|
if (!entry.isFile())
|
|
144
144
|
continue;
|
|
145
145
|
keep.add(entry.name);
|
|
146
|
-
|
|
146
|
+
import_node_fs2.default.copyFileSync(src_dir + `/${entry.name}`, dest_dir + `/${entry.name}`);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
{
|
|
150
|
-
let entries =
|
|
150
|
+
let entries = import_node_fs2.default.readdirSync(dest_dir, { withFileTypes: true });
|
|
151
151
|
for (let entry of entries) {
|
|
152
152
|
if (!entry.isFile())
|
|
153
153
|
continue;
|
|
154
154
|
if (keep.has(entry.name))
|
|
155
155
|
continue;
|
|
156
|
-
|
|
156
|
+
import_node_fs2.default.unlinkSync(dest_dir + `/${entry.name}`);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
function unlinkRecursive(path2) {
|
|
161
161
|
try {
|
|
162
|
-
|
|
162
|
+
import_node_fs2.default.rmSync(path2, { recursive: true, maxRetries: process.platform == "win32" ? 3 : 0 });
|
|
163
163
|
} catch (err) {
|
|
164
164
|
if (err.code !== "ENOENT")
|
|
165
165
|
throw err;
|
|
@@ -196,7 +196,7 @@ function compareVersions(ver1, ver2) {
|
|
|
196
196
|
|
|
197
197
|
// ../cnoke/src/assets.js
|
|
198
198
|
var FIND_CNOKE_CMAKE = `# SPDX-License-Identifier: MIT
|
|
199
|
-
# SPDX-FileCopyrightText:
|
|
199
|
+
# SPDX-FileCopyrightText: 2026 Niels Martign\xE8ne <niels.martignene@protonmail.com>
|
|
200
200
|
|
|
201
201
|
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR
|
|
202
202
|
CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
|
@@ -324,7 +324,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU
|
|
|
324
324
|
endif()
|
|
325
325
|
`;
|
|
326
326
|
var WIN_DELAY_HOOK_C = `// SPDX-License-Identifier: MIT
|
|
327
|
-
// SPDX-FileCopyrightText:
|
|
327
|
+
// SPDX-FileCopyrightText: 2026 Niels Martign\xE8ne <niels.martignene@protonmail.com>
|
|
328
328
|
|
|
329
329
|
#include <stdlib.h>
|
|
330
330
|
#if !defined(NOMINMAX)
|
|
@@ -413,10 +413,10 @@ function Builder(config = {}) {
|
|
|
413
413
|
checkCompatibility();
|
|
414
414
|
console.log(`>> Node: ${runtime_version}`);
|
|
415
415
|
console.log(`>> Toolchain: ${toolchain ?? "native"}`);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
retry &=
|
|
416
|
+
import_node_fs3.default.mkdirSync(build_dir, { recursive: true, mode: 493 });
|
|
417
|
+
import_node_fs3.default.mkdirSync(work_dir, { recursive: true, mode: 493 });
|
|
418
|
+
import_node_fs3.default.mkdirSync(output_dir, { recursive: true, mode: 493 });
|
|
419
|
+
retry &= import_node_fs3.default.existsSync(work_dir + "/CMakeCache.txt");
|
|
420
420
|
args.push(`-DNODE_JS_EXECPATH=${process.execPath}`);
|
|
421
421
|
if (options2.api == null) {
|
|
422
422
|
let downloaded = false;
|
|
@@ -427,7 +427,7 @@ function Builder(config = {}) {
|
|
|
427
427
|
let api_dir = expandPath(options2.api, project_dir);
|
|
428
428
|
args.push(`-DNODE_JS_INCLUDE_DIRS=${api_dir}/include`);
|
|
429
429
|
}
|
|
430
|
-
|
|
430
|
+
import_node_fs3.default.writeFileSync(work_dir + "/FindCNoke.cmake", FIND_CNOKE_CMAKE);
|
|
431
431
|
args.push(`-DCMAKE_MODULE_PATH=${work_dir}`);
|
|
432
432
|
let win32 = (toolchain ?? host).startsWith("win32_");
|
|
433
433
|
let mingw = process.platform == "win32" && process.env.MSYSTEM != null;
|
|
@@ -444,16 +444,16 @@ function Builder(config = {}) {
|
|
|
444
444
|
args.push(`-DNODE_JS_LINK_DEF=${api_dir}/def/node_api.def`);
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
|
-
|
|
447
|
+
import_node_fs3.default.writeFileSync(work_dir + "/win_delay_hook.c", WIN_DELAY_HOOK_C);
|
|
448
448
|
args.push(`-DNODE_JS_SOURCES=${work_dir}/win_delay_hook.c`);
|
|
449
449
|
}
|
|
450
450
|
if (process.platform != "win32" || mingw) {
|
|
451
|
-
if ((0,
|
|
451
|
+
if ((0, import_node_child_process.spawnSync)("ninja", ["--version"]).status === 0) {
|
|
452
452
|
args.push("-G", "Ninja");
|
|
453
453
|
} else if (process.platform == "win32") {
|
|
454
454
|
args.push("-G", "MinGW Makefiles");
|
|
455
455
|
}
|
|
456
|
-
if (config.ccache && (0,
|
|
456
|
+
if (config.ccache && (0, import_node_child_process.spawnSync)("ccache", ["--version"]).status === 0) {
|
|
457
457
|
args.push("-DCMAKE_C_COMPILER_LAUNCHER=ccache");
|
|
458
458
|
args.push("-DCMAKE_CXX_COMPILER_LAUNCHER=ccache");
|
|
459
459
|
}
|
|
@@ -475,7 +475,7 @@ function Builder(config = {}) {
|
|
|
475
475
|
args.push(`-D${define}`);
|
|
476
476
|
args.push("--no-warn-unused-cli");
|
|
477
477
|
console.log(">> Running configuration");
|
|
478
|
-
let proc = (0,
|
|
478
|
+
let proc = (0, import_node_child_process.spawnSync)(cmake_bin, args, { cwd: work_dir, stdio: "inherit" });
|
|
479
479
|
if (proc.status !== 0) {
|
|
480
480
|
unlinkRecursive(work_dir);
|
|
481
481
|
if (retry)
|
|
@@ -494,10 +494,10 @@ function Builder(config = {}) {
|
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
checkCMake();
|
|
497
|
-
if (!
|
|
497
|
+
if (!import_node_fs3.default.existsSync(work_dir + "/CMakeCache.txt"))
|
|
498
498
|
await self.configure();
|
|
499
499
|
if (process.env.MAKEFLAGS == null)
|
|
500
|
-
process.env.MAKEFLAGS = "-j" + (
|
|
500
|
+
process.env.MAKEFLAGS = "-j" + (import_node_os.default.cpus().length || 1);
|
|
501
501
|
let args = [
|
|
502
502
|
"--build",
|
|
503
503
|
work_dir,
|
|
@@ -509,14 +509,14 @@ function Builder(config = {}) {
|
|
|
509
509
|
for (let target of targets)
|
|
510
510
|
args.push("--target", target);
|
|
511
511
|
console.log(">> Running build");
|
|
512
|
-
let proc = (0,
|
|
512
|
+
let proc = (0, import_node_child_process.spawnSync)(cmake_bin, args, { stdio: "inherit" });
|
|
513
513
|
if (proc.status !== 0)
|
|
514
514
|
throw new Error("Failed to run build step");
|
|
515
515
|
console.log(">> Copy target files");
|
|
516
516
|
syncFiles(output_dir, build_dir);
|
|
517
517
|
};
|
|
518
518
|
async function checkPrebuild() {
|
|
519
|
-
let proc = (0,
|
|
519
|
+
let proc = (0, import_node_child_process.spawnSync)(process.execPath, ["-e", "require(process.argv[1])", package_dir]);
|
|
520
520
|
return proc.status === 0;
|
|
521
521
|
}
|
|
522
522
|
this.clean = function() {
|
|
@@ -526,9 +526,9 @@ function Builder(config = {}) {
|
|
|
526
526
|
if (process.platform == "win32")
|
|
527
527
|
dirname = dirname.replace(/\\/g, "/");
|
|
528
528
|
do {
|
|
529
|
-
if (
|
|
529
|
+
if (import_node_fs3.default.existsSync(dirname + "/" + basename))
|
|
530
530
|
return dirname;
|
|
531
|
-
dirname =
|
|
531
|
+
dirname = import_node_path.default.dirname(dirname);
|
|
532
532
|
} while (!dirname.endsWith("/"));
|
|
533
533
|
return null;
|
|
534
534
|
}
|
|
@@ -537,7 +537,7 @@ function Builder(config = {}) {
|
|
|
537
537
|
let cache_dir2 = process.env["LOCALAPPDATA"] || process.env["APPDATA"];
|
|
538
538
|
if (cache_dir2 == null)
|
|
539
539
|
throw new Error("Missing LOCALAPPDATA and APPDATA environment variable");
|
|
540
|
-
cache_dir2 =
|
|
540
|
+
cache_dir2 = import_node_path.default.join(cache_dir2, "cnoke");
|
|
541
541
|
return cache_dir2;
|
|
542
542
|
} else {
|
|
543
543
|
let cache_dir2 = process.env["XDG_CACHE_HOME"];
|
|
@@ -545,29 +545,29 @@ function Builder(config = {}) {
|
|
|
545
545
|
let home = process.env["HOME"];
|
|
546
546
|
if (home == null)
|
|
547
547
|
throw new Error("Missing HOME environment variable");
|
|
548
|
-
cache_dir2 =
|
|
548
|
+
cache_dir2 = import_node_path.default.join(home, ".cache");
|
|
549
549
|
}
|
|
550
|
-
cache_dir2 =
|
|
550
|
+
cache_dir2 = import_node_path.default.join(cache_dir2, "cnoke");
|
|
551
551
|
return cache_dir2;
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
554
|
function checkCMake() {
|
|
555
555
|
if (cmake_bin != null)
|
|
556
556
|
return;
|
|
557
|
-
if (!
|
|
557
|
+
if (!import_node_fs3.default.existsSync(project_dir + "/CMakeLists.txt"))
|
|
558
558
|
throw new Error("This directory does not appear to have a CMakeLists.txt file");
|
|
559
559
|
{
|
|
560
|
-
let proc = (0,
|
|
560
|
+
let proc = (0, import_node_child_process.spawnSync)("cmake", ["--version"]);
|
|
561
561
|
if (proc.status === 0) {
|
|
562
562
|
cmake_bin = "cmake";
|
|
563
563
|
} else {
|
|
564
564
|
if (process.platform == "win32") {
|
|
565
|
-
let proc2 = (0,
|
|
565
|
+
let proc2 = (0, import_node_child_process.spawnSync)("reg", ["query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Kitware\\CMake", "/v", "InstallDir"]);
|
|
566
566
|
if (proc2.status === 0) {
|
|
567
567
|
let matches = proc2.stdout.toString("utf-8").match(/InstallDir[ \t]+REG_[A-Z_]+[ \t]+(.*)+/);
|
|
568
568
|
if (matches != null) {
|
|
569
|
-
let bin =
|
|
570
|
-
if (
|
|
569
|
+
let bin = import_node_path.default.join(matches[1].trim(), "bin\\cmake.exe");
|
|
570
|
+
if (import_node_fs3.default.existsSync(bin))
|
|
571
571
|
cmake_bin = bin;
|
|
572
572
|
}
|
|
573
573
|
}
|
|
@@ -599,7 +599,7 @@ function Builder(config = {}) {
|
|
|
599
599
|
let cnoke = null;
|
|
600
600
|
if (package_dir != null) {
|
|
601
601
|
try {
|
|
602
|
-
let json =
|
|
602
|
+
let json = import_node_fs3.default.readFileSync(package_dir + "/package.json", { encoding: "utf-8" });
|
|
603
603
|
pkg = JSON.parse(json);
|
|
604
604
|
directory = package_dir;
|
|
605
605
|
} catch (err) {
|
|
@@ -610,7 +610,7 @@ function Builder(config = {}) {
|
|
|
610
610
|
if (cnoke == null)
|
|
611
611
|
cnoke = pkg?.cnoke ?? {};
|
|
612
612
|
options = {
|
|
613
|
-
name: pkg?.name ??
|
|
613
|
+
name: pkg?.name ?? import_node_path.default.basename(project_dir),
|
|
614
614
|
version: pkg?.version ?? null,
|
|
615
615
|
directory,
|
|
616
616
|
...cnoke
|
|
@@ -644,8 +644,8 @@ function Builder(config = {}) {
|
|
|
644
644
|
function expandPath(str, root) {
|
|
645
645
|
let expanded = expandString(str);
|
|
646
646
|
if (!pathIsAbsolute(expanded))
|
|
647
|
-
expanded =
|
|
648
|
-
expanded =
|
|
647
|
+
expanded = import_node_path.default.join(root, expanded);
|
|
648
|
+
expanded = import_node_path.default.normalize(expanded);
|
|
649
649
|
return expanded;
|
|
650
650
|
}
|
|
651
651
|
}
|
|
@@ -690,11 +690,11 @@ async function main() {
|
|
|
690
690
|
} else if (arg == "-D" || arg == "--directory") {
|
|
691
691
|
if (value == null)
|
|
692
692
|
throw new Error(`Missing value for ${arg}`);
|
|
693
|
-
config.project_dir =
|
|
693
|
+
config.project_dir = import_node_fs4.default.realpathSync(value);
|
|
694
694
|
} else if (arg == "-P" || arg == "--package") {
|
|
695
695
|
if (value == null)
|
|
696
696
|
throw new Error(`Missing value for ${arg}`);
|
|
697
|
-
config.package_dir =
|
|
697
|
+
config.package_dir = import_node_fs4.default.realpathSync(value);
|
|
698
698
|
} else if (arg == "-O" || arg == "--output") {
|
|
699
699
|
if (value == null)
|
|
700
700
|
throw new Error(`Missing value for ${arg}`);
|
package/doc/benchmarks.md
CHANGED
|
@@ -4,12 +4,10 @@ Here is a quick overview of the execution time of Koffi calls on three benchmark
|
|
|
4
4
|
|
|
5
5
|
- The first benchmark is based on `rand()` calls
|
|
6
6
|
- The second benchmark is based on `atoi()` calls
|
|
7
|
-
- The third benchmark is based on
|
|
7
|
+
- The third benchmark is based on `memset()` calls
|
|
8
8
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
<a href="{{ ASSET static/perf_windows.png }}" target="_blank"><img src="{{ ASSET static/perf_windows.png }}" alt="Windows x86_64 performance" style="width: 350px;"/></a>
|
|
12
|
-
</p>
|
|
9
|
+
<div class="benchmark chart" data-platform="linux_x64"></div>
|
|
10
|
+
<div class="benchmark chart" data-platform="win32_x64"></div>
|
|
13
11
|
|
|
14
12
|
These results are detailed and explained below, and compared to node-ffi/node-ffi-napi.
|
|
15
13
|
|
|
@@ -19,17 +17,9 @@ The results presented below were measured on my x86_64 Linux machine (Intel® Co
|
|
|
19
17
|
|
|
20
18
|
## rand results
|
|
21
19
|
|
|
22
|
-
This test is based around repeated calls to a simple standard C function `rand`, and
|
|
20
|
+
This test is based around repeated calls to a simple standard C function `rand`, which takes no parameter and returns a 32-bit integer.
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
- the second one calls rand through Koffi
|
|
26
|
-
- the third one uses the official Node.js FFI implementation, node-ffi-napi
|
|
27
|
-
|
|
28
|
-
rand | Iteration time | Relative performance | Overhead
|
|
29
|
-
------------- | -------------- | -------------------- | --------
|
|
30
|
-
napi | 256 ns | x1.00 | +0%
|
|
31
|
-
koffi | 375 ns | x0.68 | +46%
|
|
32
|
-
node-ffi-napi | 29783 ns | x0.009 | +11544%
|
|
22
|
+
<div class="benchmark table" data-platform="linux_x64" data-benchmark="rand"></div>
|
|
33
23
|
|
|
34
24
|
Because rand is a pretty small function, the FFI overhead is clearly visible.
|
|
35
25
|
|
|
@@ -37,27 +27,13 @@ Because rand is a pretty small function, the FFI overhead is clearly visible.
|
|
|
37
27
|
|
|
38
28
|
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.
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
------------- | -------------- | -------------------- | --------
|
|
42
|
-
napi | 371 ns | x1.00 | +0%
|
|
43
|
-
koffi | 557 ns | x0.67 | +50%
|
|
44
|
-
node-ffi-napi | 104340 ns | x0.004 | +27988%
|
|
45
|
-
|
|
46
|
-
Because atoi is a pretty small function, the FFI overhead is clearly visible.
|
|
47
|
-
|
|
48
|
-
## Raylib results
|
|
30
|
+
<div class="benchmark table" data-platform="linux_x64" data-benchmark="atoi"></div>
|
|
49
31
|
|
|
50
|
-
|
|
32
|
+
## memset results
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
- [node-raylib](https://github.com/RobLoach/node-raylib): This is a native wrapper implemented with N-API
|
|
34
|
+
This test is based around repeated calls to the standard C function `memset`. All implementations pass a Node.js Buffer for the pointer argument.
|
|
54
35
|
|
|
55
|
-
|
|
56
|
-
------------- | -------------- | -------------------- | --------
|
|
57
|
-
C++ | 10.8 µs | x1.14 | -12%
|
|
58
|
-
node-raylib | 12.3 µs | x1.00 | +0%
|
|
59
|
-
koffi | 13.2 µs | x0.92 | +8%
|
|
60
|
-
node-ffi-napi | 80.3 µs | x0.15 | +555%
|
|
36
|
+
<div class="benchmark table" data-platform="linux_x64" data-benchmark="memset"></div>
|
|
61
37
|
|
|
62
38
|
# Windows x86_64
|
|
63
39
|
|
|
@@ -65,47 +41,21 @@ The results presented below were measured on my x86_64 Windows machine (Intel®
|
|
|
65
41
|
|
|
66
42
|
## rand results
|
|
67
43
|
|
|
68
|
-
This test is based around repeated calls to a simple standard C function `rand`, and
|
|
69
|
-
|
|
70
|
-
- the first one is the reference, it calls rand 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)
|
|
71
|
-
- the second one calls rand through Koffi
|
|
72
|
-
- the third one uses the official Node.js FFI implementation, node-ffi-napi
|
|
73
|
-
|
|
74
|
-
rand | Iteration time | Relative performance | Overhead
|
|
75
|
-
------------- | -------------- | -------------------- | --------
|
|
76
|
-
napi | 859 ns | x1.00 | (ref)
|
|
77
|
-
koffi | 1352 ns | x0.64 | +57%
|
|
78
|
-
node-ffi-napi | 35640 ns | x0.02 | +4048%
|
|
44
|
+
This test is based around repeated calls to a simple standard C function `rand`, which takes no parameter and returns a 32-bit integer.
|
|
79
45
|
|
|
80
|
-
|
|
46
|
+
<div class="benchmark table" data-platform="win32_x64" data-benchmark="rand"></div>
|
|
81
47
|
|
|
82
48
|
## atoi results
|
|
83
49
|
|
|
84
50
|
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.
|
|
85
51
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
atoi | Iteration time | Relative performance | Overhead
|
|
89
|
-
------------- | -------------- | -------------------- | --------
|
|
90
|
-
napi | 1336 ns | x1.00 | (ref)
|
|
91
|
-
koffi | 2440 ns | x0.55 | +83%
|
|
92
|
-
node-ffi-napi | 136890 ns | x0.010 | +10144%
|
|
93
|
-
|
|
94
|
-
Because atoi is a pretty small function, the FFI overhead is clearly visible.
|
|
52
|
+
<div class="benchmark table" data-platform="win32_x64" data-benchmark="atoi"></div>
|
|
95
53
|
|
|
96
|
-
##
|
|
54
|
+
## memset results
|
|
97
55
|
|
|
98
|
-
This
|
|
56
|
+
This test is based around repeated calls to the standard C function `memset`. All implementations pass a Node.js Buffer for the pointer argument.
|
|
99
57
|
|
|
100
|
-
|
|
101
|
-
- raylib_cc: C++ implementation of the benchmark, without any Javascript
|
|
102
|
-
|
|
103
|
-
raylib | Iteration time | Relative performance | Overhead
|
|
104
|
-
------------- | -------------- | -------------------- | --------
|
|
105
|
-
C++ | 18.2 µs | x1.50 | -33%
|
|
106
|
-
node-raylib | 27.3 µs | x1.00 | (ref)
|
|
107
|
-
koffi | 29.8 µs | x0.92 | +9%
|
|
108
|
-
node-ffi-napi | 96.3 µs | x0.28 | +253%
|
|
58
|
+
<div class="benchmark table" data-platform="win32_x64" data-benchmark="memset"></div>
|
|
109
59
|
|
|
110
60
|
# Running benchmarks
|
|
111
61
|
|
|
@@ -124,3 +74,5 @@ Once everything is built and ready, run:
|
|
|
124
74
|
```sh
|
|
125
75
|
node benchmark.js
|
|
126
76
|
```
|
|
77
|
+
|
|
78
|
+
<script src="{{ ASSET static/benchmarks.js }}"></script>
|
package/doc/callbacks.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
In order to pass a JS function to a C function expecting a callback, you must first create a callback type with the expected return type and parameters. The syntax is similar to the one used to load functions from a shared library.
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
|
-
|
|
9
|
-
const koffi = require('koffi');
|
|
8
|
+
import koffi from 'koffi';
|
|
9
|
+
// CJS: const koffi = require('koffi');
|
|
10
10
|
|
|
11
11
|
// With the classic syntax, this callback expects an integer and returns nothing
|
|
12
12
|
const ExampleCallback = koffi.proto('ExampleCallback', 'void', ['int']);
|
|
@@ -71,8 +71,8 @@ int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
|
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
```js
|
|
74
|
-
|
|
75
|
-
const koffi = require('koffi');
|
|
74
|
+
import koffi from 'koffi';
|
|
75
|
+
// CJS: const koffi = require('koffi');
|
|
76
76
|
|
|
77
77
|
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
78
78
|
|
|
@@ -121,8 +121,8 @@ void SayIt(const char *name)
|
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
```js
|
|
124
|
-
|
|
125
|
-
const koffi = require('koffi');
|
|
124
|
+
import koffi from 'koffi';
|
|
125
|
+
// CJS: const koffi = require('koffi');
|
|
126
126
|
|
|
127
127
|
const lib = koffi.load('./callbacks.so'); // Fake path
|
|
128
128
|
|
|
@@ -169,8 +169,8 @@ You can pass this value through to another C function that expects a pointer of
|
|
|
169
169
|
The following examples uses it to sort an array of strings in-place with the standard C function `qsort()`:
|
|
170
170
|
|
|
171
171
|
```js
|
|
172
|
-
|
|
173
|
-
const koffi = require('koffi');
|
|
172
|
+
import koffi from 'koffi';
|
|
173
|
+
// CJS: const koffi = require('koffi');
|
|
174
174
|
|
|
175
175
|
const lib = koffi.load('libc.so.6');
|
|
176
176
|
|
package/doc/functions.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
To declare functions, start by loading the shared library with `koffi.load(filename)`.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
|
-
|
|
7
|
-
const koffi = require('koffi');
|
|
6
|
+
import koffi from 'koffi';
|
|
7
|
+
// CJS: const koffi = require('koffi');
|
|
8
8
|
|
|
9
9
|
const lib = koffi.load('/path/to/shared/library'); // File extension depends on platforms: .so, .dll, .dylib, etc.
|
|
10
10
|
```
|
|
@@ -105,8 +105,8 @@ You can safely use these on non-x86 platforms, they are simply ignored.
|
|
|
105
105
|
Below you can find a small example showing how to use a non-default calling convention, with the two syntaxes:
|
|
106
106
|
|
|
107
107
|
```js
|
|
108
|
-
|
|
109
|
-
const koffi = require('koffi');
|
|
108
|
+
import koffi from 'koffi';
|
|
109
|
+
// CJS: const koffi = require('koffi');
|
|
110
110
|
|
|
111
111
|
const lib = koffi.load('user32.dll');
|
|
112
112
|
|
|
@@ -142,8 +142,8 @@ printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END
|
|
|
142
142
|
You can issue asynchronous calls by calling the function through its async member. In this case, you need to provide a callback function as the last argument, with `(err, res)` parameters.
|
|
143
143
|
|
|
144
144
|
```js
|
|
145
|
-
|
|
146
|
-
const koffi = require('koffi');
|
|
145
|
+
import koffi from 'koffi';
|
|
146
|
+
// CJS: const koffi = require('koffi');
|
|
147
147
|
|
|
148
148
|
const lib = koffi.load('libc.so.6');
|
|
149
149
|
|
package/doc/index.md
CHANGED
|
@@ -21,18 +21,16 @@ The following combinations of OS and architectures __are officially supported an
|
|
|
21
21
|
|
|
22
22
|
ISA / OS | Windows | Linux/glibc | Linux/musl | macOS | FreeBSD | OpenBSD
|
|
23
23
|
------------------ | ------- | ----------- | ---------- | ----- | ------- | -------
|
|
24
|
-
x86 (IA32) [^
|
|
24
|
+
x86 (IA32) [^1] | ✅ | ✅ | 🟨 | ⬜️ | ✅ | ✅
|
|
25
25
|
x86_64 (AMD64) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
|
|
26
|
-
ARM32 LE [^3] | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
|
|
27
26
|
ARM64 (AArch64) LE | ✅ | ✅ | ✅ | ✅ | ✅ | 🟨
|
|
28
|
-
RISC-V 64 [^
|
|
27
|
+
RISC-V 64 [^2] | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
|
|
29
28
|
LoongArch64 | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
|
|
30
29
|
|
|
31
30
|
<div class="legend">✅ Yes | 🟨 Probably | ⬜️ Not applicable</div>
|
|
32
31
|
|
|
33
|
-
[^
|
|
34
|
-
[^
|
|
35
|
-
[^4]: The prebuilt binary uses the LP64D (double-precision float) ABI. The LP64 ABI is supported in theory if you build Koffi from source (untested), the LP64F ABI is not supported.
|
|
32
|
+
[^1]: The following call conventions are supported for forward calls: cdecl, stdcall, MS fastcall, thiscall. Only cdecl and stdcall can be used for C to JS callbacks.
|
|
33
|
+
[^2]: The prebuilt binary uses the LP64D (double-precision float) ABI. The LP64 ABI is supported in theory if you build Koffi from source (untested), the LP64F ABI is not supported.
|
|
36
34
|
|
|
37
35
|
For all fully supported platforms (green check marks), a prebuilt binary is included in the NPM package which means you can install Koffi without a C++ compiler.
|
|
38
36
|
|
package/doc/input.md
CHANGED
|
@@ -278,8 +278,8 @@ const char *ConcatBuild(Concat *c)
|
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
```js
|
|
281
|
-
|
|
282
|
-
const koffi = require('koffi');
|
|
281
|
+
import koffi from 'koffi';
|
|
282
|
+
// CJS: const koffi = require('koffi');
|
|
283
283
|
|
|
284
284
|
const lib = koffi.load('./handles.so');
|
|
285
285
|
|
|
@@ -348,8 +348,8 @@ Koffi applies the following conversion rules when passing arrays to/from C:
|
|
|
348
348
|
See the example below:
|
|
349
349
|
|
|
350
350
|
```js
|
|
351
|
-
|
|
352
|
-
const koffi = require('koffi');
|
|
351
|
+
import koffi from 'koffi';
|
|
352
|
+
// CJS: const koffi = require('koffi');
|
|
353
353
|
|
|
354
354
|
// Those two structs are exactly the same, only the array conversion hint is different
|
|
355
355
|
const Foo1 = koffi.struct('Foo1', {
|
|
@@ -438,8 +438,8 @@ void AppendValues(struct FlexibleArray *arr, size_t count, int start, int step)
|
|
|
438
438
|
```
|
|
439
439
|
|
|
440
440
|
```js
|
|
441
|
-
|
|
442
|
-
const koffi = require('koffi');
|
|
441
|
+
import koffi from 'koffi';
|
|
442
|
+
// CJS: const koffi = require('koffi');
|
|
443
443
|
|
|
444
444
|
const lib = koffi.load('./flexible.so');
|
|
445
445
|
|
package/doc/misc.md
CHANGED
|
@@ -151,8 +151,8 @@ The standard POSIX error codes are available in `koffi.os.errno`, as shown below
|
|
|
151
151
|
```js
|
|
152
152
|
const assert = require('assert');
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
const koffi = require('koffi');
|
|
154
|
+
import koffi from 'koffi';
|
|
155
|
+
// CJS: const koffi = require('koffi');
|
|
156
156
|
|
|
157
157
|
const lib = koffi.load('libc.so.6');
|
|
158
158
|
|