koffi 2.9.2 → 2.10.0

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 (36) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/build/koffi/darwin_arm64/koffi.node +0 -0
  3. package/build/koffi/darwin_x64/koffi.node +0 -0
  4. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  5. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  6. package/build/koffi/freebsd_x64/koffi.node +0 -0
  7. package/build/koffi/linux_arm32/koffi.node +0 -0
  8. package/build/koffi/linux_arm64/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_riscv64/koffi.node +0 -0
  11. package/build/koffi/linux_x64/koffi.node +0 -0
  12. package/build/koffi/musl_x64/koffi.node +0 -0
  13. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  14. package/build/koffi/openbsd_x64/koffi.node +0 -0
  15. package/build/koffi/win32_arm64/koffi.node +0 -0
  16. package/build/koffi/win32_ia32/koffi.node +0 -0
  17. package/build/koffi/win32_x64/koffi.node +0 -0
  18. package/doc/flaat/normal.css +19 -4
  19. package/doc/flaat/small.css +8 -1
  20. package/doc/pages/index.md +1 -1
  21. package/doc/pages/misc.md +35 -0
  22. package/doc/pages/packaging.md +11 -2
  23. package/doc/pages/platforms.md +23 -7
  24. package/doc/pages.ini +8 -7
  25. package/doc/static/koffi.png +0 -0
  26. package/doc/static/logo.webp +0 -0
  27. package/doc/templates/page.html +9 -1
  28. package/index.js +2 -2
  29. package/indirect.js +2 -2
  30. package/package.json +2 -2
  31. package/src/koffi/CMakeLists.txt +6 -0
  32. package/src/koffi/examples/yao-pkg/README.md +17 -0
  33. package/src/koffi/examples/yao-pkg/index.js +2 -0
  34. package/src/koffi/examples/yao-pkg/package.json +22 -0
  35. package/src/koffi/src/ffi.cc +40 -4
  36. package/src/koffi/src/util.cc +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@
5
5
 
6
6
  ## Koffi 2
7
7
 
8
+ ### Koffi 2.10
9
+
10
+ #### Koffi 2.10.0 (2024-12-22)
11
+
12
+ - Allow [redefinition of opaque types](misc#circular-references) to concrete struct or union
13
+ - Update documentation style
14
+
8
15
  ### Koffi 2.9
9
16
 
10
17
  #### Koffi 2.9.2 (2024-11-08)
@@ -30,7 +37,7 @@
30
37
  - Work around MSVC compiler bug introduced in Visual Studio 17.10
31
38
 
32
39
  > [!WARNING]
33
- Use on platforms without pre-built binaries is broken in Koffi 2.8.10, skip this version.
40
+ > Use on platforms without pre-built binaries is broken in Koffi 2.8.10, skip this version.
34
41
 
35
42
  #### Koffi 2.8.9 (2024-05-17)
36
43
 
@@ -700,9 +707,10 @@ This entry documents changes since version 1.1.0.
700
707
 
701
708
  The following features and improvements are planned, not necessarily in that order:
702
709
 
710
+ - Port Koffi to Loong64 ISA and ABI
711
+ - Port Koffi to PowerPC (POWER9+) ISA and ABI
703
712
  - Optimize passing of structs and arrays (with auto-generated JS)
704
713
  - Automate Windows/AArch64 (qemu) and macOS/AArch64 (how? ... thanks Apple) tests
705
714
  - Create a real-world example, using several libraries (Raylib, SQLite, libsodium) to illustrate various C API styles
706
715
  - Add simple struct type parser
707
- - Port Koffi to PowerPC (POWER9+) ABI
708
716
  - Fix assembly unwind and CFI directives for better debugging experience
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
@@ -20,8 +20,9 @@
20
20
  OTHER DEALINGS IN THE SOFTWARE. */
21
21
 
22
22
  html {
23
- --top_height: 120px;
24
- --small_height: 90px;
23
+ --top_height: 90px;
24
+ --top_padding: 6px;
25
+ --small_height: 80px;
25
26
 
26
27
  height: 100%;
27
28
  scroll-padding-top: calc(var(--top_height) + 10px);
@@ -68,7 +69,7 @@ a:has(> img) { text-decoration: none !important; }
68
69
  height: var(--top_height);
69
70
  box-sizing: border-box;
70
71
  margin: 0 auto;
71
- padding: 16px 16px 24px 16px;
72
+ padding: var(--top_padding);
72
73
  overflow: visible;
73
74
  z-index: 1;
74
75
  display: flex;
@@ -132,7 +133,6 @@ a:has(> img) { text-decoration: none !important; }
132
133
  height: 100%;
133
134
  object-fit: contain;
134
135
  }
135
- #top.border #logo { filter: saturate(0%) brightness(0); }
136
136
 
137
137
  #side menu {
138
138
  margin: 0;
@@ -175,6 +175,16 @@ main {
175
175
  }
176
176
  #side ~ main { padding-right: 290px; }
177
177
 
178
+ footer {
179
+ padding: 0.5em;
180
+ background: #f6f6f9;
181
+ display: flex;
182
+ gap: 1.5em;
183
+ align-items: center;
184
+ justify-content: center;
185
+ }
186
+ footer > img { filter: saturate(0%) brightness(0); }
187
+
178
188
  p { margin: 1em 0 0 0; }
179
189
  p:first-child, h1 + p, h2 + p, h3 + p { margin-top: 0; }
180
190
 
@@ -268,6 +278,11 @@ code:not(.hljs) {
268
278
  font-size: 14px;
269
279
  background: #eee;
270
280
  }
281
+ pre > code:not(.hljs) {
282
+ padding: 0;
283
+ font-size: inherit;
284
+ background: transparent;
285
+ }
271
286
 
272
287
  pre {
273
288
  position: relative;
@@ -36,7 +36,6 @@
36
36
  #top menu {
37
37
  height: var(--small_height);
38
38
  padding-left: 60px;
39
- padding-bottom: 16px;
40
39
  flex-direction: column;
41
40
  gap: 0;
42
41
  align-items: start;
@@ -61,6 +60,14 @@
61
60
  line-height: 1.6em;
62
61
  }
63
62
 
63
+ footer {
64
+ flex-direction: column;
65
+ padding: 0.5em;
66
+ gap: 6px;
67
+ text-align: center;
68
+ }
69
+ footer > img { display: none; }
70
+
64
71
  #logo {
65
72
  height: 100%;
66
73
  margin: 0 auto;
@@ -1,4 +1,4 @@
1
- # Overview
1
+ # Koffi
2
2
 
3
3
  Koffi is a **fast and easy-to-use C FFI module for Node.js**, featuring:
4
4
 
package/doc/pages/misc.md CHANGED
@@ -59,6 +59,41 @@ console.log(koffi.sizeof(koffi.types.long));
59
59
 
60
60
  You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
61
61
 
62
+ ## Circular references
63
+
64
+ *New in Koffi 2.10.0*
65
+
66
+ In some cases, composite types can point to each other and thus depend on each other. This can also happen when a function takes a pointer to a struct that also contains a function pointer.
67
+
68
+ To deal with this, you can create an opaque type and redefine it later to a concrete struct or union type, as shown below.
69
+
70
+ ```js
71
+ const Type1 = koffi.opaque('Type1');
72
+
73
+ const Type2 = koffi.struct('Type2', {
74
+ ptr: 'Type1 *',
75
+ i: 'int'
76
+ });
77
+
78
+ // Redefine Type1 to a concrete type
79
+ koffi.struct(Type1, {
80
+ ptr: 'Type2 *',
81
+ f: 'float'
82
+ });
83
+ ```
84
+
85
+ > [!NOTE]
86
+ > You must use a proper type object when you redefine the type. If you only have the name, use `koffi.resolve()` to get a type object from a type string.
87
+ >
88
+ > ```js
89
+ > const MyType = koffi.opaque('MyType');
90
+ >
91
+ > // This does not work, you must use the MyType object and not a type string
92
+ > koffi.struct('MyType', {
93
+ > ptr: 'Type2 *',
94
+ > f: 'float'
95
+ > });
96
+
62
97
  # Settings
63
98
 
64
99
  ## Memory usage
@@ -6,13 +6,16 @@
6
6
 
7
7
  Koffi uses native modules to work. The NPM package contains binaries for various platforms and architectures, and the appropriate module is selected at runtime.
8
8
 
9
+ > [!IMPORTANT]
10
+ > Please note that Koffi is meant for Node.js (or Electron) and not for browsers! It it not possible to load native libraries inside a browser!
11
+
9
12
  In theory, the **packagers/bundlers should be able to find all native modules** because they are explictly listed in the Javascript file (as static strings) and package them somehow.
10
13
 
11
14
  If that is not the case, you can manually arrange to copy the `node_modules/koffi/build/koffi` directory next to your bundled script.
12
15
 
13
16
  Here is an example that would work:
14
17
 
15
- ```
18
+ ```text
16
19
  koffi/
17
20
  win32_x64/
18
21
  koffi.node
@@ -24,7 +27,7 @@ MyBundle.js
24
27
 
25
28
  When running in Electron, Koffi will also try to find the native module in `process.resourcesPath`. For an Electron app you could do something like this
26
29
 
27
- ```
30
+ ```text
28
31
  locales/
29
32
  resources/
30
33
  koffi/
@@ -77,3 +80,9 @@ esbuild index.js --platform=node --bundle --loader:.node=copy --outdir=dist/
77
80
  ```
78
81
 
79
82
  You can find a full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/node-esbuild).
83
+
84
+ ## Node.js and yao-pkg
85
+
86
+ Use [yao-pkg](https://github.com/yao-pkg/pkg) to make binary packages of your Node.js-based project.
87
+
88
+ You can find a full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/yao-pkg).
@@ -14,16 +14,32 @@ Use [NVM](https://github.com/nvm-sh/nvm) to install more recent Node versions on
14
14
 
15
15
  The following combinations of OS and architectures __are officially supported and tested__ at the moment:
16
16
 
17
- ISA / OS | Windows | Linux | macOS | FreeBSD | OpenBSD
18
- ------------------ | ----------- | -------- | ----------- | ----------- | --------
19
- x86 (IA32) [^1] | ✅ Yes | ✅ Yes | ⬜️ *N/A* | ✅ Yes | ✅ Yes
20
- x86_64 (AMD64) | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes
21
- ARM32 LE [^2] | ⬜️ *N/A* | ✅ Yes | ⬜️ *N/A* | 🟨 Probably | 🟨 Probably
22
- ARM64 (AArch64) LE | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | 🟨 Probably
23
- RISC-V 64 [^3] | ⬜️ *N/A* | ✅ Yes | ⬜️ *N/A* | 🟨 Probably | 🟨 Probably
17
+ ISA / OS | Windows | Linux (glibc) | Linux (musl)
18
+ ------------------ | ------- | ------------- | ------------
19
+ x86 (IA32) [^1] | ✅ | ✅ | 🟨
20
+ x86_64 (AMD64) | ✅ | ✅ | ✅
21
+ ARM32 LE [^2] | ⬜️ | ✅ | 🟨
22
+ ARM64 (AArch64) LE | ✅ | ✅ | 🟨
23
+ RISC-V 64 [^3] | ⬜️ | ✅ | 🟨
24
+
25
+ <div class="legend">✅ Yes | 🟨 Probably | ⬜️ Not applicable</div>
26
+
27
+ ISA / OS | macOS | FreeBSD | OpenBSD
28
+ ------------------ | ----- | ----------- | --------
29
+ x86 (IA32) [^1] | ⬜️ | ✅ | ✅
30
+ x86_64 (AMD64) | ✅ | ✅ | ✅
31
+ ARM32 LE [^2] | ⬜️ | 🟨 | 🟨
32
+ ARM64 (AArch64) LE | ✅ | ✅ | 🟨
33
+ RISC-V 64 [^3] | ⬜️ | 🟨 | 🟨
34
+
35
+ <div class="legend">✅ Yes | 🟨 Probably | ⬜️ Not applicable</div>
24
36
 
25
37
  [^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.
26
38
  [^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).
27
39
  [^3]: 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.
28
40
 
29
41
  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.
42
+
43
+ <style>
44
+ table td:not(:first-child) { text-align: center; }
45
+ </style>
package/doc/pages.ini CHANGED
@@ -2,6 +2,7 @@
2
2
  Title = Koffi
3
3
  Menu = Overview
4
4
  Description = Koffi presentation and features
5
+ ToC = Off
5
6
  Template = templates/page.html
6
7
 
7
8
  [pages/platforms.md]
@@ -62,6 +63,12 @@ Title = Packaging | Koffi
62
63
  Menu = Documentation / Bundlers and Koffi
63
64
  Template = templates/page.html
64
65
 
66
+ [pages/migration.md]
67
+ Title = Migration | Koffi
68
+ Menu = Documentation / Migration guide
69
+ Description = Migration between major Koffi versions
70
+ Template = templates/page.html
71
+
65
72
  [pages/benchmarks.md]
66
73
  Title = Benchmarks | Koffi
67
74
  Menu = Benchmarks
@@ -77,12 +84,6 @@ Template = templates/page.html
77
84
  [changelog]
78
85
  SourceFile = ../../src/koffi/CHANGELOG.md
79
86
  Title = Changelog | Koffi
80
- Menu = Changelog / Version history
87
+ Menu = Changelog
81
88
  Description = List of Koffi versions
82
89
  Template = templates/page.html
83
-
84
- [pages/migration.md]
85
- Title = Migration | Koffi
86
- Menu = Changelog / Migration guide
87
- Description = Migration between major Koffi versions
88
- Template = templates/page.html
Binary file
Binary file
@@ -20,7 +20,7 @@
20
20
 
21
21
  <nav id="top">
22
22
  <menu>
23
- <a id="logo" href="/"><img src="{{ ASSET favicon.png }}" alt="Logo Rekkord" /></a>
23
+ <a id="logo" href="/"><img src="{{ ASSET static/logo.webp }}" alt="Logo Koffi" /></a>
24
24
 
25
25
  {{ LINKS }}
26
26
 
@@ -36,5 +36,13 @@
36
36
  <main>
37
37
  {{ CONTENT }}
38
38
  </main>
39
+
40
+ <footer>
41
+ <div>Koffi © 2024</div>
42
+ <div style="font-size: 0.8em;">
43
+ Niels Martignène (<a href="https://github.com/Koromix/" target="_blank">Koromix</a>)<br>
44
+ <a href="mailto:niels.martignene@protonmail.com" style="font-weight: bold; color: inherit;">niels.martignene@protonmail.com</a>
45
+ </div>
46
+ </footer>
39
47
  </body>
40
48
  </html>
package/index.js CHANGED
@@ -363,8 +363,8 @@ var require_package = __commonJS({
363
363
  "../../bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
364
364
  module2.exports = {
365
365
  name: "koffi",
366
- version: "2.9.2",
367
- stable: "2.9.2",
366
+ version: "2.10.0",
367
+ stable: "2.10.0",
368
368
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
369
369
  keywords: [
370
370
  "foreign",
package/indirect.js CHANGED
@@ -363,8 +363,8 @@ var require_package = __commonJS({
363
363
  "../../bin/Koffi/package/src/koffi/package.json"(exports2, module2) {
364
364
  module2.exports = {
365
365
  name: "koffi",
366
- version: "2.9.2",
367
- stable: "2.9.2",
366
+ version: "2.10.0",
367
+ stable: "2.10.0",
368
368
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
369
369
  keywords: [
370
370
  "foreign",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.9.2",
4
- "stable": "2.9.2",
3
+ "version": "2.10.0",
4
+ "stable": "2.10.0",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -48,6 +48,12 @@ else()
48
48
  endif()
49
49
  endif()
50
50
 
51
+ if(UNIX AND NOT APPLE)
52
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64")
53
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FILE_OFFSET_BITS=64")
54
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z noexecstack")
55
+ endif()
56
+
51
57
  # ---- Koffi ----
52
58
 
53
59
  # Recompute the version string after each commit
@@ -0,0 +1,17 @@
1
+ This is a simple example to bundle a CLI node.js app that uses Koffi, using [yao-pkg](https://github.com/yao-pkg/pkg).
2
+
3
+ To run the app, execute the following:
4
+
5
+ ```sh
6
+ cd examples/yao-pkg
7
+ npm install
8
+ npm start
9
+ ```
10
+
11
+ You can bundle the script and the native modules with the following command
12
+
13
+ ```sh
14
+ cd examples/yao-pkg
15
+ npm install
16
+ npm run bundle
17
+ ```
@@ -0,0 +1,2 @@
1
+ const koffi = require('koffi');
2
+ console.log(koffi.config());
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "KoffiConfig",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node index.js",
8
+ "bundle": "pkg ."
9
+ },
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "koffi": "^2.5.18"
14
+ },
15
+ "devDependencies": {
16
+ "@yao-pkg/pkg": "^6.1.1"
17
+ },
18
+ "bin": "index.js",
19
+ "pkg": {
20
+ "outputPath": "dist"
21
+ }
22
+ }
@@ -244,8 +244,9 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
244
244
  }
245
245
 
246
246
  bool named = info.Length() > 1;
247
+ bool redefine = named && CheckValueTag(instance, info[0], &TypeInfoMarker);
247
248
 
248
- if (named && !info[0].IsString()) {
249
+ if (named && !info[0].IsString() && !redefine) {
249
250
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
250
251
  return env.Null();
251
252
  }
@@ -274,8 +275,20 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
274
275
  };
275
276
 
276
277
  TypeInfo *type = instance->types.AppendDefault();
278
+ TypeInfo *replace = nullptr;
277
279
 
278
- if (named) {
280
+ if (redefine) {
281
+ Napi::External<TypeInfo> external = name.As<Napi::External<TypeInfo>>();
282
+ const TypeInfo *raw = external.Data();
283
+
284
+ replace = (TypeInfo *)AlignDown(raw, 4);
285
+ type->name = replace->name;
286
+
287
+ if (replace->primitive != PrimitiveKind::Void || replace == instance->void_type) {
288
+ ThrowError<Napi::TypeError>(env, "Cannot redefine non-opaque type %1", replace->name);
289
+ return env.Null();
290
+ }
291
+ } else if (named) {
279
292
  type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
280
293
 
281
294
  if (!MapType(env, instance, type, type->name))
@@ -366,6 +379,11 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
366
379
  type->flags &= ~(int)TypeFlag::IsIncomplete;
367
380
  err_guard.Disable();
368
381
 
382
+ if (replace) {
383
+ std::swap(*type, *replace);
384
+ type = replace;
385
+ }
386
+
369
387
  return WrapType(env, instance, type);
370
388
  }
371
389
 
@@ -390,8 +408,9 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
390
408
  }
391
409
 
392
410
  bool named = info.Length() > 1;
411
+ bool redefine = named && CheckValueTag(instance, info[0], &TypeInfoMarker);
393
412
 
394
- if (named && !info[0].IsString()) {
413
+ if (named && !info[0].IsString() && !redefine) {
395
414
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
396
415
  return env.Null();
397
416
  }
@@ -420,8 +439,20 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
420
439
  };
421
440
 
422
441
  TypeInfo *type = instance->types.AppendDefault();
442
+ TypeInfo *replace = nullptr;
423
443
 
424
- if (named) {
444
+ if (redefine) {
445
+ Napi::External<TypeInfo> external = name.As<Napi::External<TypeInfo>>();
446
+ const TypeInfo *raw = external.Data();
447
+
448
+ replace = (TypeInfo *)AlignDown(raw, 4);
449
+ type->name = replace->name;
450
+
451
+ if (replace->primitive != PrimitiveKind::Void || replace == instance->void_type) {
452
+ ThrowError<Napi::TypeError>(env, "Cannot redefine non-opaque type %1", replace->name);
453
+ return env.Null();
454
+ }
455
+ } else if (named) {
425
456
  type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
426
457
 
427
458
  if (!MapType(env, instance, type, type->name))
@@ -507,6 +538,11 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
507
538
  Napi::Function constructor = MagicUnion::InitClass(env, type);
508
539
  type->construct.Reset(constructor, 1);
509
540
 
541
+ if (replace) {
542
+ std::swap(*type, *replace);
543
+ type = replace;
544
+ }
545
+
510
546
  return WrapType(env, instance, type);
511
547
  }
512
548
 
@@ -146,8 +146,8 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
146
146
  return type;
147
147
  } else if (CheckValueTag(instance, value, &TypeInfoMarker)) {
148
148
  Napi::External<TypeInfo> external = value.As<Napi::External<TypeInfo>>();
149
-
150
149
  const TypeInfo *raw = external.Data();
150
+
151
151
  const TypeInfo *type = AlignDown(raw, 4);
152
152
  RG_ASSERT(type);
153
153