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.
Files changed (77) hide show
  1. package/CHANGELOG.md +26 -4
  2. package/LICENSE.txt +1 -1
  3. package/README.md +0 -2
  4. package/{build.cjs → cnoke.cjs} +46 -46
  5. package/doc/benchmarks.md +17 -65
  6. package/doc/callbacks.md +8 -8
  7. package/doc/functions.md +6 -6
  8. package/doc/index.md +4 -6
  9. package/doc/input.md +6 -6
  10. package/doc/misc.md +2 -2
  11. package/doc/output.md +10 -10
  12. package/doc/pointers.md +12 -12
  13. package/doc/start.md +4 -4
  14. package/doc/unions.md +2 -2
  15. package/index.d.ts +124 -94
  16. package/lib/native/base/base.cc +15 -10
  17. package/lib/native/base/base.hh +1 -1
  18. package/lib/native/base/unicode.inc +1 -1
  19. package/package.json +19 -3
  20. package/src/koffi/CMakeLists.txt +6 -12
  21. package/src/koffi/index.cjs +91 -89
  22. package/src/koffi/index.js +75 -76
  23. package/src/koffi/indirect.cjs +91 -89
  24. package/src/koffi/indirect.js +19 -6
  25. package/src/koffi/src/abi/arm64.cc +57 -151
  26. package/src/koffi/src/abi/arm64_asm.S +1 -1
  27. package/src/koffi/src/abi/arm64_asm.asm +1 -1
  28. package/src/koffi/src/abi/loong64.cc +1 -1
  29. package/src/koffi/src/abi/loong64_asm.S +51 -51
  30. package/src/koffi/src/abi/riscv64.cc +1009 -567
  31. package/src/koffi/src/abi/riscv64_asm.S +51 -51
  32. package/src/koffi/src/abi/x64sysv.cc +104 -108
  33. package/src/koffi/src/abi/x64sysv_asm.S +1 -5
  34. package/src/koffi/src/abi/x64win.cc +62 -83
  35. package/src/koffi/src/abi/x64win_asm.S +1 -1
  36. package/src/koffi/src/abi/x64win_asm.asm +1 -1
  37. package/src/koffi/src/abi/x86.cc +60 -100
  38. package/src/koffi/src/abi/x86_asm.S +3 -3
  39. package/src/koffi/src/abi/x86_asm.asm +1 -1
  40. package/src/koffi/src/call.cc +763 -93
  41. package/src/koffi/src/call.hh +27 -2
  42. package/src/koffi/src/errno.inc +1 -1
  43. package/src/koffi/src/ffi.cc +386 -653
  44. package/src/koffi/src/ffi.hh +80 -45
  45. package/src/koffi/src/parser.cc +1 -1
  46. package/src/koffi/src/parser.hh +1 -1
  47. package/src/koffi/src/primitives.inc +1 -1
  48. package/src/koffi/src/trampolines.cjs +3 -3
  49. package/src/koffi/src/util.cc +185 -68
  50. package/src/koffi/src/util.hh +88 -74
  51. package/src/koffi/src/uv.cc +36 -19
  52. package/src/koffi/src/uv.def +1 -3
  53. package/src/koffi/src/uv.hh +27 -14
  54. package/src/koffi/src/win32.cc +1 -1
  55. package/src/koffi/src/win32.hh +1 -1
  56. package/vendor/node-addon-api/napi-inl.h +28 -28
  57. package/bin/koffi/darwin_arm64/koffi.node +0 -0
  58. package/bin/koffi/darwin_x64/koffi.node +0 -0
  59. package/bin/koffi/freebsd_arm64/koffi.node +0 -0
  60. package/bin/koffi/freebsd_ia32/koffi.node +0 -0
  61. package/bin/koffi/freebsd_x64/koffi.node +0 -0
  62. package/bin/koffi/linux_arm64/koffi.node +0 -0
  63. package/bin/koffi/linux_ia32/koffi.node +0 -0
  64. package/bin/koffi/linux_x64/koffi.node +0 -0
  65. package/bin/koffi/musl_arm64/koffi.node +0 -0
  66. package/bin/koffi/musl_x64/koffi.node +0 -0
  67. package/bin/koffi/openbsd_ia32/koffi.node +0 -0
  68. package/bin/koffi/openbsd_x64/koffi.node +0 -0
  69. package/bin/koffi/win32_ia32/koffi.exp +0 -0
  70. package/bin/koffi/win32_ia32/koffi.lib +0 -0
  71. package/bin/koffi/win32_ia32/koffi.node +0 -0
  72. package/bin/koffi/win32_x64/koffi.exp +0 -0
  73. package/bin/koffi/win32_x64/koffi.lib +0 -0
  74. package/bin/koffi/win32_x64/koffi.node +0 -0
  75. package/doc/packaging.md +0 -88
  76. package/src/koffi/src/abi/arm32.cc +0 -1022
  77. 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 (WIP)
10
+ #### Koffi 3.0.0
11
11
 
12
- **Main changes:**
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 variables without `koffi.introspect()`
23
+ * Access type information directly on type objects without `koffi.introspect()`
17
24
  - Replace use of externals with BigInt pointers
18
- - Rewrite call preparation and execution for vastly improved performance
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) 2025 Niels Martignène <niels.martignene@protonmail.com>
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/
@@ -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 import_fs4 = __toESM(require("fs"), 1);
26
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
27
27
 
28
28
  // ../cnoke/src/builder.js
29
- var import_fs3 = __toESM(require("fs"), 1);
30
- var import_os = __toESM(require("os"), 1);
31
- var import_path = __toESM(require("path"), 1);
32
- var import_child_process = require("child_process");
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 import_fs = __toESM(require("fs"), 1);
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 = import_fs.default.openSync(filename);
79
+ let fd2 = import_node_fs.default.openSync(filename);
80
80
  let buf = Buffer.allocUnsafe(read);
81
- let len = import_fs.default.readSync(fd2, buf);
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
- import_fs.default.closeSync(fd);
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 import_fs2 = __toESM(require("fs"), 1);
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 = import_fs2.default.readdirSync(src_dir, { withFileTypes: true });
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
- import_fs2.default.copyFileSync(src_dir + `/${entry.name}`, dest_dir + `/${entry.name}`);
146
+ import_node_fs2.default.copyFileSync(src_dir + `/${entry.name}`, dest_dir + `/${entry.name}`);
147
147
  }
148
148
  }
149
149
  {
150
- let entries = import_fs2.default.readdirSync(dest_dir, { withFileTypes: true });
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
- import_fs2.default.unlinkSync(dest_dir + `/${entry.name}`);
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
- import_fs2.default.rmSync(path2, { recursive: true, maxRetries: process.platform == "win32" ? 3 : 0 });
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: 2025 Niels Martign\xE8ne <niels.martignene@protonmail.com>
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: 2025 Niels Martign\xE8ne <niels.martignene@protonmail.com>
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
- import_fs3.default.mkdirSync(build_dir, { recursive: true, mode: 493 });
417
- import_fs3.default.mkdirSync(work_dir, { recursive: true, mode: 493 });
418
- import_fs3.default.mkdirSync(output_dir, { recursive: true, mode: 493 });
419
- retry &= import_fs3.default.existsSync(work_dir + "/CMakeCache.txt");
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
- import_fs3.default.writeFileSync(work_dir + "/FindCNoke.cmake", FIND_CNOKE_CMAKE);
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
- import_fs3.default.writeFileSync(work_dir + "/win_delay_hook.c", WIN_DELAY_HOOK_C);
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, import_child_process.spawnSync)("ninja", ["--version"]).status === 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, import_child_process.spawnSync)("ccache", ["--version"]).status === 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, import_child_process.spawnSync)(cmake_bin, args, { cwd: work_dir, stdio: "inherit" });
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 (!import_fs3.default.existsSync(work_dir + "/CMakeCache.txt"))
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" + (import_os.default.cpus().length || 1);
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, import_child_process.spawnSync)(cmake_bin, args, { stdio: "inherit" });
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, import_child_process.spawnSync)(process.execPath, ["-e", "require(process.argv[1])", package_dir]);
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 (import_fs3.default.existsSync(dirname + "/" + basename))
529
+ if (import_node_fs3.default.existsSync(dirname + "/" + basename))
530
530
  return dirname;
531
- dirname = import_path.default.dirname(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 = import_path.default.join(cache_dir2, "cnoke");
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 = import_path.default.join(home, ".cache");
548
+ cache_dir2 = import_node_path.default.join(home, ".cache");
549
549
  }
550
- cache_dir2 = import_path.default.join(cache_dir2, "cnoke");
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 (!import_fs3.default.existsSync(project_dir + "/CMakeLists.txt"))
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, import_child_process.spawnSync)("cmake", ["--version"]);
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, import_child_process.spawnSync)("reg", ["query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Kitware\\CMake", "/v", "InstallDir"]);
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 = import_path.default.join(matches[1].trim(), "bin\\cmake.exe");
570
- if (import_fs3.default.existsSync(bin))
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 = import_fs3.default.readFileSync(package_dir + "/package.json", { encoding: "utf-8" });
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 ?? import_path.default.basename(project_dir),
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 = import_path.default.join(root, expanded);
648
- expanded = import_path.default.normalize(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 = import_fs4.default.realpathSync(value);
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 = import_fs4.default.realpathSync(value);
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 [Raylib](https://www.raylib.com/)
7
+ - The third benchmark is based on `memset()` calls
8
8
 
9
- <p style="text-align: center;">
10
- <a href="{{ ASSET static/perf_linux.png }}" target="_blank"><img src="{{ ASSET static/perf_linux.png }}" alt="Linux x86_64 performance" style="width: 350px;"/></a>
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 has three implementations:
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
- - 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)
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
- atoi | Iteration time | Relative performance | Overhead
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
- This benchmark uses the CPU-based image drawing functions in Raylib. The calls are much heavier than in previous benchmarks, thus the FFI overhead is reduced. In this implementation, Koffi is compared to:
32
+ ## memset results
51
33
 
52
- - Baseline: Full C++ version of the code (no JS)
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
- raylib | Iteration time | Relative performance | Overhead
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 has three implementations:
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
- Because rand is a pretty small function, the FFI overhead is clearly visible.
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
- The results below were measured on my x86_64 Windows machine (Intel® Core™ i5-4460):
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
- ## Raylib results
54
+ ## memset results
97
55
 
98
- 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:
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
- - [node-raylib](https://github.com/RobLoach/node-raylib) (baseline): This is a native wrapper implemented with N-API
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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) [^2] | ✅ | ✅ | 🟨 | ⬜️ | ✅ | ✅
24
+ x86 (IA32) [^1] | ✅ | ✅ | 🟨 | ⬜️ | ✅ | ✅
25
25
  x86_64 (AMD64) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
26
- ARM32 LE [^3] | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
27
26
  ARM64 (AArch64) LE | ✅ | ✅ | ✅ | ✅ | ✅ | 🟨
28
- RISC-V 64 [^4] | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
27
+ RISC-V 64 [^2] | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
29
28
  LoongArch64 | ⬜️ | ✅ | 🟨 | ⬜️ | 🟨 | 🟨
30
29
 
31
30
  <div class="legend">✅ Yes | 🟨 Probably | ⬜️ Not applicable</div>
32
31
 
33
- [^2]: 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.
34
- [^3]: 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).
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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
- // ES6 syntax: import koffi from 'koffi';
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