koffi 2.5.10 → 2.5.12
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 +78 -68
- package/build/koffi/darwin_arm64/koffi.node +0 -0
- package/build/koffi/darwin_x64/koffi.node +0 -0
- package/build/koffi/freebsd_arm64/koffi.node +0 -0
- package/build/koffi/freebsd_ia32/koffi.node +0 -0
- package/build/koffi/freebsd_x64/koffi.node +0 -0
- package/build/koffi/linux_arm32hf/koffi.node +0 -0
- package/build/koffi/linux_arm64/koffi.node +0 -0
- package/build/koffi/linux_ia32/koffi.node +0 -0
- package/build/koffi/linux_riscv64hf64/koffi.node +0 -0
- package/build/koffi/linux_x64/koffi.node +0 -0
- package/build/koffi/openbsd_ia32/koffi.node +0 -0
- package/build/koffi/openbsd_x64/koffi.node +0 -0
- package/build/koffi/win32_arm64/koffi.node +0 -0
- package/build/koffi/win32_ia32/koffi.node +0 -0
- package/build/koffi/win32_x64/koffi.node +0 -0
- package/doc/callbacks.md +1 -1
- package/doc/conf.py +2 -1
- package/doc/functions.md +11 -0
- package/doc/index.rst +5 -2
- package/doc/{types.md → input.md} +43 -39
- package/doc/migration.md +4 -4
- package/doc/output.md +169 -0
- package/doc/packaging.md +1 -1
- package/doc/pointers.md +52 -2
- package/doc/polymorphism.md +108 -0
- package/package.json +7 -3
- package/src/cnoke/package.json +5 -1
- package/src/core/libcc/libcc.cc +114 -4
- package/src/core/libcc/libcc.hh +44 -16
- package/src/index.js +2 -2
- package/src/koffi/CMakeLists.txt +1 -1
- package/src/koffi/src/call.cc +16 -6
- package/src/koffi/src/call.hh +1 -1
- package/src/koffi/src/ffi.cc +6 -5
- package/src/koffi/src/ffi.hh +1 -1
- package/src/koffi/src/util.cc +1 -1
- package/doc/parameters.md +0 -336
package/doc/migration.md
CHANGED
|
@@ -10,7 +10,7 @@ You may need to change your code if you use:
|
|
|
10
10
|
- Opaque types
|
|
11
11
|
- `koffi.introspect()`
|
|
12
12
|
|
|
13
|
-
### Callback
|
|
13
|
+
### Callback type changes
|
|
14
14
|
|
|
15
15
|
In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer. Now, you must use them through a pointer: `void CallIt(CallbackType func)` in Koffi 1.x becomes `void CallIt(CallbackType *func)` in version 2.0 and newer.
|
|
16
16
|
|
|
@@ -61,13 +61,13 @@ let ret = TransferToJS('Niels', 27, (str, age) => {
|
|
|
61
61
|
console.log(ret);
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Koffi 1.x only supported [transient callbacks](callbacks.md#callbacks), you must use Koffi 2.x for registered callbacks.
|
|
64
|
+
Koffi 1.x only supported [transient callbacks](callbacks.md#javascript-callbacks), you must use Koffi 2.x for registered callbacks.
|
|
65
65
|
|
|
66
66
|
```{note}
|
|
67
67
|
The function `koffi.proto()` was introduced in Koffi 2.4, it was called `koffi.callback()` in earlier versions.
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
### Opaque
|
|
70
|
+
### Opaque type changes
|
|
71
71
|
|
|
72
72
|
In Koffi 1.x, opaque handles were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer. Now, in Koffi 2.0, you must use them through a pointer, and use an array for output parameters.
|
|
73
73
|
|
|
@@ -145,7 +145,7 @@ db = ptr[0];
|
|
|
145
145
|
sqlite3_close_v2(db);
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
### koffi.introspect()
|
|
148
|
+
### New koffi.introspect()
|
|
149
149
|
|
|
150
150
|
In Koffi 1.x, `koffi.introspect()` would only work with struct types, and return the object passed to `koffi.struct()` to initialize the type. Now this function works with all types.
|
|
151
151
|
|
package/doc/output.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Output parameters
|
|
2
|
+
|
|
3
|
+
## Output and input/output
|
|
4
|
+
|
|
5
|
+
For simplicity, and because Javascript only has value semantics for primitive types, Koffi can marshal out (or in/out) multiple types of parameters:
|
|
6
|
+
|
|
7
|
+
- [Structs](input.md#struct-types) (to/from JS objects)
|
|
8
|
+
- [Unions](unions.md)
|
|
9
|
+
- [Opaque types](input.md#opaque-types)
|
|
10
|
+
- String buffers
|
|
11
|
+
|
|
12
|
+
In order to change an argument from input-only to output or input/output, use the following functions:
|
|
13
|
+
|
|
14
|
+
- `koffi.out()` on a pointer, e.g. `koffi.out(koffi.pointer(timeval))` (where timeval is a struct type)
|
|
15
|
+
- `koffi.inout()` for dual input/output parameters
|
|
16
|
+
|
|
17
|
+
The same can be done when declaring a function with a C-like prototype string, with the MSDN-like type qualifiers:
|
|
18
|
+
|
|
19
|
+
- `_Out_` for output parameters
|
|
20
|
+
- `_Inout_` for dual input/output parameters
|
|
21
|
+
|
|
22
|
+
### Primitive value
|
|
23
|
+
|
|
24
|
+
This Windows example enumerate all Chrome windows along with their PID and their title. The `GetWindowThreadProcessId()` function illustrates how to get a primitive value from an output argument.
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
28
|
+
const koffi = require('koffi');
|
|
29
|
+
|
|
30
|
+
const user32 = koffi.load('user32.dll');
|
|
31
|
+
|
|
32
|
+
const DWORD = koffi.alias('DWORD', 'uint32_t');
|
|
33
|
+
const HANDLE = koffi.pointer(koffi.opaque('HANDLE'));
|
|
34
|
+
const HWND = koffi.alias('HWND', HANDLE);
|
|
35
|
+
|
|
36
|
+
const FindWindowEx = user32.func('HWND __stdcall FindWindowExW(HWND hWndParent, HWND hWndChildAfter, const char16_t *lpszClass, const char16_t *lpszWindow)');
|
|
37
|
+
const GetWindowThreadProcessId = user32.func('DWORD __stdcall GetWindowThreadProcessId(HWND hWnd, _Out_ DWORD *lpdwProcessId)');
|
|
38
|
+
const GetWindowText = user32.func('int __stdcall GetWindowTextA(HWND hWnd, _Out_ uint8_t *lpString, int nMaxCount)');
|
|
39
|
+
|
|
40
|
+
for (let hwnd = null;;) {
|
|
41
|
+
hwnd = FindWindowEx(0, hwnd, 'Chrome_WidgetWin_1', null);
|
|
42
|
+
|
|
43
|
+
if (!hwnd)
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
// Get PID
|
|
47
|
+
let pid;
|
|
48
|
+
{
|
|
49
|
+
let ptr = [null];
|
|
50
|
+
let tid = GetWindowThreadProcessId(hwnd, ptr);
|
|
51
|
+
|
|
52
|
+
if (!tid) {
|
|
53
|
+
// Maybe the process ended in-between?
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pid = ptr[0];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Get window title
|
|
61
|
+
let title;
|
|
62
|
+
{
|
|
63
|
+
let buf = Buffer.allocUnsafe(1024);
|
|
64
|
+
let length = GetWindowText(hwnd, buf, buf.length);
|
|
65
|
+
|
|
66
|
+
if (!length) {
|
|
67
|
+
// Maybe the process ended in-between?
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
title = koffi.decode(buf, 'char', length);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log({ PID: pid, Title: title });
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Struct example
|
|
79
|
+
|
|
80
|
+
This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
84
|
+
const koffi = require('koffi');
|
|
85
|
+
|
|
86
|
+
const lib = koffi.load('libc.so.6');
|
|
87
|
+
|
|
88
|
+
const timeval = koffi.struct('timeval', {
|
|
89
|
+
tv_sec: 'unsigned int',
|
|
90
|
+
tv_usec: 'unsigned int'
|
|
91
|
+
});
|
|
92
|
+
const timezone = koffi.struct('timezone', {
|
|
93
|
+
tz_minuteswest: 'int',
|
|
94
|
+
tz_dsttime: 'int'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// The _Out_ qualifiers instruct Koffi to marshal out the values
|
|
98
|
+
const gettimeofday = lib.func('int gettimeofday(_Out_ timeval *tv, _Out_ timezone *tz)');
|
|
99
|
+
|
|
100
|
+
let tv = {};
|
|
101
|
+
gettimeofday(tv, null);
|
|
102
|
+
|
|
103
|
+
console.log(tv);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Opaque type example
|
|
107
|
+
|
|
108
|
+
This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
112
|
+
const koffi = require('koffi');
|
|
113
|
+
|
|
114
|
+
const lib = koffi.load('sqlite3.so');
|
|
115
|
+
|
|
116
|
+
const sqlite3 = koffi.opaque('sqlite3');
|
|
117
|
+
|
|
118
|
+
// Use koffi.out() on a double pointer to copy out (from C to JS) after the call
|
|
119
|
+
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);
|
|
120
|
+
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3)]);
|
|
121
|
+
|
|
122
|
+
const SQLITE_OPEN_READWRITE = 0x2;
|
|
123
|
+
const SQLITE_OPEN_CREATE = 0x4;
|
|
124
|
+
|
|
125
|
+
let out = [null];
|
|
126
|
+
if (sqlite3_open_v2(':memory:', out, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
127
|
+
throw new Error('Failed to open database');
|
|
128
|
+
let db = out[0];
|
|
129
|
+
|
|
130
|
+
sqlite3_close_v2(db);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### String buffer example
|
|
134
|
+
|
|
135
|
+
*New in Koffi 2.2*
|
|
136
|
+
|
|
137
|
+
This example calls a C function to concatenate two strings to a pre-allocated string buffer. Since JS strings are immutable, you must pass an array with a single string instead.
|
|
138
|
+
|
|
139
|
+
```c
|
|
140
|
+
void ConcatToBuffer(const char *str1, const char *str2, char *out)
|
|
141
|
+
{
|
|
142
|
+
size_t len = 0;
|
|
143
|
+
|
|
144
|
+
for (size_t i = 0; str1[i]; i++) {
|
|
145
|
+
out[len++] = str1[i];
|
|
146
|
+
}
|
|
147
|
+
for (size_t i = 0; str2[i]; i++) {
|
|
148
|
+
out[len++] = str2[i];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
out[len] = 0;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
const ConcatToBuffer = lib.func('void ConcatToBuffer(const char *str1, const char *str2, _Out_ char *out)');
|
|
157
|
+
|
|
158
|
+
let str1 = 'Hello ';
|
|
159
|
+
let str2 = 'Friends!';
|
|
160
|
+
|
|
161
|
+
// We need to reserve space for the output buffer! Including the NUL terminator
|
|
162
|
+
// because ConcatToBuffer() expects so, but Koffi can convert back to a JS string
|
|
163
|
+
// without it (if we reserve the right size).
|
|
164
|
+
let out = ['\0'.repeat(str1.length + str2.length + 1)];
|
|
165
|
+
|
|
166
|
+
ConcatToBuffer(str1, str2, out);
|
|
167
|
+
|
|
168
|
+
console.log(out[0]);
|
|
169
|
+
```
|
package/doc/packaging.md
CHANGED
package/doc/pointers.md
CHANGED
|
@@ -130,13 +130,63 @@ let total = ComputeTotalLength(strings);
|
|
|
130
130
|
console.log(total); // Prints 14
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
By default, just like for objects, array arguments are copied from JS to C but not vice-versa. You can however change the direction as documented in the section on [output parameters](
|
|
133
|
+
By default, just like for objects, array arguments are copied from JS to C but not vice-versa. You can however change the direction as documented in the section on [output parameters](output.md).
|
|
134
134
|
|
|
135
135
|
## Disposable types
|
|
136
136
|
|
|
137
|
+
*New in Koffi 2.0*
|
|
138
|
+
|
|
137
139
|
Disposable types allow you to register a function that will automatically called after each C to JS conversion performed by Koffi. This can be used to avoid leaking heap-allocated strings, for example.
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
Some C functions return heap-allocated values directly or through output parameters. While Koffi automatically converts values from C to JS (to a string or an object), it does not know when something needs to be freed, or how.
|
|
142
|
+
|
|
143
|
+
For opaque types, such as FILE, this does not matter because you will explicitly call `fclose()` on them. But some values (such as strings) get implicitly converted by Koffi, and you lose access to the original pointer. This creates a leak if the string is heap-allocated.
|
|
144
|
+
|
|
145
|
+
To avoid this, you can instruct Koffi to call a function on the original pointer once the conversion is done, by creating a **disposable type** with `koffi.dispose(name, type, func)`. This creates a type derived from another type, the only difference being that *func* gets called with the original pointer once the value has been converted and is not needed anymore.
|
|
146
|
+
|
|
147
|
+
The *name* can be omitted to create an anonymous disposable type. If *func* is omitted or is null, Koffi will use `koffi.free(ptr)` (which calls the standard C library *free* function under the hood).
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const AnonHeapStr = koffi.disposable('str'); // Anonymous type (cannot be used in function prototypes)
|
|
151
|
+
const NamedHeapStr = koffi.disposable('HeapStr', 'str'); // Same thing, but named so usable in function prototypes
|
|
152
|
+
const ExplicitFree = koffi.disposable('HeapStr16', 'str16', koffi.free); // You can specify any other JS function
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The following example illustrates the use of a disposable type derived from *str*.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
159
|
+
const koffi = require('koffi');
|
|
160
|
+
|
|
161
|
+
const lib = koffi.load('libc.so.6');
|
|
162
|
+
|
|
163
|
+
const HeapStr = koffi.disposable('str');
|
|
164
|
+
const strdup = lib.cdecl('strdup', HeapStr, ['str']);
|
|
165
|
+
|
|
166
|
+
let copy = strdup('Hello!');
|
|
167
|
+
console.log(copy); // Prints Hello!
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
When you declare functions with the [prototype-like syntax](functions.md#definition-syntax), you can either use named disposable types or use the '!' shortcut qualifier with compatibles types, as shown in the example below. This qualifier creates an anonymous disposable type that calls `koffi.free(ptr)`.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
174
|
+
const koffi = require('koffi');
|
|
175
|
+
|
|
176
|
+
const lib = koffi.load('libc.so.6');
|
|
177
|
+
|
|
178
|
+
// You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
|
|
179
|
+
const strdup = lib.func('str! strdup(const char *str)');
|
|
180
|
+
|
|
181
|
+
let copy = strdup('World!');
|
|
182
|
+
console.log(copy); // Prints World!
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Disposable types can only be created from pointer or string types.
|
|
186
|
+
|
|
187
|
+
```{warning}
|
|
188
|
+
Be careful on Windows: if your shared library uses a different CRT (such as msvcrt), the memory could have been allocated by a different malloc/free implementation or heap, resulting in undefined behavior if you use `koffi.free()`.
|
|
189
|
+
```
|
|
140
190
|
|
|
141
191
|
## Unwrap pointers
|
|
142
192
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Polymorphic arguments
|
|
2
|
+
|
|
3
|
+
## Input polymorphism
|
|
4
|
+
|
|
5
|
+
*New in Koffi 2.1*
|
|
6
|
+
|
|
7
|
+
Many C functions use `void *` parameters in order to pass polymorphic objects and arrays, meaning that the data format changes can change depending on one other argument, or on some kind of struct tag member.
|
|
8
|
+
|
|
9
|
+
Koffi provides two features to deal with this:
|
|
10
|
+
|
|
11
|
+
- Buffers and typed JS arrays can be used as values in place everywhere a pointer is expected. See [dynamic arrays](pointers.md#array-pointers-dynamic-arrays) for more information, for input or output.
|
|
12
|
+
- You can use `koffi.as(value, type)` to tell Koffi what kind of type is actually expected.
|
|
13
|
+
|
|
14
|
+
The example below shows the use of `koffi.as()` to read the header of a PNG file with `fread()`.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
18
|
+
const koffi = require('koffi');
|
|
19
|
+
|
|
20
|
+
const lib = koffi.load('libc.so.6');
|
|
21
|
+
|
|
22
|
+
const FILE = koffi.opaque('FILE');
|
|
23
|
+
|
|
24
|
+
const PngHeader = koffi.pack('PngHeader', {
|
|
25
|
+
signature: koffi.array('uint8_t', 8),
|
|
26
|
+
ihdr: koffi.pack({
|
|
27
|
+
length: 'uint32_be_t',
|
|
28
|
+
chunk: koffi.array('char', 4),
|
|
29
|
+
width: 'uint32_be_t',
|
|
30
|
+
height: 'uint32_be_t',
|
|
31
|
+
depth: 'uint8_t',
|
|
32
|
+
color: 'uint8_t',
|
|
33
|
+
compression: 'uint8_t',
|
|
34
|
+
filter: 'uint8_t',
|
|
35
|
+
interlace: 'uint8_t',
|
|
36
|
+
crc: 'uint32_be_t'
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const fopen = lib.func('FILE *fopen(const char *path, const char *mode)');
|
|
41
|
+
const fclose = lib.func('int fclose(FILE *fp)');
|
|
42
|
+
const fread = lib.func('size_t fread(_Out_ void *ptr, size_t size, size_t nmemb, FILE *fp)');
|
|
43
|
+
|
|
44
|
+
let filename = process.argv[2];
|
|
45
|
+
if (filename == null)
|
|
46
|
+
throw new Error('Usage: node png.js <image.png>');
|
|
47
|
+
|
|
48
|
+
let hdr = {};
|
|
49
|
+
{
|
|
50
|
+
let fp = fopen(filename, 'rb');
|
|
51
|
+
if (!fp)
|
|
52
|
+
throw new Error(`Failed to open '${filename}'`);
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
let len = fread(koffi.as(hdr, 'PngHeader *'), 1, koffi.sizeof(PngHeader), fp);
|
|
56
|
+
if (len < koffi.sizeof(PngHeader))
|
|
57
|
+
throw new Error('Failed to read PNG header');
|
|
58
|
+
} finally {
|
|
59
|
+
fclose(fp);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log('PNG header:', hdr);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Output buffers
|
|
67
|
+
|
|
68
|
+
*New in Koffi 2.3*
|
|
69
|
+
|
|
70
|
+
You can use buffers and typed arrays for output (and input/output) pointer parameters. Simply pass the buffer as an argument and the native function will receive a pointer to its contents.
|
|
71
|
+
|
|
72
|
+
Once the native function returns, you can decode the content with `koffi.decode(value, type)` as in the following example:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
// ES6 syntax: import koffi from 'koffi';
|
|
76
|
+
const koffi = require('koffi');
|
|
77
|
+
|
|
78
|
+
const lib = koffi.load('libc.so.6');
|
|
79
|
+
|
|
80
|
+
const Vec3 = koffi.struct('Vec3', {
|
|
81
|
+
x: 'float32',
|
|
82
|
+
y: 'float32',
|
|
83
|
+
z: 'float32'
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const memcpy = lib.func('void *memcpy(_Out_ void *dest, const void *src, size_t size)');
|
|
87
|
+
|
|
88
|
+
let vec1 = { x: 1, y: 2, z: 3 };
|
|
89
|
+
let vec2 = null;
|
|
90
|
+
|
|
91
|
+
// Copy the vector in a convoluted way through memcpy
|
|
92
|
+
{
|
|
93
|
+
let src = koffi.as(vec1, 'Vec3 *');
|
|
94
|
+
let dest = Buffer.allocUnsafe(koffi.sizeof(Vec3));
|
|
95
|
+
|
|
96
|
+
memcpy(dest, src, koffi.sizeof(Vec3));
|
|
97
|
+
|
|
98
|
+
vec2 = koffi.decode(dest, Vec3);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// CHange vector1, leaving copy alone
|
|
102
|
+
[vec1.x, vec1.y, vec1.z] = [vec1.z, vec1.y, vec1.x];
|
|
103
|
+
|
|
104
|
+
console.log(vec1); // { x: 3, y: 2, z: 1 }
|
|
105
|
+
console.log(vec2); // { x: 1, y: 2, z: 3 }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
See [pointer arguments](callbacks.md#decoding-pointer-arguments) for another example with the decode function.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koffi",
|
|
3
|
-
"version": "2.5.
|
|
4
|
-
"stable": "2.5.
|
|
3
|
+
"version": "2.5.12",
|
|
4
|
+
"stable": "2.5.12",
|
|
5
5
|
"description": "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"foreign",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"url": "https://github.com/Koromix/koffi"
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://koffi.dev/",
|
|
20
|
-
"author":
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Niels Martignène",
|
|
22
|
+
"email": "niels.martignene@protonmail.com",
|
|
23
|
+
"url": "https://koromix.dev/"
|
|
24
|
+
},
|
|
21
25
|
"main": "src/index.js",
|
|
22
26
|
"types": "src/index.d.ts",
|
|
23
27
|
"scripts": {
|
package/src/cnoke/package.json
CHANGED
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "https://github.com/Koromix/rygel.git"
|
|
15
15
|
},
|
|
16
|
-
"author":
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Niels Martignène",
|
|
18
|
+
"email": "niels.martignene@protonmail.com",
|
|
19
|
+
"url": "https://koromix.dev/"
|
|
20
|
+
},
|
|
17
21
|
"index": "src/index.js",
|
|
18
22
|
"bin": "cnoke.js",
|
|
19
23
|
"license": "MIT"
|
package/src/core/libcc/libcc.cc
CHANGED
|
@@ -801,6 +801,108 @@ bool ParseBool(Span<const char> str, bool *out_value, unsigned int flags,
|
|
|
801
801
|
return false;
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
+
bool ParseSize(Span<const char> str, int64_t *out_size, unsigned int flags, Span<const char> *out_remaining)
|
|
805
|
+
{
|
|
806
|
+
uint64_t size = 0;
|
|
807
|
+
|
|
808
|
+
if (!ParseInt(str, &size, flags & ~(int)ParseFlag::End, &str))
|
|
809
|
+
return false;
|
|
810
|
+
if (size > INT64_MAX) [[unlikely]]
|
|
811
|
+
goto overflow;
|
|
812
|
+
|
|
813
|
+
if (str.len) {
|
|
814
|
+
uint64_t multiplier = 1;
|
|
815
|
+
int next = 1;
|
|
816
|
+
|
|
817
|
+
switch (str[0]) {
|
|
818
|
+
case 'B': { multiplier = 1; } break;
|
|
819
|
+
case 'k': { multiplier = 1000; } break;
|
|
820
|
+
case 'M': { multiplier = 1000000; } break;
|
|
821
|
+
case 'G': { multiplier = 1000000000; } break;
|
|
822
|
+
case 'T': { multiplier = 1000000000000; } break;
|
|
823
|
+
default: { next = 0; } break;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if ((flags & (int)ParseFlag::End) && str.len > next) [[unlikely]] {
|
|
827
|
+
if (flags & (int)ParseFlag::Log) {
|
|
828
|
+
LogError("Unknown size unit '%1'", str[0]);
|
|
829
|
+
}
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
str = str.Take(next, str.len - next);
|
|
833
|
+
|
|
834
|
+
uint64_t total = size * multiplier;
|
|
835
|
+
if ((size && total / size != multiplier) || total > INT64_MAX) [[unlikely]]
|
|
836
|
+
goto overflow;
|
|
837
|
+
size = total;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
*out_size = (int64_t)size;
|
|
841
|
+
if (out_remaining) {
|
|
842
|
+
*out_remaining = str;
|
|
843
|
+
}
|
|
844
|
+
return true;
|
|
845
|
+
|
|
846
|
+
overflow:
|
|
847
|
+
if (flags & (int)ParseFlag::Log) {
|
|
848
|
+
LogError("Size value is too high");
|
|
849
|
+
}
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
bool ParseDuration(Span<const char> str, int64_t *out_duration, unsigned int flags, Span<const char> *out_remaining)
|
|
854
|
+
{
|
|
855
|
+
uint64_t duration = 0;
|
|
856
|
+
|
|
857
|
+
if (!ParseInt(str, &duration, flags & ~(int)ParseFlag::End, &str))
|
|
858
|
+
return false;
|
|
859
|
+
if (duration > INT64_MAX) [[unlikely]]
|
|
860
|
+
goto overflow;
|
|
861
|
+
|
|
862
|
+
if (str.len) {
|
|
863
|
+
uint64_t multiplier = 1;
|
|
864
|
+
int next = 1;
|
|
865
|
+
|
|
866
|
+
switch (str[0]) {
|
|
867
|
+
case 's': { multiplier = 1000; } break;
|
|
868
|
+
case 'm': { multiplier = 60000; } break;
|
|
869
|
+
case 'h': { multiplier = 3600000; } break;
|
|
870
|
+
case 'd': { multiplier = 86400000; } break;
|
|
871
|
+
default: { next = 0; } break;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if ((flags & (int)ParseFlag::End) && str.len > next) [[unlikely]] {
|
|
875
|
+
if (flags & (int)ParseFlag::Log) {
|
|
876
|
+
LogError("Unknown duration unit '%1'", str[0]);
|
|
877
|
+
}
|
|
878
|
+
return false;
|
|
879
|
+
}
|
|
880
|
+
str = str.Take(next, str.len - next);
|
|
881
|
+
|
|
882
|
+
uint64_t total = duration * multiplier;
|
|
883
|
+
if ((duration && total / duration != multiplier) || total > INT64_MAX) [[unlikely]]
|
|
884
|
+
goto overflow;
|
|
885
|
+
duration = total;
|
|
886
|
+
} else {
|
|
887
|
+
uint64_t total = duration * 1000;
|
|
888
|
+
if ((duration && total / duration != 1000) || total > INT64_MAX) [[unlikely]]
|
|
889
|
+
goto overflow;
|
|
890
|
+
duration = total;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
*out_duration = (int64_t)duration;
|
|
894
|
+
if (out_remaining) {
|
|
895
|
+
*out_remaining = str;
|
|
896
|
+
}
|
|
897
|
+
return true;
|
|
898
|
+
|
|
899
|
+
overflow:
|
|
900
|
+
if (flags & (int)ParseFlag::Log) {
|
|
901
|
+
LogError("Duration value is too high");
|
|
902
|
+
}
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
|
|
804
906
|
// ------------------------------------------------------------------------
|
|
805
907
|
// Format
|
|
806
908
|
// ------------------------------------------------------------------------
|
|
@@ -2576,14 +2678,12 @@ bool TestFile(const char *filename)
|
|
|
2576
2678
|
|
|
2577
2679
|
bool TestFile(const char *filename, FileType type)
|
|
2578
2680
|
{
|
|
2579
|
-
RG_ASSERT(type != FileType::Link);
|
|
2580
|
-
|
|
2581
2681
|
FileInfo file_info;
|
|
2582
2682
|
if (StatFile(filename, (int)StatFlag::IgnoreMissing, &file_info) != StatResult::Success)
|
|
2583
2683
|
return false;
|
|
2584
2684
|
|
|
2585
2685
|
// Don't follow, but don't warn if we just wanted a file
|
|
2586
|
-
if (file_info.type == FileType::Link) {
|
|
2686
|
+
if (type != FileType::Link && file_info.type == FileType::Link) {
|
|
2587
2687
|
file_info.type = FileType::File;
|
|
2588
2688
|
}
|
|
2589
2689
|
|
|
@@ -4392,7 +4492,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
|
|
|
4392
4492
|
bool ReadCommandOutput(const char *cmd_line, HeapArray<char> *out_output)
|
|
4393
4493
|
{
|
|
4394
4494
|
int exit_code;
|
|
4395
|
-
if (!ExecuteCommandLine(cmd_line, nullptr, {},
|
|
4495
|
+
if (!ExecuteCommandLine(cmd_line, nullptr, {}, Mebibytes(1), out_output, &exit_code))
|
|
4396
4496
|
return false;
|
|
4397
4497
|
if (exit_code) {
|
|
4398
4498
|
LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
|
|
@@ -6760,6 +6860,16 @@ bool SpliceStream(StreamReader *reader, int64_t max_len, StreamWriter *writer)
|
|
|
6760
6860
|
return true;
|
|
6761
6861
|
}
|
|
6762
6862
|
|
|
6863
|
+
bool IsCompressorAvailable(CompressionType compression_type)
|
|
6864
|
+
{
|
|
6865
|
+
return CompressorFunctions[(int)compression_type];
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
bool IsDecompressorAvailable(CompressionType compression_type)
|
|
6869
|
+
{
|
|
6870
|
+
return DecompressorFunctions[(int)compression_type];
|
|
6871
|
+
}
|
|
6872
|
+
|
|
6763
6873
|
// ------------------------------------------------------------------------
|
|
6764
6874
|
// INI
|
|
6765
6875
|
// ------------------------------------------------------------------------
|