koffi 2.6.11 → 2.7.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.
package/CHANGELOG.md CHANGED
@@ -2,8 +2,20 @@
2
2
 
3
3
  ## Version history
4
4
 
5
+ ### Koffi 2.7
6
+
7
+ #### Koffi 2.7.0 (2023-12-21)
8
+
9
+ - Support alternative [callback calling convention](callbacks.md#callback-types) in classic syntax
10
+ - Change syntax for [calling conventions](functions.md#calling-conventions) with classic syntax
11
+ - Drop unused "internal" property from Koffi
12
+
5
13
  ### Koffi 2.6
6
14
 
15
+ #### Koffi 2.6.12 (2023-12-11)
16
+
17
+ - Fix possible crash introduced in Koffi 2.6.11
18
+
7
19
  #### Koffi 2.6.11 (2023-12-05)
8
20
 
9
21
  - Speed up resolving simple and often used type names
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/callbacks.md CHANGED
@@ -8,6 +8,8 @@ The function `koffi.proto()` was introduced in Koffi 2.4, it was called `koffi.c
8
8
 
9
9
  ## Callback types
10
10
 
11
+ *Changed in Koffi 2.7*
12
+
11
13
  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.
12
14
 
13
15
  ```js
@@ -21,6 +23,23 @@ const ExampleCallback = koffi.proto('ExampleCallback', 'void', ['int']);
21
23
  const AddDoubleFloat = koffi.proto('double AddDoubleFloat(double d, float f)');
22
24
  ```
23
25
 
26
+ For alternative [calling conventions](functions.md#calling-conventions) (such as `stdcall` on Windows x86 32-bit), you can specify as the first argument with the classic syntax, or after the return type in prototype strings, like this:
27
+
28
+ ```js
29
+ const HANDLE = koffi.pointer('HANDLE', koffi.opaque());
30
+ const HWND = koffi.alias('HWND', HANDLE);
31
+
32
+ // These two declarations work the same, and use the __stdcall convention on Windows x86
33
+ const EnumWindowsProc = koffi.proto('bool __stdcall EnumWindowsProc (HWND hwnd, long lParam)');
34
+ const EnumWindowsProc = koffi.proto('__stdcall', 'EnumWindowsProc', 'bool', ['HWND', 'long']);
35
+ ```
36
+
37
+ ```{warning}
38
+ You have to make sure you **get the calling convention right** (such as specifying __stdcall for a Windows API callback), or your code will crash on Windows 32-bit.
39
+
40
+ Before Koffi 2.7, it was *impossible to use an alternative callback calling convention with the classic syntax*. Use a prototype string or *upgrade to Koffi 2.7* to solve this limitation.
41
+ ```
42
+
24
43
  Once your callback type is declared, you can use a pointer to it in struct definitions, as function parameters and/or return types, or to call/decode function pointers.
25
44
 
26
45
  ```{note}
package/doc/functions.md CHANGED
@@ -83,19 +83,27 @@ On x86 platforms, only the Cdecl convention can be used for variadic functions.
83
83
 
84
84
  ### Calling conventions
85
85
 
86
+ *Changed in Koffi 2.7*
87
+
86
88
  By default, calling a C function happens synchronously.
87
89
 
88
90
  Most architectures only support one procedure call standard per process. The 32-bit x86 platform is an exception to this, and Koffi supports several x86 conventions:
89
91
 
90
- Convention | Classic form | Prototype form | Description
91
- ------------- | ----------------------------- | -------------- | -------------------------------------------------------------------
92
- **Cdecl** | `koffi.cdecl` or `koffi.func` | _(default)_ | This is the default convention, and the only one on other platforms
93
- **Stdcall** | `koffi.stdcall` | __stdcall | This convention is used extensively within the Win32 API
94
- **Fastcall** | `koffi.fastcall` | __fastcall | Rarely used, uses ECX and EDX for first two parameters
95
- **Thiscall** | `koffi.thiscall` | __thiscall | Rarely used, uses ECX for first parameter
92
+ Convention | Classic form | Prototype form | Description
93
+ ------------- | --------------------------------------------- | -------------- | -------------------------------------------------------------------
94
+ **Cdecl** | `koffi.func(name, ret, params)` | _(default)_ | This is the default convention, and the only one on other platforms
95
+ **Stdcall** | `koffi.func('__stdcall', name, ret, params)` | __stdcall | This convention is used extensively within the Win32 API
96
+ **Fastcall** | `koffi.func('__fastcall', name, ret, params)` | __fastcall | Rarely used, uses ECX and EDX for first two parameters
97
+ **Thiscall** | `koffi.func('__thiscall', name, ret, params)` | __thiscall | Rarely used, uses ECX for first parameter
96
98
 
97
99
  You can safely use these on non-x86 platforms, they are simply ignored.
98
100
 
101
+ ```{note}
102
+ Support for specifying the convention as the first argument of the classic form was introduced in Koffi 2.7.
103
+
104
+ In earlier versions, you had to use `koffi.stdcall()` and similar functions. These functions are still supported but deprecated, and will be removed in Koffi 3.0.
105
+ ```
106
+
99
107
  Below you can find a small example showing how to use a non-default calling convention, with the two syntaxes:
100
108
 
101
109
  ```js
@@ -105,7 +113,7 @@ const koffi = require('koffi');
105
113
  const lib = koffi.load('user32.dll');
106
114
 
107
115
  // The following two declarations are equivalent, and use stdcall on x86 (and the default ABI on other platforms)
108
- const MessageBoxA_1 = lib.stdcall('MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
116
+ const MessageBoxA_1 = lib.func('__stdcall', 'MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
109
117
  const MessageBoxA_2 = lib.func('int __stdcall MessageBoxA(void *hwnd, str text, str caption, uint type)');
110
118
  ```
111
119
 
package/doc/start.md CHANGED
@@ -99,8 +99,8 @@ const IDYES = 6;
99
99
  const IDNO = 7;
100
100
 
101
101
  // Find functions
102
- const MessageBoxA = lib.stdcall('MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
103
- const MessageBoxW = lib.stdcall('MessageBoxW', 'int', ['void *', 'str16', 'str16', 'uint']);
102
+ const MessageBoxA = lib.func('__stdcall', 'MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
103
+ const MessageBoxW = lib.func('__stdcall', 'MessageBoxW', 'int', ['void *', 'str16', 'str16', 'uint']);
104
104
 
105
105
  let ret = MessageBoxA(null, 'Do you want another message box?', 'Koffi', MB_YESNO | MB_ICONQUESTION);
106
106
  if (ret == IDYES)
package/index.d.ts CHANGED
@@ -67,18 +67,16 @@ declare module 'koffi' {
67
67
  export interface IKoffiLib {
68
68
  func(definition: string): KoffiFunction;
69
69
  func(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
70
+ func(convention: string, name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
70
71
 
71
- cdecl(definition: string): KoffiFunction;
72
- cdecl(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
73
-
74
- stdcall(definition: string): KoffiFunction;
75
- stdcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
76
-
77
- fastcall(definition: string): KoffiFunction;
78
- fastcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
79
-
80
- thiscall(definition: string): KoffiFunction;
81
- thiscall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
72
+ /** @deprecated */ cdecl(definition: string): KoffiFunction;
73
+ /** @deprecated */ cdecl(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
74
+ /** @deprecated */ stdcall(definition: string): KoffiFunction;
75
+ /** @deprecated */ stdcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
76
+ /** @deprecated */ fastcall(definition: string): KoffiFunction;
77
+ /** @deprecated */ fastcall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
78
+ /** @deprecated */ thiscall(definition: string): KoffiFunction;
79
+ /** @deprecated */ thiscall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
82
80
 
83
81
  symbol(name: string, type: TypeSpec): any;
84
82
 
@@ -118,8 +116,10 @@ declare module 'koffi' {
118
116
 
119
117
  export function proto(definition: string): IKoffiCType;
120
118
  export function proto(name: string, result: TypeSpec, arguments: TypeSpec[]): IKoffiCType;
119
+ export function proto(convention: string, name: string, result: TypeSpec, arguments: TypeSpec[]): IKoffiCType;
121
120
  /** @deprecated */ export function callback(definition: string): IKoffiCType;
122
121
  /** @deprecated */ export function callback(name: string, result: TypeSpec, arguments: TypeSpec[]): IKoffiCType;
122
+ /** @deprecated */ export function callback(convention: string, name: string, result: TypeSpec, arguments: TypeSpec[]): IKoffiCType;
123
123
 
124
124
  export function register(callback: Function, type: TypeSpec): IKoffiRegisteredCallback;
125
125
  export function register(thisValue: any, callback: Function, type: TypeSpec): IKoffiRegisteredCallback;
package/index.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.6.11",
382
- stable: "2.6.11",
381
+ version: "2.7.0",
382
+ stable: "2.7.0",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -557,3 +557,12 @@ module.exports = {
557
557
  handle: util.deprecate(native.opaque, "The koffi.handle() function was deprecated in Koffi 2.1, use koffi.opaque() instead", "KOFFI001"),
558
558
  callback: util.deprecate(native.proto, "The koffi.callback() function was deprecated in Koffi 2.4, use koffi.proto() instead", "KOFFI002")
559
559
  };
560
+ var load = module.exports.load;
561
+ module.exports.load = (...args) => {
562
+ let lib = load(...args);
563
+ lib.cdecl = util.deprecate((...args2) => lib.func("__cdecl", ...args2), "The koffi.stdcall() function was deprecated in Koffi 2.7, use koffi.func(...) instead", "KOFFI003");
564
+ lib.stdcall = util.deprecate((...args2) => lib.func("__stdcall", ...args2), 'The koffi.stdcall() function was deprecated in Koffi 2.7, use koffi.func("__stdcall", ...) instead', "KOFFI004");
565
+ lib.fastcall = util.deprecate((...args2) => lib.func("__fastcall", ...args2), 'The koffi.fastcall() function was deprecated in Koffi 2.7, use koffi.func("__fastcall", ...) instead', "KOFFI005");
566
+ lib.thiscall = util.deprecate((...args2) => lib.func("__thiscall", ...args2), 'The koffi.thiscall() function was deprecated in Koffi 2.7, use koffi.func("__thiscall", ...) instead', "KOFFI006");
567
+ return lib;
568
+ };
package/indirect.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.6.11",
382
- stable: "2.6.11",
381
+ version: "2.7.0",
382
+ stable: "2.7.0",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -477,3 +477,12 @@ module.exports = {
477
477
  handle: util.deprecate(native.opaque, "The koffi.handle() function was deprecated in Koffi 2.1, use koffi.opaque() instead", "KOFFI001"),
478
478
  callback: util.deprecate(native.proto, "The koffi.callback() function was deprecated in Koffi 2.4, use koffi.proto() instead", "KOFFI002")
479
479
  };
480
+ var load = module.exports.load;
481
+ module.exports.load = (...args) => {
482
+ let lib = load(...args);
483
+ lib.cdecl = util.deprecate((...args2) => lib.func("__cdecl", ...args2), "The koffi.stdcall() function was deprecated in Koffi 2.7, use koffi.func(...) instead", "KOFFI003");
484
+ lib.stdcall = util.deprecate((...args2) => lib.func("__stdcall", ...args2), 'The koffi.stdcall() function was deprecated in Koffi 2.7, use koffi.func("__stdcall", ...) instead', "KOFFI004");
485
+ lib.fastcall = util.deprecate((...args2) => lib.func("__fastcall", ...args2), 'The koffi.fastcall() function was deprecated in Koffi 2.7, use koffi.func("__fastcall", ...) instead', "KOFFI005");
486
+ lib.thiscall = util.deprecate((...args2) => lib.func("__thiscall", ...args2), 'The koffi.thiscall() function was deprecated in Koffi 2.7, use koffi.func("__thiscall", ...) instead', "KOFFI006");
487
+ return lib;
488
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.6.11",
4
- "stable": "2.6.11",
3
+ "version": "2.7.0",
4
+ "stable": "2.7.0",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -5599,7 +5599,6 @@ public:
5599
5599
  AsyncPool(int threads, bool leak);
5600
5600
 
5601
5601
  int GetWorkerCount() const { return (int)workers.len; }
5602
- int CountPendingTasks() const { return pending_tasks; }
5603
5602
 
5604
5603
  void RegisterAsync();
5605
5604
  void UnregisterAsync();
@@ -5673,11 +5672,6 @@ bool Async::Sync()
5673
5672
  return success;
5674
5673
  }
5675
5674
 
5676
- int Async::CountPendingTasks()
5677
- {
5678
- return pool->CountPendingTasks();
5679
- }
5680
-
5681
5675
  bool Async::IsTaskRunning()
5682
5676
  {
5683
5677
  return async_running_task;
@@ -5806,6 +5800,11 @@ void AsyncPool::AddTask(Async *async, const std::function<bool()> &func)
5806
5800
  pending_cv.notify_all();
5807
5801
  sync_cv.notify_all();
5808
5802
  }
5803
+
5804
+ // Limit queue size (back pressure)
5805
+ while (pending_tasks >= RG_ASYNC_MAX_PENDING_TASKS) {
5806
+ RunTasks(0);
5807
+ }
5809
5808
  }
5810
5809
 
5811
5810
  void AsyncPool::RunWorker(int worker_idx)
@@ -8372,4 +8371,72 @@ bool PromptYN(const char *prompt, bool *out_value)
8372
8371
  return prompter.ReadYN(out_value);
8373
8372
  }
8374
8373
 
8374
+ // ------------------------------------------------------------------------
8375
+ // Mime types
8376
+ // ------------------------------------------------------------------------
8377
+
8378
+ const char *GetMimeType(Span<const char> extension, const char *default_type)
8379
+ {
8380
+ static const HashMap<Span<const char>, const char *> mime_types = {
8381
+ #define MIMETYPE(Extension, MimeType) { (Extension), (MimeType) },
8382
+ #include "mimetypes.inc"
8383
+
8384
+ {"", "application/octet-stream"}
8385
+ };
8386
+
8387
+ const char *mime_type = mime_types.FindValue(extension, nullptr);
8388
+
8389
+ if (!mime_type) {
8390
+ LogError("Unknown MIME type for extension '%1'", extension);
8391
+ mime_type = default_type;
8392
+ }
8393
+
8394
+ return mime_type;
8395
+ }
8396
+
8397
+ bool CanCompressFile(const char *filename)
8398
+ {
8399
+ char extension[8];
8400
+ {
8401
+ const char *ptr = GetPathExtension(filename).ptr;
8402
+
8403
+ Size i = 0;
8404
+ while (i < RG_SIZE(extension) - 1 && ptr[i]) {
8405
+ extension[i] = LowerAscii(ptr[i]);
8406
+ i++;
8407
+ }
8408
+ extension[i] = 0;
8409
+ }
8410
+
8411
+ if (TestStrI(extension, ".zip"))
8412
+ return false;
8413
+ if (TestStrI(extension, ".rar"))
8414
+ return false;
8415
+ if (TestStrI(extension, ".7z"))
8416
+ return false;
8417
+ if (TestStrI(extension, ".gz") || TestStrI(extension, ".tgz"))
8418
+ return false;
8419
+ if (TestStrI(extension, ".bz2") || TestStrI(extension, ".tbz2"))
8420
+ return false;
8421
+ if (TestStrI(extension, ".xz") || TestStrI(extension, ".txz"))
8422
+ return false;
8423
+ if (TestStrI(extension, ".zst") || TestStrI(extension, ".tzst"))
8424
+ return false;
8425
+ if (TestStrI(extension, ".woff") || TestStrI(extension, ".woff2"))
8426
+ return false;
8427
+ if (TestStrI(extension, ".db") || TestStrI(extension, ".sqlite3"))
8428
+ return false;
8429
+
8430
+ const char *mime_type = GetMimeType(extension);
8431
+
8432
+ if (StartsWith(mime_type, "video/"))
8433
+ return false;
8434
+ if (StartsWith(mime_type, "audio/"))
8435
+ return false;
8436
+ if (StartsWith(mime_type, "image/") && !TestStr(mime_type, "image/svg+xml"))
8437
+ return false;
8438
+
8439
+ return true;
8440
+ }
8441
+
8375
8442
  }
@@ -93,6 +93,8 @@ namespace RG {
93
93
 
94
94
  #define RG_ASYNC_MAX_THREADS 2048
95
95
  #define RG_ASYNC_MAX_IDLE_TIME 10000
96
+ #define RG_ASYNC_MAX_PENDING_TASKS 1024
97
+
96
98
  #define RG_FIBER_DEFAULT_STACK_SIZE Kibibytes(128)
97
99
 
98
100
  // ------------------------------------------------------------------------
@@ -4265,8 +4267,6 @@ public:
4265
4267
  void Run(const std::function<bool()> &f);
4266
4268
  bool Sync();
4267
4269
 
4268
- int CountPendingTasks();
4269
-
4270
4270
  static bool IsTaskRunning();
4271
4271
  static int GetWorkerIdx();
4272
4272
  static int GetWorkerCount();
@@ -5016,4 +5016,12 @@ static inline const char *Prompt(const char *prompt, Allocator *alloc)
5016
5016
  { return Prompt(prompt, nullptr, nullptr, alloc); }
5017
5017
  bool PromptYN(const char *prompt, bool *out_value);
5018
5018
 
5019
+ // ------------------------------------------------------------------------
5020
+ // Mime types
5021
+ // ------------------------------------------------------------------------
5022
+
5023
+ const char *GetMimeType(Span<const char> extension, const char *default_typ = "application/octet-stream");
5024
+
5025
+ bool CanCompressFile(const char *filename);
5026
+
5019
5027
  }